# AGPL 3 or any later version # (C) J.Y.Amihud ( Blender Dumbass ) import os import json import random import hashlib import urllib.parse from datetime import datetime from modules import Set from modules import markdown from modules.Common import * def guess_type(path): if "/json/" in path or ".json" in path: return "application/json" if "/css" in path or ".css" in path: return "text/css" if "/icon" in path or path.endswith(".png"): return "image/png" if path.endswith("jpg"): return "image/jpg" return "text/html" def headers(server, code): # Basic cookie for logins to work server.send_response(code) server.send_header("Content-type", guess_type(server.path)) if not server.cookie: server.send_header("Set-Cookie", "temp_id="+RandString()) server.end_headers() def head(title="", description="", image="", config={}): if image.startswith("/"): image = config.get("url","")+image favicon = config.get("favicon", "/icon/internet") html = """ """+title+""" """ return html def send(server, html, code): # Add headers headers(server, code) server.wfile.write(html.encode("utf-8")) def tabs(): folder = Set.Folder()+"/tabs" tabs = {} for tab in sorted(list(os.walk(folder))[0][1]): try: with open(folder+"/"+tab+"/config.json") as o: data = json.load(o) tabs[tab] = data except Exception as e: print(e) pass return tabs def accounts(): folder = Set.Folder()+"/accounts" accounts = {} for account in sorted(list(os.walk(folder))[0][2]): try: with open(folder+"/"+account) as o: data = json.load(o) data["username"] = account.replace(".json","") accounts[account.replace(".json","")] = data except Exception as e: print(e) pass return accounts def validate(cookie): Accounts = accounts() for account in Accounts: if cookie in Accounts[account].get("sessions", []): return Accounts[account] return {} def moderates(moderator, user): Accounts = accounts() if moderator not in Accounts: return False if user not in Accounts: return True if moderator == user: return True if rank(moderator, Accounts) < rank(user, Accounts): return True def rank(account, Accounts=None): if not Accounts: Accounts = accounts() if account not in Accounts: return 1000000 if not Accounts[account].get("invited_by") or Accounts[account].get("invited_by") == account: return 0 return 1 + rank(Accounts[account].get("invited_by"), Accounts) def articles(tab): folder = Set.Folder()+"/tabs/"+tab articles = {} for article in list(os.walk(folder))[0][1]: try: with open(folder+"/"+article+"/metadata.json") as o: data = json.load(o) data["tab"] = tab data["url"] = "/"+tab+"/"+article articles[article] = data except Exception as e: print(e) pass # Sorting articles based on timestamp articles = {k:articles[k] for k in sorted(articles, key=lambda y: articles[y]["timestamp"], reverse=True)} return articles def allArticles(): articles = {} f = Set.Folder() for tab in list(os.walk(f+"/tabs/"))[0][1]: folder = f+"/tabs/"+tab for article in list(os.walk(folder))[0][1]: try: with open(folder+"/"+article+"/metadata.json") as o: data = json.load(o) data["tab"] = tab data["url"] = "/"+tab+"/"+article articles[article] = data except Exception as e: print(e) pass # Sorting articles based on timestamp articles = {k:articles[k] for k in sorted(articles, key=lambda y: articles[y]["timestamp"], reverse=True)} return articles def randomArticles(): articles = {} f = Set.Folder() for tab in list(os.walk(f+"/tabs/"))[0][1]: folder = f+"/tabs/"+tab for article in list(os.walk(folder))[0][1]: try: with open(folder+"/"+article+"/metadata.json") as o: data = json.load(o) data["tab"] = tab data["url"] = "/"+tab+"/"+article articles[article] = data except Exception as e: print(e) pass # Randomizing Articles. newarticles = {} while articles: article = random.choice(list(articles.keys())) newarticles[article] = articles.pop(article) return newarticles def suggestedArticles(cookie, random=False): if not random: articles = allArticles() else: articles = randomArticles() # Suggesting unread articles. newarticles = {} move = [] for article in articles: if cookie not in articles[article].get("views", {}).get("viewers", []): move.append(article) for article in move: newarticles[article] = articles[article] for article in articles: if article not in move: newarticles[article] = articles[article] return newarticles def previewsToSize(text): # Calculates roughly how many previews to fit any # given article. # A thousand character article is about 4 articles. return len(text)/2200 ### def MainPage(server): # Reading config config = Set.Load() # Generating html = head(title = config.get("title", "Website"), description = config.get("description", "Description"), config = config ) html = html + LoginButton(server) html = html + """

[LOGO]

"""+config.get("title", "My Website")+"""

"""+config.get("tagline", "")+"""

""" Tabs = tabs() for tab in Tabs: html = html + Button(Tabs[tab].get("title", tab), "/"+tab, Tabs[tab].get("icon", "folder")) html = html + "
" # Trending articles html = html + '
' trends = suggestedArticles(server.cookie) Buffer = 20 for n, article in enumerate(trends): if n >= Buffer: break article = trends[article] html = html + ArticlePreview(article, Tabs, server.cookie) html = html + '
' html = html + Footer() html = html + LoginButton(server) send(server, html, 200) def ListPage(server, tab): Tabs = tabs() Articles = articles(tab) config = Set.Load() try: page = int(server.parsed.get("page", ["0"])[0]) except Exception as e: print(e) page = 0 Buffer = 16 From = Buffer*page To = From+Buffer # Generating html = head(title = Tabs.get(tab, {}).get("title", tab), description = "", config = config ) html = html + Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet")) # Scroll thingie if len(Articles) > Buffer: if page > 0: html = html + Button(str(page-1), tab+"?page="+str(page-1), "left") html = html + '
'+str(page)+'
' if To < len(Articles)-1: html = html + Button(str(page+1), tab+"?page="+str(page+1), "right") html = html + """

""" print(page) rendered = 0 for n, article in enumerate(Articles): if n < From: continue if n >= To: break html = html + ArticlePreview(Articles[article], Tabs, server.cookie) rendered += 1 html = html + '

' # Bottom pannel for large dialogs if rendered >= Buffer/2: html = html + Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet")) # Scroll thingie if len(Articles) > Buffer: if page > 0: html = html + Button(str(page-1), tab+"?page="+str(page-1), "left") html = html + '
'+str(page)+'
' if To < len(Articles)-1: html = html + Button(str(page+1), tab+"?page="+str(page+1), "right") html = html + LoginButton(server) send(server, html, 200) def ArticlePage(server, url): user = validate(server.cookie) if url.endswith(".md"): url = url.replace(".md", "") config = Set.Load() tab, article = url.split("/") Tabs = tabs() Articles = articles(tab) f = Set.Folder() # Generating html = head(title = Articles.get(article, {}).get("title", article), description = Articles.get(article, {}).get("description", ""), config = config ) html = html + Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet")) html = html + Button(Tabs.get(tab, {}).get("title", tab), "/"+tab, Tabs.get(tab, {}).get("icon", "folder")) # The article itself html = html + '
' html = html + '
' html = html +"

"+Articles.get(article, {}).get("title", article)+"

" # Page author author = Articles.get(article, {}).get("author", "") if author: html = html + '
'+User( author )+'
' # Page views # Views are calculated using an iframe which only loads when the page is # rendered in a browser. It is also looking at whether the same cookie renders # the same page, to avoid double counting from the same person. # The iframe itself shows a graph of views. views = str(Articles.get(article, {}).get("views", {}).get("amount", 0)) html = html + '
👁 '+views+'' html = html + """

""" html = html + '
' # Audio recording of the article recording = Articles.get(article, {}).get("recording", "") if recording: html = html + '' html = html + '
' html = html + markdown.convert(f+"/tabs/"+tab+"/"+article+"/text.md") html = html + '
' # Comments html = html + CommentInput(server, url) comments = Articles.get(article, {}).get("comments", {}).get("comments", []) commentsTextLength = 0 comment_edit = server.parsed.get("comment_edit", [""])[0] if comments: for n, comment in enumerate(comments): if str(n) == comment_edit and moderates(user.get("username"), comment.get("username")): html = html + CommentEditInput(server, comment, url, n, user) else: html = html + Comment(comment, url, n, user) # Needed to extend the suggestion for pages with many comments commentsTextLength += previewsToSize(comment.get("text", "")) # Requests requests = Articles.get(article, {}).get("comments", {}).get("requests", []) if requests: for n, comment in enumerate(requests): if comment.get("cookie") == server.cookie: html = html + Comment(comment, url, n, user, request=True) elif moderates(user.get("username"), comment.get("username")): html = html + CommentEditInput(server, comment, url, n, user, request=str(n)) html = html + '
' # Thumbnail and suggestions html = html + '
' thumbnail = Articles.get(article, {}).get("thumbnail") if thumbnail: html = html + '

' html = html + '' html = html + '

' suggestions = suggestedArticles(server.cookie, random=True) toomuch = previewsToSize(open(f+"/tabs/"+tab+"/"+article+"/text.md").read()) toomuch += commentsTextLength for n, title in enumerate(suggestions): if server.path in suggestions[title].get("url") : continue if n > toomuch: break article = suggestions[title] html = html + ArticlePreview(article, Tabs, server.cookie) html = html + "" html = html + LoginButton(server) send(server, html, 200) def AccountPage(server, account): config = Set.Load() Accounts = accounts() Tabs = tabs() Articles = allArticles() f = Set.Folder() # Generating html = head(title = Safe(Accounts.get(account, {}).get("title", account)), description = Safe(Accounts.get(account, {}).get("bio" , "")), config = config ) html = html + Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet")) # Name and bio html = html + '
' html = html + '
' html = html +"

"+Accounts.get(account, {}).get("title", account)+"

" # Rank Rank = rank(account) html = html + Button("Rank "+str(Rank), "", icon="analytics") html = html + '
' # Website website = Safe(Accounts.get(account, {}).get("website" , "")) if website: webtitle = website.replace("https://", "").replace("http://", "") if not website.startswith("http"): website = "http://"+website html = html + '
' html = html + '' html = html + ' '+webtitle+'' html = html + '
' # Email email = Safe(Accounts.get(account, {}).get("email" , "")) if email: html = html + '
' html = html + '' html = html + ' '+email+'' html = html + '
' # Mastodon mastodon = Safe(Accounts.get(account, {}).get("mastodon" , "")) if mastodon: # It could be mastodon url and not handle. try: if "/" in mastodon: mastodon = mastodon.replace("https://", "").replace("http://", "") mastodon = mastodon.split("/")[1]+"@"+mastodon.split("/")[0] mastolink = "https://"+mastodon[1:].split("@")[1]+"/@"+mastodon[1:].split("@")[0] html = html + '
' html = html + '' html = html + ' '+mastodon+'' html = html + '
' except: pass # Matrix matrix = Safe(Accounts.get(account, {}).get("matrix" , "")) if matrix: # Matrix could be the matrix.to link if "/" in matrix: matrix = matrix[matrix.rfind("/")+1:] matrixlink = "https://matrix.to/#/"+matrix html = html + '
' html = html + '' html = html + ' '+matrix+'' html = html + '
' if any((website, email, mastodon, matrix)): html = html + '
' html = html + '
' invited_by = Accounts.get(account, {}).get("invited_by", "") if invited_by: html = html + '
' html = html +"
Invited by:
"+User(invited_by)+"
" html = html + '
' bio = Safe(Accounts.get(account, {}).get("bio" , "")) if bio: html = html + '
' html = html + markdown.convert(bio, False)+'
' html = html + '
' # Posts by this account html = html + '
' for article in Articles: if Articles[article].get("author") != account: continue html = html + ArticlePreview(Articles[article], Tabs, server.cookie) html = html + '
' html = html + '
' # Thumbnail and suggestions html = html + '
' avatar = Safe(Accounts.get(account, {}).get("avatar" , "")) if avatar: html = html + '

' html = html + '' html = html + '

' # Invited invited = Accounts.get(account, {}).get("invited", []) if invited: html = html + '
Invited:
' for username in invited: if username in Accounts: html = html + '
' html = html + '
' + User(username) + '
\n' html = html + '
' html = html + LoginButton(server) send(server, html, 200) def LoginPage(server): config = Set.Load() Accounts = accounts() f = Set.Folder() wrongname = server.parsed.get("wrong", [""])[0] # Generating html = head(title = "Login", description = "Login", config = config ) html = html + Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet")) html = html + '
' if wrongname: html = html + '\n
Wrong Username / Password
\n' html = html + """


Don't have an account?
""" html = html + Button("Register", "/register", icon="user_new") send(server, html, 200) def RegisterPage(server): user = validate(server.cookie) config = Set.Load() Accounts = accounts() f = Set.Folder() code = server.parsed.get("code", [""])[0] userexists = server.parsed.get("userexists", [""])[0] wrongcode = server.parsed.get("wrongcode", [""])[0] # Generating html = head(title = "Register", description = "Register", config = config ) html = html + Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet")) html = html + '
' html = html + """
""" if wrongcode: html = html + "Invalid Invite Code.

" if not code and not user: html = html + """ """ username = "" else: for account in Accounts: if code in Accounts[account].get("invite_codes", []): username = Simplify(Accounts[account]["invite_codes"][code], "file") html = html + '
Invited by:

' html = html + User(account)+'
' html = html + '' break if not user: if userexists: html = html + "Username is taken.

" html = html + """

""" elif code: html = html + "Share this page with those you invited." else: html = html + "
You already registered and logged in." html = html + """
Have an account?
""" html = html + Button("Login", "/login", icon="unlock") send(server, html, 200) def SettingsPage(server): user = validate(server.cookie) if not user: Redirect(server, "/login") return config = Set.Load() # Generating html = head(title = "Settings", description = "Settings", config = config ) html = html + Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet")) html = html + Button("Public Profile", "/account/"+user.get("username", ""), image="/pictures/monkey.png") # Main settings html = html + """

Public Info


Bio
""" # Current Logged in Sessions sessions = user.get("sessions", {}) html = html + '
' html = html + '

Active Sessions

' for cookie in sessions: session = sessions[cookie] CancelButton = Button("Log Out", "/log_out?cookie="+cookie, icon="cancel") if server.cookie == cookie: html = html + '
' else: html = html + '
' html = html + ''+CancelButton html = html + '

' html = html + '
' html = html + '
' # Invites and Invite codes invite_codes = user.get("invite_codes", {}) html = html + '
' html = html + '

Invites

' for code in invite_codes: nick = invite_codes[code] Open = "" if code == server.parsed.get("code", [""])[0]: Open = "open" html = html + '
' html = html + ''+nick html = html + '
' html = html + '' html = html + '' html = html + Button("Share Link", "/register?code="+code, icon="link") html = html + Button("Cancel", "/cancel_invite?code="+code, icon="cancel") html = html + '
' html = html + """
""" send(server, html, 200) ### def Button(text, link, icon="", image=""): html = """ """ if icon: html = html + """ """ if image: html = html + """ """ html = html + """ """+text+""" """ return html def ArticlePreview(article, Tabs, cookie=""): html = """
""" url, tab = article.get("url", ""), article.get("tab","") sup = "" if cookie not in article.get("views", {}).get("viewers", ""): sup = '
  Unread  

' html = html + '

' html = html + '' html = html + article.get("title", "")+"

"+sup+"\n" if "thumbnail" in article: html = html + '
' author = article.get("author", "") if author: html = html + '
'+User( author )+'
' views = str(article.get("views", {}).get("amount", 0)) try: comments = str(len(article.get("comments", {}).get("comments"))) except: comments = "0" html = html +'
👁 '+views+' 💬 '+comments+'
' html = html + "
"+markdown.convert(article.get("description", ""), False)+"

" html = html + '
\n' return html def Footer(): html = """
""" html = html + Button("Powered with BDServer", "https://codeberg.org/blenderdumbass/BDServer/", "codeberg") html = html + """
""" return html def User(username, stretch=False): try: with open(Set.Folder()+"/accounts/"+username+".json") as o: account = json.load(o) except: account = {} # We are doing a lot of reductions in case somebody sneaks html code. avatar = Safe(account.get("avatar", "")) if not avatar: avatar = "/pictures/monkey.png" username = Safe(username) title = Safe(account.get("title", username)) if account: html = '  '+title+'
\n' else: html = '  '+title+'
\n' return html def Graph(server, url): user = validate(server.cookie) html = """ """ if url.endswith(".md"): url = url.replace(".md", "") try: with open(Set.Folder()+"/tabs"+url+"/metadata.json") as o: article = json.load(o) except: article = {} dates = article.get("views", {}).get("dates", {}) largest = max(dates.values()) dateformat = "%Y-%m-%d" startdate = datetime.strptime(sorted(list(dates.keys()), reverse=True)[0], dateformat) enddate = datetime.strptime(sorted(list(dates.keys()), reverse=True)[-1], dateformat) alldays = int((startdate - enddate).days) print(alldays) for n, date in enumerate(sorted(dates, reverse=True)): amount = dates[date] width = 100 / (alldays+1) height = 60 * (amount / largest) cd = datetime.strptime(date, dateformat) nd = int((startdate - cd).days) html = html + '
\n' # Saving the view cookies = [server.cookie] if user: # If user is logged in, we want to record # per reading the article, on all pers # sessions. for cookie in user.get("sessions"): if cookie not in cookies: cookies.append(cookie) for cookie in cookies: if cookie not in article.get("views", {}).get("viewers", []): article["views"]["amount"] += 1 article["views"]["viewers"].append(cookie) with open(Set.Folder()+"/tabs"+url+"/metadata.json", "w") as save: json.dump(article, save, indent=4) send(server, html, 200) def CommentInput(server, url): user = validate(server.cookie) html = """

""" if not user: html = html + '' html = html + '' else: html = html + User(user.get("username", ""))+'
' html = html + """

""" return html def CommentEditInput(server, comment, url, n=0, user=None, request=None): Accounts = accounts() html = '
' html = html + """
""" if request: html = html + '' html = html + '
  Pending Approval  

' account = comment.get("username", "") if account in accounts(): html = html + User(Safe(account)) elif account: html = html + '' html = html + '' warning = comment.get("warning", "") html = html + """
""" html = html + """' html = html + """ """ html = html + '
' html = html + '
' return html def Comment(comment, url, n=0, user=None, request=False): if not request: html = '
' else: html = '
' account = comment.get("username", "Anonymous User") html = html + User(account) + '
\n' if request: html = html + '
  Pending Approval  

' warning = comment.get("warning", "") if warning: html = html + '
' html = html + '' html = html + '' html = html + warning html = html + '' html = html + markdown.convert(Safe(comment.get("text", "")), False)+'
' if warning: html = html + '
' if moderates(user.get("username"), account): html = html + '' html = html + ' Edit' html = html + '' html = html + '' html = html + ' Delete' html = html + '' html = html + '
\n' return html def LoginButton(server): user = validate(server.cookie) html = '' return html ### def Redirect(server, url): html = """""" send(server, html, 200) def Login(server): username = server.parsed.get("user_name", [""])[0] password = server.parsed.get("password" , [""])[0] hashed = hashlib.sha512(password.encode("utf-8")).hexdigest() Accounts = accounts() # Failed authentication if username not in Accounts or hashed != Accounts[username].get("password"): Redirect(server, "/login?wrong=username") # Succesfull authentication else: account = Accounts[username] if "sessions" not in account: account["sessions"] = {} account["sessions"][server.cookie] = server.headers.get("User-Agent") f = Set.Folder() folder = f+"/accounts" with open(folder+"/"+username+".json", "w") as save: json.dump(account, save, indent=4) # Move the cookie arround # When a login happens, we want to make the server know # which articles the person already read and stuff. So # we want to come all the cookies. Arround. articles = allArticles() for title in articles: article = articles[title] for cookie in account["sessions"]: if cookie != server.cookie: # Making it so it knows what you were watching previously. if cookie in article.get("views", {}).get("viewers", [])\ and server.cookie not in article["views"]["viewers"]: article["views"]["viewers"].append(server.cookie) # Making it so previously logged in account would know. # what you were watching thus far from this cookie. if server.cookie in article.get("views", {}).get("viewers", [])\ and cookie not in article["views"]["viewers"]: article["views"]["viewers"].append(cookie) with open(f+"/tabs"+article.get("url")+"/metadata.json", "w") as save: json.dump(article, save, indent=4) Redirect(server, "/") def Register(server): # If by mistake we are logged in user = validate(server.cookie) if user: Redirect(server, "/register") username = Simplify(server.parsed.get("user_name", [""])[0], "file") code = server.parsed.get("code", [""])[0] password = server.parsed.get("password" , [""])[0] hashed = hashlib.sha512(password.encode("utf-8")).hexdigest() Accounts = accounts() # We avoid username swappage if username in Accounts or not username: if code: Redirect(server, "/register?code="+code+"&userexists=True#user_name") else: Redirect(server, "/register?userexists=True#user_name") return # Validating the invite code invited_by = "" for account in Accounts: if code in Accounts[account].get("invite_codes", []): invited_by = account break if not invited_by: Redirect(server, "/register?wrongcode=True") return # Now we can finally make our account. # New account first account = { "username":username, "bio":"", "invite_codes":{}, "invited":[], "invited_by":invited_by, "password":hashed, "title":username, "email":"", "website":"", "mastodon":"", "matrix":"", "sessions":{ server.cookie:server.headers.get("User-Agent") } } f = Set.Folder() folder = f+"/accounts" with open(folder+"/"+username+".json", "w") as save: json.dump(account, save, indent=4) # Now the invitor changes account = Accounts[invited_by] del account["invite_codes"][code] account["invited"].append(username) with open(folder+"/"+account.get("username", "")+".json", "w") as save: json.dump(account, save, indent=4) Redirect(server, "/settings") def LogOut(server): user = validate(server.cookie) cookie = server.parsed.get("cookie", [""])[0] # This might be an attack. So we don't want that. if cookie not in user.get("sessions",{}): Redirect(server, "/") return del user["sessions"][cookie] f = Set.Folder() folder = f+"/accounts" with open(folder+"/"+user.get("username", "")+".json", "w") as save: json.dump(user, save, indent=4) # If the user logged out this session if cookie == server.cookie: Redirect(server, "/") else: Redirect(server, "/settings#sessions") def UpdateAccount(server): user = validate(server.cookie) if not user: Redirect(server, "/login") return keys = [ "title", "avatar", "bio", "website", "email", "mastodon", "matrix" ] for key in keys: data = server.parsed.get(key, [""])[0] user[key] = Safe(data) f = Set.Folder() folder = f+"/accounts" with open(folder+"/"+user.get("username", "")+".json", "w") as save: json.dump(user, save, indent=4) Redirect(server, "/settings") def DoComment(server): user = validate(server.cookie) Accounts = accounts() url = server.parsed.get("url", ["/"])[0] if not url.startswith("/"): url = "/" + url text = server.parsed.get("text", [""])[0] nick = server.parsed.get("username", [""])[0] warn = server.parsed.get("warning", [""])[0] number = server.parsed.get("number", [""])[0] request = server.parsed.get("request", [""])[0] metadata = Set.Folder()+"/tabs"+url+"/metadata.json" try: with open(metadata) as o: article = json.load(o) except: Redirect(server, "/") return if "comments" not in article: article["comments"] = {} if "comments" not in article["comments"]: article["comments"]["comments"] = [] if "requests" not in article["comments"]: article["comments"]["requests"] = [] comment = { "text":text } placeRedirect = "#comment_" if warn: comment["warning"] = warn if not nick and user: comment["username"] = user.get("username", "") place = "comments" elif request: if nick in Accounts or not nick: nick = "Anonymous Guest" comment["username"] = nick del article["comments"]["requests"][int(request)] place = "comments" number = "" else: if nick in Accounts or not nick: nick = "Anonymous Guest" comment["username"] = nick placeRedirect = "#request_" place = "requests" if not user: comment["cookie"] = server.cookie if not number: article["comments"][place].append(comment) number = len(article["comments"][place])-1 else: number = int(number) if moderates(user.get("username"), article["comments"]["comments"][number]["username"]): # Making sure moderators done get credit for small edits # in comments. originalcommet = article["comments"]["comments"][number] if originalcommet.get("username") in Accounts: comment["username"] = originalcommet["username"] article["comments"]["comments"][number] = comment try: with open(metadata, "w") as save: json.dump(article, save, indent=4) except: pass if not number: placeRedirect = "#comments" number = "" Redirect(server, url+placeRedirect+str(number)) def DeleteComment(server): user = validate(server.cookie) url = server.parsed.get("url", ["/"])[0] if not url.startswith("/"): url = "/" + url number = int(server.parsed.get("number", ["0"])[0]) metadata = Set.Folder()+"/tabs"+url+"/metadata.json" try: with open(metadata) as o: article = json.load(o) except: Redirect(server, "/") return comment = article["comments"]["comments"][number] if moderates(user.get("username", ""), comment.get("username", "")): del article["comments"]["comments"][number] try: with open(metadata, "w") as save: json.dump(article, save, indent=4) except: pass if number: redirect = "#comment_"+str(number-1) else: redirect = "#comments" Redirect(server, url+redirect) def CancelInvite(server): user = validate(server.cookie) code = server.parsed.get("code", [""])[0] if user: del user["invite_codes"][code] f = Set.Folder() folder = f+"/accounts" with open(folder+"/"+user.get("username", "")+".json", "w") as save: json.dump(user, save, indent=4) Redirect(server, "/settings#invites") else: Redirect(server, "/") def CreateInvite(server): user = validate(server.cookie) nick = server.parsed.get("nick", [""])[0] if not nick: nick = "Unknown" code = RandString() if user: user["invite_codes"][code] = nick f = Set.Folder() folder = f+"/accounts" with open(folder+"/"+user.get("username", "")+".json", "w") as save: json.dump(user, save, indent=4) Redirect(server, "/settings?code="+code+"#invite_"+code) else: Redirect(server, "/")