Initial commit.
This commit is contained in:
@@ -0,0 +1,519 @@
|
||||
# AGPL 3 or any later version
|
||||
# (C) J.Y.Amihud ( Blender Dumbass )
|
||||
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import email
|
||||
import random
|
||||
import hashlib
|
||||
import subprocess
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
import urllib3
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from modules import Set
|
||||
from modules import Analyse
|
||||
from modules import Federation
|
||||
from modules import Render
|
||||
from modules import ActivityPub
|
||||
from modules import markdown
|
||||
from modules import Plugins
|
||||
from modules.Common import *
|
||||
|
||||
API_cache = {}
|
||||
|
||||
def Get(url, data=None, headers={}, raw=False, timeout=10):
|
||||
|
||||
|
||||
|
||||
config = Set.Load()
|
||||
|
||||
if data:
|
||||
try:
|
||||
data = json.dumps(data).encode("utf-8")
|
||||
except:
|
||||
try:
|
||||
data = data.encode("utf-8")
|
||||
except:
|
||||
pass
|
||||
|
||||
#if not headers:
|
||||
# headers = {"User-Agent":"BDServer ( "+config.get("domain", "")+" )"}
|
||||
|
||||
if not data:
|
||||
#print("Using urllib3")
|
||||
req = urllib3.PoolManager()
|
||||
f = req.request("GET", url, headers=headers, timeout=timeout)
|
||||
|
||||
if raw:
|
||||
return f.data
|
||||
data = json.loads(f.data.decode("utf-8"))
|
||||
|
||||
else:
|
||||
#print("Using urllib")
|
||||
req = urllib.request.Request(url, data=data, headers=headers)
|
||||
f = urllib.request.urlopen(req, timeout=timeout)
|
||||
|
||||
if raw:
|
||||
return f.read()
|
||||
data = json.loads(f.read().decode('utf-8'))
|
||||
|
||||
|
||||
|
||||
return data
|
||||
|
||||
def Value(url, keys, data={}, timeout=10):
|
||||
|
||||
if not data:
|
||||
data = Get(url, timeout)
|
||||
for key in keys:
|
||||
data = data[key]
|
||||
return data
|
||||
|
||||
def Petition(article):
|
||||
|
||||
petition = article.get("petition", {})
|
||||
api = petition.get("api", {})
|
||||
timestamp = api.get("timestamp",0)
|
||||
|
||||
# 5 minutes delay
|
||||
if time.time() - 300 < timestamp:
|
||||
return
|
||||
|
||||
print(api.get("api", ""))
|
||||
|
||||
value = int(Value(api.get("api", ""), api.get("keys", []), timeout=10))
|
||||
article["petition"]["api"]["timestamp"] = time.time()
|
||||
article["petition"]["signed"] = value
|
||||
|
||||
f = Set.Folder()
|
||||
with open(f+"/tabs"+article.get("url", "")+"/metadata.json", "w") as save:
|
||||
json.dump(article, save, indent=4)
|
||||
|
||||
|
||||
def PetitionFetch():
|
||||
|
||||
Articles = Render.allArticles()
|
||||
for article in Articles:
|
||||
petition = Articles.get(article, {}).get("petition", "")
|
||||
if petition:
|
||||
if petition.get("api"):
|
||||
try:
|
||||
print("Trying to update petition for ", article)
|
||||
Petition(Articles[article])
|
||||
except Exception as e:
|
||||
print("Petition failed with", e)
|
||||
|
||||
|
||||
|
||||
|
||||
def Mastodon(mastoname, posts=4):
|
||||
|
||||
# This function will get few latest posts
|
||||
# and return them back to whatever needs
|
||||
# them.
|
||||
|
||||
# First we want to get the mastodon ID of the
|
||||
# user in question ( mastodon API is strange ).
|
||||
|
||||
if mastoname.startswith("@"):
|
||||
mastoname = mastoname[1:]
|
||||
username, host, *rest = mastoname.split("@")
|
||||
|
||||
if not "mastoid" in API_cache:
|
||||
|
||||
url = "https://"+host+"/api/v1/accounts/lookup?acct="+username
|
||||
ID = Value(url, ["id"])
|
||||
|
||||
API_cache["mastoid"] = ID
|
||||
|
||||
ID = API_cache["mastoid"]
|
||||
|
||||
# Now that we have the ID we can request the actuall
|
||||
# list that we came here for.
|
||||
|
||||
if time.time()-300 > API_cache.get("mastotime", 0):
|
||||
|
||||
url = "https://"+host+"/api/v1/accounts/"+str(ID)+"/statuses?inned=false&limit="+str(posts)
|
||||
data = Get(url)
|
||||
|
||||
API_cache["mastotime"] = time.time()
|
||||
API_cache["mastodata"] = data
|
||||
|
||||
return API_cache["mastodata"]
|
||||
|
||||
def GetMetadata(url):
|
||||
|
||||
config = Set.Load()
|
||||
|
||||
if config.get("domain", "") in url:
|
||||
url = url.replace("https://", "").replace("http://", "").replace(config.get("domain", ""), "")
|
||||
|
||||
if url.startswith("/"):
|
||||
data = GiveJSON(url[1:])
|
||||
data["domain"] = config.get("domain", "local")
|
||||
return data
|
||||
|
||||
# Check for https://
|
||||
if not url.startswith(("http://","https://")):
|
||||
url = "https://"+url
|
||||
|
||||
# Cheking if data can be reached.
|
||||
domain = url.replace("http://", "").replace("https://", "").split("/")[0]
|
||||
request = url[url.find(domain)+len(domain):]
|
||||
|
||||
jsonurl = "https://"+domain+"/json"+request
|
||||
|
||||
try:
|
||||
data = Get(jsonurl)
|
||||
if data.get("title", ""):
|
||||
data["domain"] = domain
|
||||
return data
|
||||
except:
|
||||
pass
|
||||
|
||||
data = {}
|
||||
|
||||
# Check if there is at least ActivityPub data of this article
|
||||
|
||||
try:
|
||||
apdata = Get(url, headers={"Accept": "application/activity+json"})
|
||||
try:
|
||||
username = ActivityPub.GetHandle(apdata.get("attributedTo", ""))
|
||||
except:
|
||||
username = "Anonymous"
|
||||
data["title"] = "Fediverse Post"
|
||||
data["author"] = username
|
||||
data["description"] = markdown.toMarkdown(apdata.get("content", ""))
|
||||
data["domain"] = domain
|
||||
|
||||
return data
|
||||
|
||||
except Exception as e:
|
||||
print("ER", e)
|
||||
pass
|
||||
|
||||
# If this doesn't work we are going for something else.
|
||||
# Regular spoofing of User-Agent should do the trick to load various
|
||||
# pages that do not want to be loaded otherwise.
|
||||
|
||||
req = urllib.request.Request(url, headers={"User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:132.0) Gecko/20100101 Firefox/132.0"})
|
||||
f = urllib.request.urlopen(req, timeout=10)
|
||||
page = f.read().decode('utf-8')
|
||||
|
||||
# Lets find the title
|
||||
title = ""
|
||||
if "<title>" in page and "</title>" in page:
|
||||
title = page[page.find("<title>")+7:page.find("</title>")]
|
||||
|
||||
if title:
|
||||
data["title"] = title
|
||||
|
||||
description = ""
|
||||
meta = '<meta property="og:description" content="'
|
||||
if meta in page and '">' in page:
|
||||
description = page[page.find(meta)+len(meta):]
|
||||
description = description[:description.find('"')]
|
||||
|
||||
if description:
|
||||
data["description"] = description
|
||||
|
||||
data["domain"] = domain
|
||||
|
||||
fediverse_creator = ""
|
||||
meta = '<meta name="fediverse:creator" content="'
|
||||
if meta in page and '" />' in page:
|
||||
fediverse_creator = page[page.find(meta)+len(meta):]
|
||||
fediverse_creator = fediverse_creator[:fediverse_creator.find('" />')]
|
||||
fediverse_creator = Render.mastohead(fediverse_creator)
|
||||
|
||||
if fediverse_creator:
|
||||
|
||||
try:
|
||||
mastoname = fediverse_creator
|
||||
if mastoname.startswith("@"):
|
||||
mastoname = mastoname[1:]
|
||||
username, host, *rest = mastoname.split("@")
|
||||
url = "https://"+host+"/api/v1/accounts/lookup?acct="+username
|
||||
mastodata = Get(url)
|
||||
|
||||
author = {
|
||||
"avatar":mastodata.get("avatar", ""),
|
||||
"username":mastodata.get("username",mastoname),
|
||||
"title":mastodata.get("display_name",mastoname),
|
||||
"url":mastodata.get("url", Render.mastolink(mastoname)),
|
||||
}
|
||||
data["author"] = author
|
||||
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def APISafeAccount(account, config):
|
||||
|
||||
# Function making account dicts safe
|
||||
# to be shared online.
|
||||
|
||||
try: del account["password"]
|
||||
except: pass
|
||||
|
||||
try: del account["sessions"]
|
||||
except: pass
|
||||
|
||||
try: del account["invite_codes"]
|
||||
except: pass
|
||||
|
||||
try: del account["email"]
|
||||
except: pass
|
||||
|
||||
try: del account["activity_pub"]["password"]
|
||||
except: pass
|
||||
|
||||
try: del account["activity_pub"]["pem"]
|
||||
except: pass
|
||||
|
||||
try: del account["matrix"]
|
||||
except: pass
|
||||
|
||||
try: del account["forums"]
|
||||
except: pass
|
||||
|
||||
try: del account["notifications"]
|
||||
except: pass
|
||||
|
||||
# Links
|
||||
|
||||
if account.get("avatar", "").startswith("/"):
|
||||
account["avatar"] = "https://"+config.get("domain", "")+account["avatar"]
|
||||
|
||||
account["url"] = "https://"+config.get("domain", "")+"/account/"+account.get("username", "")
|
||||
|
||||
return account
|
||||
|
||||
def APISafeArticle(article, Accounts, config):
|
||||
|
||||
# Function that makes an article dictionary
|
||||
# safe to be sent over the network.
|
||||
|
||||
try: del article["views"]["viewers"]
|
||||
except: pass
|
||||
|
||||
try: del article["views"]["referrers"]
|
||||
except: pass
|
||||
|
||||
try: del article["comments"]["requests"]
|
||||
except: pass
|
||||
|
||||
try: del article["petition"]["signatures"]
|
||||
except: pass
|
||||
|
||||
try: article["author"] = APISafeAccount(Accounts[article["author"]], config)
|
||||
except: pass
|
||||
|
||||
if article.get("thumbnail","").startswith("/"):
|
||||
article["thumbnail"] = "https://"+config.get("domain", "")+article["thumbnail"]
|
||||
|
||||
article["url"] = "https://"+config.get("domain", "")+article.get("url","")
|
||||
|
||||
return article
|
||||
|
||||
def GiveJSON(url, server=None):
|
||||
|
||||
# Function returning json for various things.
|
||||
|
||||
config = Set.Load()
|
||||
|
||||
data = {}
|
||||
|
||||
Tabs = Render.tabs()
|
||||
Accounts = Render.accounts()
|
||||
f = Set.Folder()
|
||||
categories = list(Tabs.keys())
|
||||
if url in categories:
|
||||
data = Tabs.get(url, {})
|
||||
Articles = Render.articles(url)
|
||||
data["posts"] = {}
|
||||
for art in Articles:
|
||||
article = APISafeArticle(Articles[art], Accounts, config)
|
||||
data["posts"][art] = article
|
||||
|
||||
elif url.startswith(tuple(categories)):
|
||||
|
||||
Articles = Render.allArticles()
|
||||
article = APISafeArticle(Articles.get("/"+url,{}), Accounts, config)
|
||||
data = article
|
||||
|
||||
elif url.startswith("plugins"):
|
||||
|
||||
data = Plugins.GetData()
|
||||
|
||||
elif url.startswith("federate/"):
|
||||
|
||||
domain = url.split("/")[1]
|
||||
|
||||
# add to the list
|
||||
auto = config.get("federation", {}).get("auto", False)
|
||||
banned = config.get("federation", {}).get("banned_websites", [])
|
||||
known = config.get("federation", {}).get("known_websites", [])
|
||||
|
||||
d = Federation.Clean(domain)
|
||||
if d and d not in banned and d not in known and d != config.get("domain"):
|
||||
if auto:
|
||||
print(consoleForm("Federation"), "Auto-adding new domain:", d)
|
||||
Federation.Add(d, silent=True)
|
||||
else:
|
||||
print(consoleForm("Federation"), "Suggested new domain:", d)
|
||||
Federation.Suggest(d, silent=True)
|
||||
|
||||
|
||||
data = {"federating_with":domain}
|
||||
data["comments"] = {}
|
||||
|
||||
Articles = Render.allArticles()
|
||||
n = 0
|
||||
for art in Articles:
|
||||
|
||||
if Articles[art].get("reply_to_data", {}).get("domain") == domain:
|
||||
article = APISafeArticle(Articles[art], Accounts, config)
|
||||
data["comments"][art] = article
|
||||
|
||||
# Making sure to not overbombard the servers.
|
||||
n += 1
|
||||
if n > 20:
|
||||
break
|
||||
|
||||
data["known_websites"] = known
|
||||
|
||||
# Analytics API
|
||||
|
||||
elif url.startswith("analytics/totals"):
|
||||
|
||||
data = Analyse.CollectTotals()
|
||||
data["dates"] = Analyse.CollectTotals(everything=True).get("data", {})
|
||||
|
||||
elif url.startswith("analytics/rss"):
|
||||
|
||||
try:
|
||||
days = int(server.parsed.get("days",["10"])[0])
|
||||
except:
|
||||
days = 10
|
||||
|
||||
data = Analyse.EstimateRSSSubscribers(days)
|
||||
|
||||
elif url.startswith("analytics/ua"):
|
||||
|
||||
try:
|
||||
days = int(server.parsed.get("days",["10"])[0])
|
||||
except:
|
||||
days = 10
|
||||
|
||||
skip_me = server.parsed.get("skip_me",[""])[0]
|
||||
human_only = server.parsed.get("human_only",[""])[0]
|
||||
force_percent = server.parsed.get("force_percent",[""])[0]
|
||||
|
||||
data = Analyse.UserAgents(days, skip_me=skip_me,
|
||||
human_only=human_only,
|
||||
force_percent=force_percent,
|
||||
onlydata=True)
|
||||
|
||||
elif url.startswith("analytics/trends"):
|
||||
|
||||
try:
|
||||
days = int(server.parsed.get("days",["10"])[0])
|
||||
except:
|
||||
days = 10
|
||||
|
||||
try:
|
||||
cap = int(server.parsed.get("cap",["10"])[0])
|
||||
except:
|
||||
cap = 10
|
||||
|
||||
tags = server.parsed.get("tags", [""])[0]
|
||||
normalize = server.parsed.get("normalize", [""])[0]
|
||||
|
||||
data = Analyse.Trends(days, cap=cap,
|
||||
tags=tags,
|
||||
normalize=normalize,
|
||||
onlydata=True)
|
||||
|
||||
if server:
|
||||
data = json.dumps(data).encode("utf-8")
|
||||
Render.headers(server, 200)
|
||||
server.wfile.write(data)
|
||||
|
||||
else:
|
||||
return data
|
||||
|
||||
|
||||
def PeerStreaming(peerhandle):
|
||||
|
||||
# First we will attempt fetching the response as
|
||||
# if the handle is a channel.
|
||||
|
||||
url = "https://"+peerhandle.split("@")[1]+"/api/v1/video-channels/"+peerhandle.split("@")[0]+"/videos?isLive=True"
|
||||
|
||||
try:
|
||||
return Get(url)["data"]
|
||||
except:
|
||||
pass
|
||||
|
||||
# If that doesn't work, try full account
|
||||
|
||||
url = "https://"+peerhandle.split("@")[1]+"/api/v1/accounts/"+peerhandle.split("@")[0]+"/videos?isLive=True"
|
||||
|
||||
try:
|
||||
return Get(url)["data"]
|
||||
except:
|
||||
pass
|
||||
|
||||
# If nothing works. Returning nothing.( As list to avoid implemenation friction )
|
||||
return []
|
||||
|
||||
def IsPeerSreaming(peerhandle):
|
||||
|
||||
# Wrapper over the PeerStreaming.
|
||||
# To avoid over bombardment of APIs.
|
||||
|
||||
data = API_cache.get(peerhandle+"_streaming",{})
|
||||
|
||||
if time.time()-300 > data.get("time", 0):
|
||||
streaming = PeerStreaming(peerhandle)
|
||||
data = {"streaming":streaming,
|
||||
"time":time.time()}
|
||||
API_cache[peerhandle+"_streaming"]= data
|
||||
|
||||
streaming = data.get("streaming", [])
|
||||
return streaming
|
||||
|
||||
def GetMatrixMemberts(matrix):
|
||||
|
||||
# Getting matrix room ID
|
||||
|
||||
url = "https://matrix.org/_matrix/client/v3/directory/room/"+matrix.replace("#","%23")
|
||||
ID = Value(url, ["room_id"])
|
||||
|
||||
# I was trying to find a simple way to get
|
||||
# matrix API to work easily. Nothing works.
|
||||
# We will be parsing HTML code from view.matrix.org
|
||||
# intead.
|
||||
|
||||
url = 'https://view.matrix.org/room/'+ID
|
||||
|
||||
req = urllib.request.Request(url, headers={"User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:132.0) Gecko/20100101 Firefox/132.0"})
|
||||
f = urllib.request.urlopen(req, timeout=10)
|
||||
page = f.read().decode('utf-8')
|
||||
|
||||
p1 = ID+'/members">'
|
||||
p2 = ' Members</a>'
|
||||
|
||||
Members = int(page[page.find(p1)+len(p1):page.find(p2)])
|
||||
return Members
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
# AGPL 3 or any later version
|
||||
# (C) J.Y.Amihud ( Blender Dumbass )
|
||||
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import random
|
||||
import hashlib
|
||||
import urllib.parse
|
||||
|
||||
from datetime import datetime
|
||||
from getpass import getpass
|
||||
|
||||
from modules import Set
|
||||
from modules import markdown
|
||||
from modules.Common import *
|
||||
|
||||
|
||||
def Load(name):
|
||||
|
||||
folder = Set.Folder()+"/accounts/"
|
||||
try:
|
||||
with open(folder+name+".json") as o:
|
||||
return json.load(o)
|
||||
except:
|
||||
return {}
|
||||
|
||||
def Save(account):
|
||||
|
||||
name = account.get("username", "")
|
||||
folder = Set.Folder()+"/accounts/"
|
||||
|
||||
try:
|
||||
with open(folder+name+".json", "w") as save:
|
||||
json.dump(account, save, indent=4)
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Cannot save account!", e)
|
||||
|
||||
def Account():
|
||||
|
||||
anything = False
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
anything = True
|
||||
from modules import Help
|
||||
Help.Accounts()
|
||||
|
||||
if "--list" in sys.argv:
|
||||
anything = True
|
||||
List()
|
||||
|
||||
elif "--add" in sys.argv:
|
||||
anything = True
|
||||
try:
|
||||
account = sys.argv[ sys.argv.index("--add") + 1]
|
||||
if "--" in account: 1/0 # Failing this for the error message.
|
||||
Add(account)
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Account name wasn't specified!")
|
||||
print('Use: $ python3 run.py --account --add blenderdumbass')
|
||||
|
||||
elif "--edit" in sys.argv:
|
||||
anything = True
|
||||
try:
|
||||
account = sys.argv[ sys.argv.index("--edit") + 1]
|
||||
if "--" in account: 1/0 # Failing this for the error message.
|
||||
Edit(account)
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Account name wasn't specified!")
|
||||
print('Use: $ python3 run.py --account --edit blenderdumbass')
|
||||
|
||||
if not anything:
|
||||
print(clr["bold"]+clr["tdrd"]+"Unknown command!"+clr["norm"])
|
||||
|
||||
def List():
|
||||
|
||||
folder = Set.Folder()+"/accounts"
|
||||
for account in os.listdir(folder):
|
||||
if account.endswith(".json"):
|
||||
print(account.replace(".json", ""))
|
||||
|
||||
def Add(name):
|
||||
|
||||
# Adds a new account.
|
||||
|
||||
account = Load(name)
|
||||
if account:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Account already exists.")
|
||||
|
||||
account = {
|
||||
"username":name,
|
||||
"bio":"",
|
||||
"invite_codes":{},
|
||||
"invited":[],
|
||||
"invited_by":"",
|
||||
"password":hashlib.sha512(getpass("Account's Password: ").encode("utf-8")).hexdigest(),
|
||||
"title":name,
|
||||
"email":"",
|
||||
"website":"",
|
||||
"mastodon":"",
|
||||
"matrix":"",
|
||||
"sessions":{}
|
||||
}
|
||||
|
||||
|
||||
Save(account)
|
||||
|
||||
print(clr["bold"]+clr["tbyl"]+name+clr["norm"]+" is added."+clr["norm"])
|
||||
|
||||
def Edit(name):
|
||||
|
||||
folder = Set.Folder()+"/accounts/"
|
||||
os.system(Set.Load().get("editor", "nano")+" "+folder+name+".json")
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,380 @@
|
||||
# AGPL 3 or any later version
|
||||
# (C) J.Y.Amihud ( Blender Dumbass )
|
||||
|
||||
import time
|
||||
import random
|
||||
import threading
|
||||
|
||||
clr = {
|
||||
"norm":"\033[00m", # Reset to normal
|
||||
"bold":"\033[01m", # Bold Text
|
||||
"ital":"\033[03m", # Italic Text
|
||||
"undr":"\033[04m", # Underlined
|
||||
"blnk":"\033[05m", # Blinking
|
||||
|
||||
# Text
|
||||
"tdbl":"\033[30m", # Dark Black
|
||||
"tdrd":"\033[31m", # Dark Red
|
||||
"tdgr":"\033[32m", # Dark Green
|
||||
"tdyl":"\033[33m", # Dark Yellow
|
||||
"tdbu":"\033[34m", # Dark Blue
|
||||
"tdma":"\033[35m", # Dark Magenta
|
||||
"tdcy":"\033[36m", # Dark Cyan
|
||||
"tdwh":"\033[37m", # Dark White
|
||||
|
||||
"tbbl":"\033[90m", # Bright Black
|
||||
"tbrd":"\033[91m", # Bright Red
|
||||
"tbgr":"\033[92m", # Bright Green
|
||||
"tbyl":"\033[93m", # Bright Yellow
|
||||
"tbbu":"\033[94m", # Bright Blue
|
||||
"tbma":"\033[95m", # Bright Magenta
|
||||
"tbcy":"\033[96m", # Bright Cyan
|
||||
"tbwh":"\033[97m", # Bright White
|
||||
# Background
|
||||
"bdbl":"\033[40m", # Dark Black
|
||||
"bdrd":"\033[41m", # Dark Red
|
||||
"bdgr":"\033[42m", # Dark Green
|
||||
"bdyl":"\033[43m", # Dark Yellow
|
||||
"bdbu":"\033[44m", # Dark Blue
|
||||
"bdma":"\033[45m", # Dark Magenta
|
||||
"bdcy":"\033[46m", # Dark Cyan
|
||||
"bdwh":"\033[47m", # Dark White
|
||||
|
||||
"bbbl":"\033[100m", # Bright Black
|
||||
"bbrd":"\033[101m", # Bright Red
|
||||
"bbgr":"\033[102m", # Bright Green
|
||||
"bbyl":"\033[103m", # Bright Yellow
|
||||
"bbbu":"\033[104m", # Bright Blue
|
||||
"bbma":"\033[105m", # Bright Magenta
|
||||
"bbcy":"\033[106m", # Bright Cyan
|
||||
"bbwh":"\033[108m" # Bright White
|
||||
}
|
||||
|
||||
def Simplify(text, extrasimple=True):
|
||||
|
||||
good = "QWERTYUIOPLKJHGFDSAZXCVBNMqwertyuiopasdfghjklzxcvbnm.1234567890-_:* "
|
||||
|
||||
if extrasimple == "file":
|
||||
good = "QWERTYUIOPLKJHGFDSAZXCVBNMqwertyuiopasdfghjklzxcvbnm.1234567890-_"
|
||||
|
||||
elif extrasimple:
|
||||
good = "qwertyuiopasdfghjklzxcvbnm.1234567890-_:"
|
||||
text = text.lower()
|
||||
|
||||
ntext = ""
|
||||
for i in text:
|
||||
if i in good:
|
||||
ntext = ntext + i
|
||||
else:
|
||||
ntext = ntext + "_"
|
||||
|
||||
return ntext
|
||||
|
||||
def Safe(text):
|
||||
return str(text).replace("<", "<").replace(">", ">")
|
||||
|
||||
def RandString(n=50):
|
||||
# this will make a random string
|
||||
s = ""
|
||||
good = "QWERTYUIOPASDFGHJKLZXCVBNM1234567890"
|
||||
|
||||
for i in range(n):
|
||||
s = s + random.choice(good)
|
||||
|
||||
return s
|
||||
|
||||
|
||||
IDColors = {" ANONYMOUS ":clr["norm"]}
|
||||
|
||||
def stripDomain(domain):
|
||||
if "://" in domain:
|
||||
domain = domain[domain.find("://")+3:]
|
||||
if "/" in domain:
|
||||
domain = domain[:domain.find("/")]
|
||||
return domain
|
||||
|
||||
def StripUserAgent(text, via=""):
|
||||
|
||||
if "Googlebot" in text:
|
||||
return "[ Google ]"
|
||||
|
||||
if "YandexBot" in text:
|
||||
return "[ Yandex ]"
|
||||
|
||||
if "MYOB/6.66 (AN/ON)" in text:
|
||||
return "[ i2p ]"
|
||||
|
||||
if "Amazonbot" in text:
|
||||
return "[ Amazon ]"
|
||||
|
||||
if "bingbot" in text:
|
||||
return "[ Bing ]"
|
||||
|
||||
if "Bytespider" in text:
|
||||
return "[ Bytespider ]"
|
||||
|
||||
if "FlipboardProxy" in text:
|
||||
return "[ FlipboardProxy ]"
|
||||
|
||||
if "archive.org" in via:
|
||||
return "[ Wayback Machine ]"
|
||||
|
||||
if "facebookexternalhit" in text:
|
||||
return "[ Facebook / Threads / Instagram ]"
|
||||
|
||||
if "Mastodon" in text:
|
||||
s = "https://"
|
||||
e = ")"
|
||||
if s in text:
|
||||
link = text[text.find(s)+len(s):]
|
||||
if e in text:
|
||||
link = link[:link.find(e)].replace("/", "")
|
||||
return "[ Mastodon: "+link+"]"
|
||||
return "[ Mastodon ]"
|
||||
|
||||
if "Lemmy" in text:
|
||||
s = "https://"
|
||||
e = ")"
|
||||
if s in text:
|
||||
link = text[text.find(s)+len(s):]
|
||||
if e in text:
|
||||
link = link[:link.find(e)].replace("/", "")
|
||||
return "[ Lemmy: "+link+"]"
|
||||
return "[ Lemmy ]"
|
||||
|
||||
if "Friendica" in text:
|
||||
|
||||
s = "https://"
|
||||
e = ")"
|
||||
if s in text:
|
||||
link = text[text.find(s)+len(s):]
|
||||
if e in text:
|
||||
link = link[:link.find(e)].replace("/", "")
|
||||
return "[ Friendica: "+link+"]"
|
||||
return "[ Friendica ]"
|
||||
|
||||
# Browsers
|
||||
|
||||
browsers = {
|
||||
"Edg/":"Ms Edge",
|
||||
"OPR/":"Opera",
|
||||
"Presto":"Old Opera",
|
||||
"PaleMoon":"Palemoon",
|
||||
"Firefox":"Firefox",
|
||||
"Chrome":"Chromium",
|
||||
"AppleWebKit":"Safari",
|
||||
"Safari":"Safari"
|
||||
}
|
||||
|
||||
browser = ""
|
||||
for i in browsers:
|
||||
if i in text:
|
||||
browser = browsers[i]
|
||||
break
|
||||
|
||||
# Operating system
|
||||
|
||||
oss = {
|
||||
"Android":"Android",
|
||||
"CrOS":"Chrome OS",
|
||||
"Linux":"GNU / Linux",
|
||||
"Windows":"Windows",
|
||||
"iPad":"iOS",
|
||||
"iPhone":"iOS",
|
||||
"Mac OS":"macOS"
|
||||
}
|
||||
|
||||
os = ""
|
||||
for i in oss:
|
||||
if i in text:
|
||||
os = oss[i]
|
||||
break
|
||||
|
||||
if os and browser:
|
||||
return "[ "+browser+" | "+os+" ]"
|
||||
|
||||
return "[ "+text+" ]"
|
||||
|
||||
|
||||
|
||||
humanCheckCache = {}
|
||||
pathsHTML = {}
|
||||
|
||||
def RecordHTML(path, html):
|
||||
pathsHTML[path] = html
|
||||
|
||||
def GuessHuman(server):
|
||||
|
||||
via = server.headers.get("Via", "")
|
||||
useragent = str(server.headers.get("User-Agent"))
|
||||
if " | " not in StripUserAgent(useragent, via):
|
||||
return False
|
||||
|
||||
if not server.cookie:
|
||||
return False
|
||||
|
||||
if time.time() -120 > humanCheckCache.get(server.cookie,{}).get("timestamp", 0)\
|
||||
and not humanCheckCache.get(server.cookie,{}).get("human", False):
|
||||
try:
|
||||
del humanCheckCache[server.cookie]
|
||||
except:
|
||||
pass
|
||||
|
||||
if server.cookie not in humanCheckCache:
|
||||
humanCheckCache[server.cookie] = {"human":False,
|
||||
"path":server.path,
|
||||
"count":0,
|
||||
"timestamp":time.time()}
|
||||
return False
|
||||
|
||||
elif humanCheckCache[server.cookie].get("human"):
|
||||
return True
|
||||
|
||||
elif not humanCheckCache[server.cookie].get("human"):
|
||||
|
||||
if "?" in server.path:
|
||||
path = server.path[:server.path.find("?")]
|
||||
else: path = server.path
|
||||
|
||||
if path in pathsHTML.get(humanCheckCache[server.cookie].get("path", "/"),""):
|
||||
humanCheckCache[server.cookie]["count"] += 1
|
||||
|
||||
if humanCheckCache[server.cookie]["count"] > 5:
|
||||
humanCheckCache[server.cookie]["human"] = True
|
||||
|
||||
# Garbage collection
|
||||
try:
|
||||
del humanCheckCache[server.cookie]["html"]
|
||||
except:
|
||||
pass
|
||||
|
||||
return True
|
||||
|
||||
def typeOfConnection(server):
|
||||
|
||||
|
||||
if " | " not in StripUserAgent(str(server.headers.get("User-Agent"))):
|
||||
return clr["norm"]+clr["bold"]+clr["bdrd"]+clr["tbwh"]+" BOT! "+clr["norm"]
|
||||
|
||||
try:
|
||||
|
||||
if not server.cookie:
|
||||
return clr["norm"]+clr["bold"]+clr["bdrd"]+clr["tbwh"]+" BOT? "+clr["norm"]
|
||||
|
||||
from modules import Render
|
||||
if Render.validate(server.cookie):
|
||||
return clr["norm"]+clr["bold"]+clr["bdgr"]+clr["tdbl"]+" LOGD "+clr["norm"]
|
||||
|
||||
if GuessHuman(server):
|
||||
return clr["norm"]+clr["bold"]+clr["bdyl"]+clr["tdbl"]+" HUMN "+clr["norm"]
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
return clr["norm"]+clr["bold"]+clr["bdwh"]+clr["tdbl"]+" NEW "+clr["norm"]
|
||||
|
||||
|
||||
def consoleForm(obj):
|
||||
|
||||
from modules.Render import accounts
|
||||
|
||||
# Username preview
|
||||
username = ""
|
||||
Accounts = accounts()
|
||||
for account in Accounts:
|
||||
if obj in Accounts[account].get("sessions"):
|
||||
username = account
|
||||
break
|
||||
|
||||
# Function that prints a pretty object info about
|
||||
|
||||
if not obj: obj = " ANONYMOUS "
|
||||
|
||||
ID = obj
|
||||
|
||||
if not username:
|
||||
NAME = obj[-14:]
|
||||
else:
|
||||
NAME = username[:15].upper()
|
||||
while len(NAME) < 14:
|
||||
NAME = NAME + " "
|
||||
|
||||
c = IDcolor(ID)
|
||||
|
||||
return c + " " + NAME + " " + clr["norm"]
|
||||
|
||||
def IDcolor(ID):
|
||||
|
||||
# Coloring
|
||||
if ID not in IDColors:
|
||||
r = random.random()
|
||||
g = random.random()
|
||||
b = random.random()
|
||||
|
||||
ct = clr["tbwh"]
|
||||
if max(r, g, b ) > 0.9 or sum((r, g, b))/3 > 0.5:
|
||||
ct = clr["tdbl"]
|
||||
|
||||
c = clr["bold"] + "\033[48;2;"+str(int(r*256))+";"+str(int(g*256))+";"+str(int(b*256))+"m"+ct
|
||||
IDColors[ID] = c
|
||||
c = IDColors[ID]
|
||||
return c
|
||||
|
||||
|
||||
# Licenses for stuff
|
||||
Licenses = {
|
||||
"cc0":{
|
||||
"name":"CC0 Public Domain",
|
||||
"link":"https://creativecommons.org/publicdomain/zero/1.0/"
|
||||
},
|
||||
"cc-by":{
|
||||
"name":"Creative Commons Attribution",
|
||||
"link":"https://creativecommons.org/licenses/by/4.0/"
|
||||
},
|
||||
"cc-by-sa":{
|
||||
"name":"Creative Commons Attribution Share-Alike",
|
||||
"link":"https://creativecommons.org/licenses/by-sa/4.0/"
|
||||
},
|
||||
"gpl3":{
|
||||
"name":"GNU General Public License Version 3",
|
||||
"link":"https://www.gnu.org/licenses/gpl.html"
|
||||
},
|
||||
"lgpl3":{
|
||||
"name":"GNU Lesser General Public License Version 3",
|
||||
"link":"https://www.gnu.org/licenses/lgpl.html"
|
||||
},
|
||||
"agpl3":{
|
||||
"name":"GNU Affero General Public License Version 3",
|
||||
"link":"https://www.gnu.org/licenses/agpl.html"
|
||||
},
|
||||
"gpl3-or-later":{
|
||||
"name":"GNU General Public License Version 3 or any later version",
|
||||
"link":"https://www.gnu.org/licenses/"
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
def csize(x):
|
||||
|
||||
x = float(x)
|
||||
|
||||
l = ["B","KB", "MB", "GB", "TB"]
|
||||
|
||||
for i in range(5):
|
||||
if x > 1024:
|
||||
x = x / 1024
|
||||
else:
|
||||
return str(round(x, 2))+" "+l[i]
|
||||
return str(round(x, 2))+" "+l[i]
|
||||
|
||||
|
||||
locks = {}
|
||||
def ThreadLock(filename):
|
||||
try: return locks[filename]
|
||||
except:
|
||||
locks[filename] = threading.Lock()
|
||||
return locks[filename]
|
||||
@@ -0,0 +1,108 @@
|
||||
# AGPL 3 or any later version
|
||||
# (C) J.Y.Amihud ( Blender Dumbass )
|
||||
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
|
||||
from modules.Common import *
|
||||
from modules import Set
|
||||
|
||||
def Create(name):
|
||||
|
||||
# Creates a base directory before publishing.
|
||||
|
||||
if not name.count("/") == 1:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Wrong Format!")
|
||||
print('Use: $ python3 run.py --create articles/My_Article')
|
||||
|
||||
name = Simplify(name.split("/")[0])+"/"+Simplify(name.split("/")[1])
|
||||
|
||||
folder = Set.Folder()+"/editing"
|
||||
|
||||
try:
|
||||
os.makedirs(folder+"/"+name)
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Can't Create!", e)
|
||||
|
||||
# The text file
|
||||
text = open(folder+"/"+name+"/text.md", "w")
|
||||
text.close()
|
||||
|
||||
metadata = {
|
||||
"title":name.split("/")[1],
|
||||
"timestamp":time.time(),
|
||||
"description":"",
|
||||
"author":"",
|
||||
"thumbnail":"",
|
||||
"license":"cc-by-sa",
|
||||
"views":{
|
||||
"amount":0,
|
||||
"viewers":[],
|
||||
"dates":{}
|
||||
},
|
||||
"recording":"",
|
||||
"comments":{
|
||||
"comments":[],
|
||||
"requests":[]
|
||||
}
|
||||
}
|
||||
|
||||
with open(folder+"/"+name+"/metadata.json", "w") as save:
|
||||
json.dump(metadata, save, indent=4)
|
||||
|
||||
print(clr["bold"]+clr["tbyl"]+name+clr["norm"]+" is created of editing.")
|
||||
print(' To write text use: $ python3 run.py --write '+name)
|
||||
print('To edit metadata use: $ python3 run.py --metadata '+name)
|
||||
print(' To publish use: $ python3 run.py --publish '+name)
|
||||
|
||||
def Write(name):
|
||||
|
||||
if not name.count("/") == 1:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Wrong Format!")
|
||||
print('Use: $ python3 run.py --write articles/My_Article')
|
||||
|
||||
name = Simplify(name.split("/")[0])+"/"+Simplify(name.split("/")[1])
|
||||
|
||||
folder = Set.Folder()+"/editing/"+name
|
||||
|
||||
os.system(Set.Load().get("editor", "nano")+" "+folder+"/text.md")
|
||||
|
||||
def Metadata(name):
|
||||
|
||||
if not name.count("/") == 1:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Wrong Format!")
|
||||
print('Use: $ python3 run.py --metadata articles/My_Article')
|
||||
|
||||
name = Simplify(name.split("/")[0])+"/"+Simplify(name.split("/")[1])
|
||||
|
||||
folder = Set.Folder()+"/editing/"+name
|
||||
|
||||
os.system(Set.Load().get("editor", "nano")+" "+folder+"/metadata.json")
|
||||
|
||||
def Publish(name):
|
||||
|
||||
if not name.count("/") == 1:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Wrong Format!")
|
||||
print('Use: $ python3 run.py --publish articles/My_Article')
|
||||
|
||||
name = Simplify(name.split("/")[0])+"/"+Simplify(name.split("/")[1])
|
||||
|
||||
folder = Set.Folder()
|
||||
|
||||
source = folder+"/editing/"+name
|
||||
destination = folder+"/tabs/"+name
|
||||
|
||||
|
||||
|
||||
|
||||
try:
|
||||
|
||||
os.makedirs(folder+"/tabs/"+name)
|
||||
os.rename(source+"/text.md", destination+"/text.md")
|
||||
os.rename(source+"/metadata.json", destination+"/metadata.json")
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Could not publish!", e)
|
||||
|
||||
print(destination, "has been published!")
|
||||
@@ -0,0 +1,268 @@
|
||||
import smtplib
|
||||
import email
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
|
||||
import json
|
||||
|
||||
from modules import Set
|
||||
from modules.Common import *
|
||||
|
||||
from modules import Render
|
||||
|
||||
verifying = {}
|
||||
|
||||
def Send(reciever, subject, html, text=""):
|
||||
|
||||
config = Set.Load()
|
||||
Email = config.get("email")
|
||||
|
||||
if not Email:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" No email credentials!")
|
||||
|
||||
try: port = int(Email.get("port", ""))
|
||||
except: port = 587
|
||||
|
||||
message = MIMEMultipart()
|
||||
message["From"] = Email["visible"]
|
||||
message["To"] = reciever
|
||||
message["Subject"] = subject
|
||||
message['Date'] = email.utils.formatdate(localtime=True)
|
||||
|
||||
message.attach(MIMEText(html, "html"))
|
||||
if text:
|
||||
message.attach(MIMEText(text, "plain"))
|
||||
|
||||
# Send the email
|
||||
with smtplib.SMTP(Email["server"], port) as server:
|
||||
|
||||
server.starttls()
|
||||
|
||||
server.login(Email["sender"],
|
||||
Email["password"])
|
||||
|
||||
server.sendmail(Email["sender"],
|
||||
reciever,
|
||||
message.as_string())
|
||||
|
||||
print('Email "'+subject+'" was sent to', reciever)
|
||||
|
||||
def VerifyPage(server):
|
||||
|
||||
user = Render.validate(server.cookie)
|
||||
|
||||
# Authorization check
|
||||
if not user:
|
||||
Render.AccessDenied(server)
|
||||
return
|
||||
|
||||
config = Set.Load()
|
||||
|
||||
# Generating <head>
|
||||
html = Render.head(title = "Verify Email",
|
||||
description = "Verify Email",
|
||||
config = config
|
||||
)
|
||||
|
||||
html = html + Render.Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet"))
|
||||
|
||||
code = server.parsed.get("code", [""])[0]
|
||||
if not code:
|
||||
|
||||
|
||||
|
||||
html = html + """
|
||||
|
||||
<div class="middle_section_article">
|
||||
|
||||
<div class="dark_box">
|
||||
|
||||
<form action="/email_verify">
|
||||
|
||||
<center>
|
||||
<br>
|
||||
You have recieved an email with a code.<br><br>
|
||||
<small>It could be in spam.</small>
|
||||
<br><br>
|
||||
<input class="button" name="code" required placeholder="Your code...">
|
||||
<br>
|
||||
<button class="button" type="submit">
|
||||
<img alt="[icon ok]" style="vertical-align: middle" src="/icon/ok">
|
||||
Verify
|
||||
</button>
|
||||
|
||||
</center>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
"""
|
||||
code = RandString(10)
|
||||
verifying[code] = user.get("email")
|
||||
|
||||
text = """
|
||||
<center>
|
||||
Your Verification Code
|
||||
<br>
|
||||
<div class="toot">"""+code+"""
|
||||
</div>
|
||||
</center>
|
||||
"""
|
||||
|
||||
texthtml = Format(text)
|
||||
|
||||
Send(user.get("email", ""), "Verification Code", texthtml, text)
|
||||
|
||||
Render.send(server, html, 200)
|
||||
|
||||
else:
|
||||
|
||||
# If we recieved the code.
|
||||
|
||||
if code not in verifying:
|
||||
Render.Error(server, "Wrong Code!")
|
||||
return
|
||||
|
||||
|
||||
if verifying[code] != user.get("email"):
|
||||
Render.Error(server, "Wrong Code!")
|
||||
return
|
||||
|
||||
user["email_verified"] = True
|
||||
|
||||
f = Set.Folder()
|
||||
folder = f+"/accounts"
|
||||
with open(folder+"/"+user.get("username", "")+".json", "w") as save:
|
||||
json.dump(user, save, indent=4)
|
||||
|
||||
del verifying[code]
|
||||
|
||||
Render.Redirect(server, "/settings#email")
|
||||
|
||||
def SignPetition(server):
|
||||
|
||||
config = Set.Load()
|
||||
|
||||
# Generating <head>
|
||||
html = Render.head(title = "Verify Email",
|
||||
description = "Verify Email",
|
||||
config = config
|
||||
)
|
||||
|
||||
html = html + Render.Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet"))
|
||||
|
||||
article = server.parsed.get("article", [""])[0]
|
||||
print(article)
|
||||
|
||||
Articles = Render.allArticles()
|
||||
if article not in Articles:
|
||||
Render.AccessDenied(server)
|
||||
return
|
||||
|
||||
Article = Articles[article]
|
||||
|
||||
code = server.parsed.get("code", [""])[0]
|
||||
email = server.parsed.get("email", [""])[0]
|
||||
|
||||
if email in Article.get("petition", {}).get("signatures", []):
|
||||
Render.Error(server, "You already signed!")
|
||||
return
|
||||
|
||||
if not code:
|
||||
|
||||
html = html + """
|
||||
|
||||
<div class="middle_section_article">
|
||||
|
||||
<div class="dark_box">
|
||||
|
||||
<form action="/sign_petition">
|
||||
|
||||
<center>
|
||||
<br>
|
||||
You have recieved an email with a code.<br><br>
|
||||
<small>It could be in spam.</small>
|
||||
<br><br>
|
||||
<input type="hidden" name="article" value='"""+article+"""'>
|
||||
<input class="button" name="code" required placeholder="Your code...">
|
||||
<br>
|
||||
<button class="button" type="submit">
|
||||
<img alt="[icon ok]" style="vertical-align: middle" src="/icon/ok">
|
||||
Verify
|
||||
</button>
|
||||
|
||||
</center>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
"""
|
||||
code = RandString(10)
|
||||
verifying[code] = email
|
||||
|
||||
text = """
|
||||
<center>
|
||||
Your Verification Code
|
||||
<br>
|
||||
<div class="toot">"""+code+"""
|
||||
</div>
|
||||
</center>
|
||||
"""
|
||||
|
||||
text = Format(text)
|
||||
|
||||
Send(email, "Verification Code", text)
|
||||
|
||||
Render.send(server, html, 200)
|
||||
|
||||
else:
|
||||
|
||||
# If we recieved the code.
|
||||
|
||||
if code not in verifying:
|
||||
Render.Error(server, "Wrong Code!")
|
||||
return
|
||||
|
||||
|
||||
Article["petition"]["signed"] += 1
|
||||
Article["petition"]["signatures"].append(verifying[code])
|
||||
|
||||
f = Set.Folder()
|
||||
with open(f+"/tabs"+article+"/metadata.json", "w") as save:
|
||||
json.dump(Article, save, indent=4)
|
||||
|
||||
|
||||
del verifying[code]
|
||||
|
||||
Render.Redirect(server, article)
|
||||
|
||||
|
||||
###
|
||||
|
||||
def Format(text):
|
||||
|
||||
config = Set.Load()
|
||||
|
||||
favicon = config.get("favicon", "")
|
||||
if favicon.startswith("/"):
|
||||
favicon = "https://"+config.get("domain", "")+favicon
|
||||
|
||||
css = open(config.get("css", "default.css"), "r")
|
||||
html = '<html><style>'+css.read()+'</style>'
|
||||
html = html + '<div class="dark_box"><center>'
|
||||
|
||||
if favicon:
|
||||
html = html + '<img alt="[favicon]" style="height:40px;vertical-align: middle" src="'+favicon+'">'
|
||||
|
||||
html = html + " "+config.get("title", "My Website")
|
||||
html = html + "</center><br></div>"
|
||||
html = html + '<div class="dark_box">'
|
||||
html = html + text
|
||||
html = html + '</div></html>'
|
||||
|
||||
return html
|
||||
|
||||
@@ -0,0 +1,413 @@
|
||||
# AGPL 3 or any later version
|
||||
# (C) J.Y.Amihud ( Blender Dumbass )
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import json
|
||||
import random
|
||||
import traceback
|
||||
import hashlib
|
||||
import urllib.parse
|
||||
|
||||
from datetime import datetime
|
||||
from getpass import getpass
|
||||
|
||||
from modules import Set
|
||||
from modules import API
|
||||
from modules import Plugins
|
||||
from modules import Analyse
|
||||
from modules import Render
|
||||
from modules import markdown
|
||||
from modules import ActivityPub
|
||||
from modules.Common import *
|
||||
|
||||
def Federation():
|
||||
|
||||
anything = False
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
from modules import Help
|
||||
Help.Federation()
|
||||
anything = True
|
||||
|
||||
|
||||
if "--add" in sys.argv:
|
||||
anything = True
|
||||
try:
|
||||
domain = sys.argv[ sys.argv.index("--add") + 1]
|
||||
if "--" in domain: 1/0 # Failing this for the error message.
|
||||
Add(domain)
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Domain wasn't specified!")
|
||||
print('Use: $ python3 run.py --federation --add blog.madiator.com')
|
||||
|
||||
|
||||
if "--remove" in sys.argv:
|
||||
anything = True
|
||||
try:
|
||||
domain = sys.argv[ sys.argv.index("--remove") + 1]
|
||||
if "--" in domain: 1/0 # Failing this for the error message.
|
||||
Remove(domain)
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Domain wasn't specified!")
|
||||
print('Use: $ python3 run.py --federation --remove threads.net')
|
||||
|
||||
|
||||
if "--ban" in sys.argv:
|
||||
anything = True
|
||||
try:
|
||||
domain = sys.argv[ sys.argv.index("--ban") + 1]
|
||||
if "--" in domain: 1/0 # Failing this for the error message.
|
||||
Ban(domain)
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Domain wasn't specified!")
|
||||
print('Use: $ python3 run.py --federation --ban threads.net')
|
||||
|
||||
if "--unban" in sys.argv:
|
||||
anything = True
|
||||
try:
|
||||
domain = sys.argv[ sys.argv.index("--unban") + 1]
|
||||
if "--" in domain: 1/0 # Failing this for the error message.
|
||||
UnBan(domain)
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Domain wasn't specified!")
|
||||
print('Use: $ python3 run.py --federation --unban blog.madiator.com')
|
||||
|
||||
|
||||
if "--list" in sys.argv:
|
||||
anything = True
|
||||
List()
|
||||
|
||||
if "--auto_on" in sys.argv:
|
||||
anything = True
|
||||
AutoOn()
|
||||
|
||||
if "--auto_off" in sys.argv:
|
||||
anything = True
|
||||
AutoOff()
|
||||
|
||||
|
||||
#########
|
||||
|
||||
if not anything:
|
||||
print(clr["bold"]+clr["tdrd"]+"Unknown command!"+clr["norm"])
|
||||
|
||||
def Clean(domain):
|
||||
|
||||
return domain.replace("https://", "").replace("http://", "")
|
||||
|
||||
def Add(domain, silent=False):
|
||||
|
||||
domain = Clean(domain)
|
||||
config = Set.Load()
|
||||
|
||||
if domain == config.get("domain"):
|
||||
return
|
||||
|
||||
if "federation" not in config: config["federation"] = {}
|
||||
if "known_websites" not in config["federation"]: config["federation"]["known_websites"] = []
|
||||
if "suggested_websites" not in config["federation"]: config["federation"]["suggested_websites"] = []
|
||||
if "banned_websites" not in config["federation"]: config["federation"]["banned_websites"] = []
|
||||
|
||||
if domain not in config["federation"]["known_websites"]:
|
||||
|
||||
if domain in config["federation"]["suggested_websites"]:
|
||||
config["federation"]["suggested_websites"].remove(domain)
|
||||
if domain in config["federation"]["banned_websites"]:
|
||||
config["federation"]["banned_websites"].remove(domain)
|
||||
|
||||
config["federation"]["known_websites"].append(domain)
|
||||
|
||||
Set.Save(config)
|
||||
|
||||
if not silent:
|
||||
print(clr["bold"]+clr["tbyl"]+domain+clr["norm"]+" is added for federation.")
|
||||
|
||||
def Suggest(domain, silent=False):
|
||||
|
||||
domain = Clean(domain)
|
||||
config = Set.Load()
|
||||
|
||||
if domain == config.get("domain"):
|
||||
return
|
||||
|
||||
if "federation" not in config: config["federation"] = {}
|
||||
if "suggested_websites" not in config["federation"]: config["federation"]["suggested_websites"] = []
|
||||
|
||||
if domain not in config["federation"]["suggested_websites"]:
|
||||
config["federation"]["suggested_websites"].append(domain)
|
||||
|
||||
Set.Save(config)
|
||||
|
||||
if not silent:
|
||||
print(clr["bold"]+clr["tbyl"]+domain+clr["norm"]+" is suggested for federation.")
|
||||
|
||||
def Remove(domain):
|
||||
|
||||
domain = Clean(domain)
|
||||
|
||||
config = Set.Load()
|
||||
|
||||
if "federation" not in config: config["federation"] = {}
|
||||
if "known_websites" not in config["federation"]: config["federation"]["known_websites"] = []
|
||||
|
||||
try:
|
||||
config["federation"]["known_websites"].remove(domain)
|
||||
|
||||
except:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Domain is not known!")
|
||||
return
|
||||
|
||||
Set.Save(config)
|
||||
Suggest(domain, True)
|
||||
|
||||
print(clr["bold"]+clr["tbyl"]+domain+clr["norm"]+" is removed from federation.")
|
||||
|
||||
def Ban(domain):
|
||||
|
||||
domain = Clean(domain)
|
||||
|
||||
config = Set.Load()
|
||||
|
||||
if "federation" not in config: config["federation"] = {}
|
||||
if "known_websites" not in config["federation"]: config["federation"]["known_websites"] = []
|
||||
if "suggested_websites" not in config["federation"]: config["federation"]["suggested_websites"] = []
|
||||
if "banned_websites" not in config["federation"]: config["federation"]["banned_websites"] = []
|
||||
|
||||
if domain not in config["federation"]["banned_websites"]:
|
||||
|
||||
if domain in config["federation"]["known_websites"]:
|
||||
config["federation"]["known_websites"].remove(domain)
|
||||
if domain in config["federation"]["suggested_websites"]:
|
||||
config["federation"]["suggested_websites"].remove(domain)
|
||||
|
||||
config["federation"]["banned_websites"].append(domain)
|
||||
|
||||
Set.Save(config)
|
||||
|
||||
print(clr["bold"]+clr["tbyl"]+domain+clr["norm"]+" is banned.")
|
||||
|
||||
def UnBan(domain):
|
||||
|
||||
domain = Clean(domain)
|
||||
config = Set.Load()
|
||||
|
||||
if "federation" not in config: config["federation"] = {}
|
||||
if "banned_websites" not in config["federation"]: config["federation"]["banned_websites"] = []
|
||||
|
||||
|
||||
try:
|
||||
config["federation"]["banned_websites"].remove(domain)
|
||||
except:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Domain is not banned!")
|
||||
return
|
||||
|
||||
Set.Save(config)
|
||||
Suggest(domain, True)
|
||||
|
||||
print(clr["bold"]+clr["tbyl"]+domain+clr["norm"]+" is unbanned.")
|
||||
|
||||
def List():
|
||||
|
||||
config = Set.Load()
|
||||
|
||||
print()
|
||||
print(" Federating with:")
|
||||
print()
|
||||
|
||||
for domain in config.get("federation", {}).get("known_websites", []):
|
||||
print(domain)
|
||||
|
||||
print()
|
||||
print(" Suggested:")
|
||||
print()
|
||||
|
||||
for domain in config.get("federation", {}).get("suggested_websites", []):
|
||||
print(domain)
|
||||
|
||||
print()
|
||||
print(" Banned from auto-federating:")
|
||||
print()
|
||||
|
||||
for domain in config.get("federation", {}).get("banned_websites", []):
|
||||
print(domain)
|
||||
|
||||
def AutoOn():
|
||||
|
||||
config = Set.Load()
|
||||
|
||||
if "federation" not in config: config["federation"] = {}
|
||||
config["federation"]["auto"] = True
|
||||
|
||||
Set.Save(config)
|
||||
|
||||
print("Auto-federation is turned on.")
|
||||
|
||||
def AutoOff():
|
||||
|
||||
config = Set.Load()
|
||||
|
||||
if "federation" not in config: config["federation"] = {}
|
||||
config["federation"]["auto"] = False
|
||||
|
||||
Set.Save(config)
|
||||
|
||||
print("Auto-federation is turned off.")
|
||||
|
||||
|
||||
def FederationThread():
|
||||
|
||||
# This thread happens alongside the main website thread.
|
||||
# And is used to request websites various federation
|
||||
# requests.
|
||||
|
||||
while True:
|
||||
|
||||
try:
|
||||
|
||||
time.sleep(600)
|
||||
|
||||
API.PetitionFetch()
|
||||
|
||||
# Fetch() # This needs to be rewritten to not try to fetch AP servers
|
||||
ActivityPub.SendArticles()
|
||||
Analyse.CollectTotals() # Making sure the totals are collected.
|
||||
Plugins.onFEDI()
|
||||
|
||||
|
||||
except KeyboardInterrupt:
|
||||
return
|
||||
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
def Fetch():
|
||||
|
||||
config = Set.Load()
|
||||
|
||||
known = config.get("federation", {}).get("known_websites", [])
|
||||
banned = config.get("federation", {}).get("banned_websites", [])
|
||||
auto = config.get("federation", {}).get("auto", False)
|
||||
|
||||
Articles = Render.allArticles()
|
||||
|
||||
print(" "+consoleForm("Federation "), "Fetching federation data")
|
||||
|
||||
for domain in known:
|
||||
|
||||
if domain in banned:
|
||||
continue
|
||||
|
||||
#print(" "+consoleForm("Federation "), "Fetching:", domain)
|
||||
|
||||
try:
|
||||
data = API.Get("https://"+domain+"/json/federate/"+config.get("domain", ""))
|
||||
except:
|
||||
#print(" "+consoleForm("Federation "), clr["bold"]+clr["tdrd"]+"Failed to fetch:", clr["norm"]+ domain)
|
||||
continue
|
||||
|
||||
# Adding to the known_websites
|
||||
|
||||
for d in data.get("known_websites", []):
|
||||
d = Clean(d)
|
||||
if d not in banned and d not in known and d != config.get("domain"):
|
||||
if auto:
|
||||
print(" "+consoleForm("Federation "), "Auto-adding new domain:", d)
|
||||
Add(d, silent=True)
|
||||
else:
|
||||
print(" "+consoleForm("Federation "), "Suggested new domain:", d)
|
||||
Suggest(d, silent=True)
|
||||
|
||||
# Adding comments
|
||||
|
||||
for comment in data.get("comments", {}):
|
||||
|
||||
comment = data["comments"][comment]
|
||||
is_reply_to = comment.get("is_reply_to", "")
|
||||
article = is_reply_to.replace("https://", "").replace(config.get("domain", ""), "")
|
||||
|
||||
article = Articles.get(article, {})
|
||||
|
||||
if article:
|
||||
|
||||
# Checking if this comment is already recorded
|
||||
url = comment.get("url","")
|
||||
found = False
|
||||
for c in article.get("comments", {}).get("comments", []):
|
||||
if c.get("url", "") == url:
|
||||
found = True
|
||||
|
||||
if not found:
|
||||
print(" "+consoleForm("Federation "), "New comment from:", domain, "on:", article.get("url", ""))
|
||||
|
||||
description = comment.get("description", "")
|
||||
if not description:
|
||||
description = "A reply from "+domain
|
||||
|
||||
description = "*...from "+domain+"*\n\n**"+comment.get("title", "Reply")+"**\n\n"+description
|
||||
|
||||
|
||||
cdata = {"username":comment.get("author",domain),
|
||||
"text":Safe(description),
|
||||
"url":Safe(url)}
|
||||
|
||||
if "comments" not in article:
|
||||
article["comments"] = {}
|
||||
if "comments" not in article["comments"]:
|
||||
article["comments"]["comments"] = []
|
||||
|
||||
article["comments"]["comments"].append(cdata)
|
||||
|
||||
metadata = Set.Folder()+"/tabs"+article.get("url","")+"/metadata.json"
|
||||
|
||||
try:
|
||||
with ThreadLock(metadata):
|
||||
with open(metadata, "w") as save:
|
||||
json.dump(article, save, indent=4)
|
||||
except:
|
||||
pass
|
||||
|
||||
# Notification
|
||||
if article.get("author"):
|
||||
text = comment.get("author", {}).get("username", "")
|
||||
text = text + ' from '+domain+' made an article reply to '
|
||||
text = text + article.get("title", "")
|
||||
|
||||
Render.Notify(article.get("author"), Safe(comment.get("url", article.get("url", ""))), Safe(text))
|
||||
|
||||
|
||||
# Get plugins
|
||||
|
||||
try:
|
||||
data = API.Get("https://"+domain+"/json/plugins")
|
||||
except:
|
||||
continue
|
||||
|
||||
# Load
|
||||
|
||||
try:
|
||||
|
||||
with ThreadLock(filename):
|
||||
with open(filename) as o:
|
||||
Pdata = json.load(o)
|
||||
except:
|
||||
Pdata = {}
|
||||
|
||||
Pdata[domain] = data
|
||||
|
||||
# Save
|
||||
try:
|
||||
filename = Set.Folder()+"/plugins.json"
|
||||
with ThreadLock(filename):
|
||||
with open(filename, "w") as save:
|
||||
json.dump(Pdata, save, indent=4)
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Cannot save plugins!", e)
|
||||
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
# AGPL 3 or any later version
|
||||
# (C) J.Y.Amihud ( Blender Dumbass )
|
||||
|
||||
from modules.Common import *
|
||||
|
||||
def Help():
|
||||
|
||||
print()
|
||||
print(clr["bold"]+" BDServer Help"+clr["norm"])
|
||||
print()
|
||||
print(clr["tdyl"]+"--help"+clr["norm"]+" - Prints this Help Page.")
|
||||
print(clr["tdyl"]+"--run"+clr["norm"]+" - Run the server.")
|
||||
print(clr["tdyl"]+"--analyse"+clr["norm"]+" - Analyse the website.")
|
||||
print()
|
||||
print(clr["tdyl"]+"--set"+clr["norm"]+" - Use for changing settings.")
|
||||
print(clr["tdyl"]+"--account"+clr["norm"]+" - Manage Accounts.")
|
||||
print()
|
||||
print(clr["tdyl"]+"--create"+clr["norm"]+" - Start a new article.")
|
||||
print()
|
||||
print(clr["tdyl"]+"--config"+clr["norm"]+" - Edit config file directly, bypassing --set.")
|
||||
print(clr["tdyl"]+"--folder"+clr["norm"]+" - Show the folder of the website's data.")
|
||||
print()
|
||||
print(clr["tdyl"]+"--federation"+clr["norm"]+" - Federation settings with other BDServers.")
|
||||
print()
|
||||
print(clr["tdyl"]+"--transfer"+clr["norm"]+" - Transfers legacy website data to the new format.")
|
||||
print(clr["tdyl"]+"--ban"+clr["norm"]+" - Ban a cookie from DDosing your website.")
|
||||
print()
|
||||
|
||||
print()
|
||||
print(" If you just installed BDServer start \n with "+clr["tdyl"]+"--set"+clr["norm"]+" to set things up.")
|
||||
print()
|
||||
|
||||
def Set():
|
||||
|
||||
print()
|
||||
print(clr["bold"]+" BDServer Set Help "+clr["norm"])
|
||||
print()
|
||||
print(" Set is used for setting up server configurations.")
|
||||
print()
|
||||
print(clr["tdyl"]+"--title"+clr["norm"]+" - Set title of the website.")
|
||||
print(clr["tdyl"]+"--tagline"+clr["norm"]+" - Set tagline of the website.")
|
||||
print(clr["tdyl"]+"--domain"+clr["norm"]+" - Let the server know the clearnet domain.")
|
||||
print(clr["tdyl"]+"--tor"+clr["norm"]+" - Let the server know the tor domain.")
|
||||
print(clr["tdyl"]+"--email"+clr["norm"]+" - Set Email SMTP account for internal automatic messages.")
|
||||
print(clr["tdyl"]+"--account"+clr["norm"]+" - Set the Main Account, for the footer and stuff.")
|
||||
print(clr["tdyl"]+"--port"+clr["norm"]+" - Set port where to run the website.")
|
||||
print(clr["tdyl"]+"--css"+clr["norm"]+" - Set a CSS file.")
|
||||
print(clr["tdyl"]+"--css_edit"+clr["norm"]+" - edit a CSS file.")
|
||||
print(clr["tdyl"]+"--favicon"+clr["norm"]+" - Set a favicon file.")
|
||||
print(clr["tdyl"]+"--add_tab"+clr["norm"]+" - Adds a category of articles.")
|
||||
print(clr["tdyl"]+"--edit_tab"+clr["norm"]+" - Edit the config of a category.")
|
||||
print(clr["tdyl"]+"--tab_rows"+clr["norm"]+" - Set in how many rows to draw the tabs.")
|
||||
print()
|
||||
print(clr["tdyl"]+"--editor"+clr["norm"]+" - Set editor. Default nano.")
|
||||
print()
|
||||
print(clr["tdyl"]+"--fc_api"+clr["norm"]+" - API for software Free Competitors search.")
|
||||
print()
|
||||
|
||||
print()
|
||||
print(" If you are new here I guess start \n with "+clr["tdyl"]+"--title"+clr["norm"]+" to name your website.")
|
||||
print()
|
||||
|
||||
def Accounts():
|
||||
|
||||
print()
|
||||
print(clr["bold"]+" BDServer Account Help "+clr["norm"])
|
||||
print()
|
||||
print(" Account is used to manage website's accounts.")
|
||||
print()
|
||||
print(clr["tdyl"]+"--add"+clr["norm"]+" - Add a new account.")
|
||||
print(clr["tdyl"]+"--list"+clr["norm"]+" - List accounts.")
|
||||
print(clr["tdyl"]+"--edit"+clr["norm"]+" - Edit accounts.")
|
||||
print()
|
||||
|
||||
def Federation():
|
||||
|
||||
print()
|
||||
print(clr["bold"]+" BDServer Federation Help "+clr["norm"])
|
||||
print()
|
||||
print(" Federation is used for interoperability between websites.")
|
||||
print()
|
||||
print(clr["tdyl"]+"--add"+clr["norm"]+" - Add a new website to the list.")
|
||||
print(clr["tdyl"]+"--remove"+clr["norm"]+" - Remove a website from the list.")
|
||||
print(clr["tdyl"]+"--ban"+clr["norm"]+" - Ban a website from auto-federating.")
|
||||
print(clr["tdyl"]+"--unban"+clr["norm"]+" - Remove a ban from a website.")
|
||||
print(clr["tdyl"]+"--list"+clr["norm"]+" - List federation websites.")
|
||||
print()
|
||||
print(clr["tdyl"]+"--auto_on"+clr["norm"]+" - Turn on auto-federation.")
|
||||
print(clr["tdyl"]+"--auto_off"+clr["norm"]+"- Turn off auto-federation.")
|
||||
print()
|
||||
|
||||
def Analyse():
|
||||
|
||||
print()
|
||||
print(clr["bold"]+" BDServer Analyse Help "+clr["norm"])
|
||||
print()
|
||||
print(" Analyse traffic and other things related to the website.")
|
||||
print()
|
||||
print(clr["tdyl"]+"--logs"+clr["norm"]+" - Search a query in the logs.")
|
||||
print(clr["tdyl"]+"--totals"+clr["norm"]+" - View website's numbers.")
|
||||
print(clr["tdyl"]+"--rss"+clr["norm"]+" - Analyse Subscribers.")
|
||||
print(clr["tdyl"]+"--mastodon"+clr["norm"]+" - See who shares you on Mastodon.")
|
||||
print(clr["tdyl"]+"--words"+clr["norm"]+" - See popularity of certain words.")
|
||||
print(clr["tdyl"]+"--performance"+clr["norm"]+" - Compare recent article to one another.")
|
||||
print(clr["tdyl"]+"--timing"+clr["norm"]+" - See good times to post at ( hours, days, months, years )")
|
||||
print()
|
||||
print(clr["tdyl"]+"--ua"+clr["norm"]+" - See Operating Systems and Browsers")
|
||||
print( " --skip_me - to filter main account")
|
||||
print( " --human_only - to filter bots")
|
||||
print()
|
||||
print(clr["tdyl"]+"--sources"+clr["norm"]+" - See where people are coming from.")
|
||||
print()
|
||||
print(clr["tdyl"]+"--trends"+clr["norm"]+" - See what is trending.")
|
||||
print( " --tags - See hashtags intead.")
|
||||
print( " --verbose - spit out more data.")
|
||||
print()
|
||||
print()
|
||||
print(" A good start here would be "+clr["tdyl"]+"--totals"+clr["norm"]+" to \n get basic analytics.")
|
||||
print()
|
||||
@@ -0,0 +1,160 @@
|
||||
# AGPL 3 or any later version
|
||||
# (C) J.Y.Amihud ( Blender Dumbass )
|
||||
|
||||
import os
|
||||
import json
|
||||
|
||||
from modules import Set
|
||||
from modules.Common import *
|
||||
|
||||
categories = ["/articles", "/books", "/films", "/music", "/games", "/software", "/forum", "/reviews", "/malware", "/legals"]
|
||||
|
||||
def count(folder, text):
|
||||
walk = os.walk(folder)
|
||||
c = 0
|
||||
for i in walk:
|
||||
for b in i[2]:
|
||||
try:
|
||||
c = c + open(i[0]+"/"+b).read().count(text)
|
||||
except:
|
||||
pass
|
||||
return c
|
||||
|
||||
def Transfer(location):
|
||||
|
||||
# This function is pretty pointless. But it is useful for me. Because I
|
||||
# Was using another, badly written server for years. And now to migrate
|
||||
# to this server, I need something that will transfer my data over. If
|
||||
# by any miracle you happended to use my code to build your own website,
|
||||
# well, using this you too could do the same. LOL.
|
||||
|
||||
if not os.path.exists(location):
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Path does not exist!")
|
||||
return
|
||||
|
||||
##### ARTICLES #####
|
||||
|
||||
for tab in categories:
|
||||
|
||||
for article in os.listdir(location+tab):
|
||||
if article.endswith(".md"):
|
||||
|
||||
title = article[article.replace("/", " ", 1).find("/")+1:].replace("-", " ").replace("_", " ").replace(".md", "").replace("%28", "(").replace("%29", ")")
|
||||
timestamp = os.path.getctime(location+tab+"/"+article.replace("%28", "(").replace("%29", ")"))
|
||||
|
||||
text = open(location+tab+"/"+article)
|
||||
text = text.read()
|
||||
|
||||
# Description is a bit more complicated
|
||||
art = text
|
||||
while art.startswith("*"):
|
||||
art = art[art.find("\n"):]
|
||||
while art.startswith("\n") or art.startswith("_"):
|
||||
art = art[1:]
|
||||
description = art[:art.find("\n")]
|
||||
|
||||
# Legacy system only had non blenderdumbass authors
|
||||
# when it is a forum.
|
||||
|
||||
author = "blenderdumbass"
|
||||
if tab == "/forum":
|
||||
with open(location+"/accounts.json") as o:
|
||||
accounts = json.load(o)["accounts"]
|
||||
|
||||
for person in accounts:
|
||||
if tab+"/"+article in accounts[person].get("forums", []):
|
||||
author = person
|
||||
|
||||
with open(location+"/posters.json") as o:
|
||||
posters = json.load(o)
|
||||
|
||||
thumbnail = posters.get(tab+"/"+article, "")
|
||||
|
||||
# This will only give us the general number of views.
|
||||
views = count(location+"/logs", "/graph"+tab+"/"+article)
|
||||
|
||||
# But we also want to get number of views per day.
|
||||
dates = {}
|
||||
for date in os.listdir(location+"/logs"):
|
||||
if date.endswith(".log"):
|
||||
date = date[:-4]
|
||||
try: datetext = open(location+"/logs/"+date+".log", "r")
|
||||
except: continue
|
||||
|
||||
amount = datetext.read().count("/graph"+tab+"/"+article)
|
||||
if amount > 0:
|
||||
dates[date] = amount
|
||||
|
||||
|
||||
|
||||
# Comments
|
||||
|
||||
with open(location+"/comments.json") as o:
|
||||
commentdata = json.load(o)
|
||||
|
||||
comments = {
|
||||
"comments":commentdata["comments"].get(tab+"/"+article, []),
|
||||
"requests":commentdata["requests"].get(tab+"/"+article, [])
|
||||
}
|
||||
|
||||
|
||||
# Making the directory for the article
|
||||
|
||||
try: os.makedirs(Set.Folder()+"/tabs"+tab+"/"+article[:-3])
|
||||
except:pass
|
||||
|
||||
# Transferring the texts
|
||||
|
||||
textfile = Set.Folder()+"/tabs"+tab+"/"+article[:-3]+"/text.md"
|
||||
textfile = open(textfile, "w")
|
||||
textfile.write(text)
|
||||
textfile.close()
|
||||
|
||||
# Metadata
|
||||
|
||||
metadata = {
|
||||
"title":title,
|
||||
"timestamp":timestamp,
|
||||
"description":description,
|
||||
"author":author,
|
||||
"thumbnail":thumbnail,
|
||||
"views":{
|
||||
"amount":views,
|
||||
"viewers":[],
|
||||
"dates":dates
|
||||
},
|
||||
"recording":"",
|
||||
"comments":comments
|
||||
}
|
||||
|
||||
with open(Set.Folder()+"/tabs"+tab+"/"+article[:-3]+"/metadata.json", "w") as save:
|
||||
json.dump(metadata, save, indent=4)
|
||||
|
||||
#### ACCOUNTS ####
|
||||
|
||||
with open(location+"/accounts.json") as o:
|
||||
accounts = json.load(o)["accounts"]
|
||||
|
||||
for username in accounts:
|
||||
|
||||
account = accounts[username]
|
||||
|
||||
# We are updating the invites
|
||||
invite_codes = account.pop("invite_codes")
|
||||
account["invite_codes"] = {}
|
||||
|
||||
for code in invite_codes:
|
||||
account["invite_codes"][code] = "Unknown"
|
||||
|
||||
# We are adding some new stuff to accounts
|
||||
|
||||
account["title"] = username # Visible title
|
||||
account["email"] = "" # Contact info
|
||||
account["website"] = "" # Website
|
||||
account["mastodon"] = "" # Mastodon ( useful for fediverse tags )
|
||||
account["sessions"] = {} # List of cookies accosiated with this account
|
||||
|
||||
# We are saving the accounts in a different way too.
|
||||
|
||||
with open(Set.Folder()+"/accounts/"+username+".json", "w") as save:
|
||||
json.dump(account, save, indent=4)
|
||||
@@ -0,0 +1,113 @@
|
||||
# AGPL 3 or any later version
|
||||
# (C) J.Y.Amihud ( Blender Dumbass )
|
||||
|
||||
from modules.Common import *
|
||||
from modules import Set
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import hashlib
|
||||
import traceback
|
||||
|
||||
Plugins = {}
|
||||
|
||||
def Import():
|
||||
|
||||
# This functions imports the plugins.
|
||||
|
||||
pluginsF = Set.Folder()+"/plugins"
|
||||
sys.path.append(Set.Folder())
|
||||
|
||||
if not os.path.exists(pluginsF+"/__init__.py"):
|
||||
t = open(pluginsF+"/__init__.py", "w")
|
||||
t.close()
|
||||
|
||||
for plugin in os.listdir(pluginsF):
|
||||
if plugin.endswith(".py"):
|
||||
plugin = plugin.replace(".py", "")
|
||||
exec("from plugins import "+plugin)
|
||||
Plugins[plugin] = eval(plugin)
|
||||
|
||||
def onHTML(server, html):
|
||||
|
||||
for plugin in Plugins:
|
||||
if "onHTML" in dir(Plugins[plugin]):
|
||||
try:
|
||||
html = Plugins[plugin].onHTML(server, html)
|
||||
except:
|
||||
print(traceback.format_exc())
|
||||
|
||||
return html
|
||||
|
||||
def onGET(server):
|
||||
|
||||
for plugin in Plugins:
|
||||
if "onGET" in dir(Plugins[plugin]):
|
||||
try:
|
||||
if Plugins[plugin].onGET(server):
|
||||
return True
|
||||
except:
|
||||
print(traceback.format_exc())
|
||||
return False
|
||||
|
||||
def onFEDI():
|
||||
|
||||
for plugin in Plugins:
|
||||
if "onFEDI" in dir(Plugins[plugin]):
|
||||
try:
|
||||
Plugins[plugin].onFEDI()
|
||||
except:
|
||||
print(traceback.format_exc())
|
||||
|
||||
def GetData():
|
||||
|
||||
config = Set.Load()
|
||||
pluginsF = Set.Folder()+"/plugins"
|
||||
data = {}
|
||||
|
||||
Pdata = {}
|
||||
|
||||
try:
|
||||
with open(Set.Folder()+"/plugins.json") as o:
|
||||
Pdata = json.load(o)
|
||||
except:
|
||||
pass
|
||||
|
||||
for plugin in os.listdir(pluginsF):
|
||||
if plugin.endswith(".py") and plugin != "__init__.py":
|
||||
|
||||
text = open(pluginsF+"/"+plugin, "r")
|
||||
text = text.read()
|
||||
|
||||
sha512 = hashlib.sha512(text.encode("utf-8")).hexdigest()
|
||||
sha256 = hashlib.sha256(text.encode("utf-8")).hexdigest()
|
||||
md5 = hashlib.md5(text.encode("utf-8")).hexdigest()
|
||||
|
||||
try:
|
||||
PluginData = dict(Plugins[plugin.replace(".py","")].PluginData)
|
||||
except:
|
||||
PluginData = {}
|
||||
|
||||
pluginData = {"sha512":sha512,
|
||||
"sha256":sha256,
|
||||
"md5" :md5}
|
||||
|
||||
for i in PluginData:
|
||||
pluginData[i] = PluginData[i]
|
||||
|
||||
pluginData["domains"] = [config.get("domain","localhost")]
|
||||
|
||||
for domain in Pdata:
|
||||
|
||||
if domain == config.get("domain"):
|
||||
continue
|
||||
|
||||
for pluginname in Pdata[domain]:
|
||||
pluginD = Pdata[domain][pluginname]
|
||||
#if pluginD.get("sha512") == sha512:
|
||||
# pluginData["domains"].append(domain)
|
||||
|
||||
|
||||
data[plugin] = pluginData
|
||||
return data
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,549 @@
|
||||
# AGPL 3 or any later version
|
||||
# (C) J.Y.Amihud ( Blender Dumbass )
|
||||
|
||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
from socketserver import ThreadingMixIn
|
||||
from subprocess import *
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
import email
|
||||
import random
|
||||
import threading
|
||||
import datetime
|
||||
import hashlib
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
|
||||
from modules import Set
|
||||
from modules import API
|
||||
from modules import Render
|
||||
from modules import Federation
|
||||
from modules import ActivityPub
|
||||
from modules import Email
|
||||
from modules import Themes
|
||||
from modules import Plugins
|
||||
from modules.Common import *
|
||||
|
||||
print()
|
||||
|
||||
# I don't know what I'm doing. But here is an idea
|
||||
|
||||
antiDDos = {}
|
||||
settings = Set.Load()
|
||||
def DDosProtect(server):
|
||||
|
||||
if "/graph/" in server.path:
|
||||
return True
|
||||
|
||||
request = str(server.path) + str(server.headers.get("User-Agent", "None"))
|
||||
|
||||
l = settings.get("banned_cookies", [])
|
||||
for i in l:
|
||||
if i in str(server.cookie):
|
||||
return False
|
||||
|
||||
# if server.cookie and server.cookie not in Render.KnownCookies:
|
||||
# server.path = "DDOS"
|
||||
# server.send_response(403)
|
||||
# return False
|
||||
|
||||
if GuessHuman(server):
|
||||
return True
|
||||
if request not in antiDDos:
|
||||
antiDDos[request] = {"last":time.time()}
|
||||
return True
|
||||
if antiDDos[request]["last"] + 10 < time.time():
|
||||
antiDDos[request]["last"] = time.time()
|
||||
return True
|
||||
|
||||
server.path = "DDOS"
|
||||
server.send_response(403)
|
||||
return False
|
||||
|
||||
class handler(BaseHTTPRequestHandler):
|
||||
|
||||
def log_message(self, format, *args):
|
||||
|
||||
if "?" in self.path:
|
||||
self.path = self.path[:self.path.find("?")]
|
||||
|
||||
|
||||
if self.path == "DDOS":
|
||||
return
|
||||
|
||||
q = clr["tbbl"]
|
||||
b = clr["tbbl"]
|
||||
|
||||
via = self.headers.get("Via", "")
|
||||
|
||||
try:
|
||||
if self.newview:
|
||||
b = clr["bold"]+clr["tbrd"]
|
||||
elif self.isPage:
|
||||
b = clr["norm"]
|
||||
|
||||
if self.requestline.startswith("GET"):
|
||||
for i in self.parsed:
|
||||
if i != "password":
|
||||
q = q + "[ "+i+" : "+self.parsed[i][0]+" ] "
|
||||
else:
|
||||
q = q + "[ "+i+" : ******* ] "
|
||||
|
||||
cookie = self.cookie
|
||||
|
||||
toprint = typeOfConnection(self) + consoleForm(cookie)+" "+b+"[ "+str(datetime.datetime.now())+" ] "+self.path+" "+q+" "+clr["tdyl"]+StripUserAgent(str(self.headers.get("User-Agent")), via)+clr["norm"]
|
||||
|
||||
except:
|
||||
|
||||
cookie = None
|
||||
|
||||
toprint = typeOfConnection(self) + consoleForm(cookie)+" "+b+"[ "+str(datetime.datetime.now())+" ] "+self.requestline+" "+clr["tdyl"]+StripUserAgent(str(self.headers.get("User-Agent")), via)+clr["norm"]
|
||||
|
||||
toprint = toprint + " TRDS:" + str(threading.active_count())
|
||||
|
||||
print(toprint)
|
||||
|
||||
folder = Set.Folder()
|
||||
filename = folder+"/logs/"+datetime.datetime.now().strftime('%Y-%m-%d')+".log"
|
||||
with ThreadLock(filename):
|
||||
logfile = open(filename, "ab")
|
||||
logfile.write((toprint+"\n").encode('utf-8'))
|
||||
logfile.close()
|
||||
|
||||
def do_POST(self):
|
||||
|
||||
self.path = self.path.replace("/..", "/")
|
||||
self.path = self.path.replace("%27", "'")
|
||||
|
||||
self.cookie = self.headers.get("Cookie")
|
||||
self.newview = False
|
||||
|
||||
if self.cookie: self.cookie = self.cookie[-200:]
|
||||
if not DDosProtect(self):
|
||||
return
|
||||
|
||||
|
||||
self.isPage = False
|
||||
|
||||
# Failing early to make sure that nobody will
|
||||
# try attacking this part of the server.
|
||||
|
||||
commands = ["do_edit",
|
||||
"do_theme_edit",
|
||||
"do_login",
|
||||
"do_register",
|
||||
"change_password",
|
||||
"upload_image_do",
|
||||
#"upload_css_global",
|
||||
#"upload_css_posts",
|
||||
"activitypub"]
|
||||
|
||||
found = False
|
||||
for i in commands:
|
||||
if i in self.path:
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
Render.AccessDenied(self)
|
||||
return
|
||||
|
||||
|
||||
# Not allowing more than 1 MB for the sake of
|
||||
# network. A full book of Sheiny The Hacker
|
||||
# which was posted on blenderdumbass.org is
|
||||
# only about 160 KB. 1MB should be plenty.
|
||||
|
||||
length = int(self.headers["Content-Length"])
|
||||
readdata = False
|
||||
if length < 3000000:
|
||||
try:
|
||||
|
||||
binaries = ("upload_image_do", "upload_css_global", "upload_css_posts")
|
||||
|
||||
if not self.path[1:].startswith(binaries):
|
||||
text = self.rfile.read(length).decode("utf-8")
|
||||
readdata = True
|
||||
else:
|
||||
text = self.rfile.read(length)
|
||||
readdata = True
|
||||
except:
|
||||
Render.AccessDenied(self)
|
||||
return
|
||||
|
||||
else:
|
||||
Render.AccessDenied(self)
|
||||
return
|
||||
|
||||
|
||||
if type(text) == str:
|
||||
try:
|
||||
self.parsed = json.loads(text)
|
||||
except:
|
||||
self.parsed = urllib.parse.parse_qs(text)
|
||||
else:
|
||||
try:
|
||||
content_type = self.headers['content-type']
|
||||
boundary = content_type.split("=")[1].encode()
|
||||
self.parsed = text.split(boundary)
|
||||
except:
|
||||
self.parsed = {}
|
||||
AccessDenied(self)
|
||||
|
||||
if self.path[1:].startswith("do_edit"):
|
||||
Render.Publish(self)
|
||||
|
||||
elif self.path[1:].startswith("do_theme_edit"):
|
||||
Themes.DoThemeEdit(self)
|
||||
|
||||
elif self.path[1:].startswith("do_login"):
|
||||
Render.Login(self)
|
||||
|
||||
elif self.path[1:].startswith("change_password"):
|
||||
Render.UpdatePassword(self)
|
||||
|
||||
elif self.path[1:].startswith("do_register"):
|
||||
Render.Register(self)
|
||||
|
||||
elif self.path[1:].startswith("upload_image_do"):
|
||||
Render.ImageUploadDo(self)
|
||||
|
||||
elif self.path[1:].startswith("upload_css_global"):
|
||||
Render.CSS_Apply(self, "global")
|
||||
|
||||
elif self.path[1:].startswith("upload_css_posts"):
|
||||
Render.CSS_Apply(self, "posts")
|
||||
|
||||
elif self.path.startswith("/activitypub/"):
|
||||
url = self.path[12:]
|
||||
if "?" in url: url = url[:url.find("?")]
|
||||
ActivityPub.Parse(self, url, text=text)
|
||||
|
||||
else:
|
||||
Render.NotFound(self)
|
||||
|
||||
def do_GET(self):
|
||||
|
||||
self.path = self.path.replace("/..", "/")
|
||||
self.path = self.path.replace("%27", "'")
|
||||
self.path = self.path.replace("%22", '"')
|
||||
self.path = self.path.replace("//", '/')
|
||||
|
||||
parsed_url = urllib.parse.urlparse(self.path)
|
||||
self.parsed = urllib.parse.parse_qs(parsed_url.query)
|
||||
|
||||
#print(self.headers)
|
||||
|
||||
self.cookie = self.headers.get("Cookie")
|
||||
if self.cookie: self.cookie = self.cookie[-200:]
|
||||
if not DDosProtect(self):
|
||||
return
|
||||
|
||||
self.isPage = False
|
||||
|
||||
|
||||
|
||||
# ignore = ["/rss"]
|
||||
|
||||
# if not self.cookie and not self.path.startswith(tuple(ignore)):
|
||||
# Render.Redirect(self, self.path)
|
||||
# return
|
||||
|
||||
self.newview = False
|
||||
|
||||
categories = list(Render.tabs().keys())
|
||||
|
||||
if self.path[:self.path.find("?")] == "/" or self.path == "/":
|
||||
self.isPage = True
|
||||
Render.MainPage(self)
|
||||
|
||||
|
||||
elif self.path[1:] in categories or self.path[1:self.path.find("?")] in categories:
|
||||
url = self.path[1:]
|
||||
if "?" in url: url = url[:url.find("?")]
|
||||
self.isPage = True
|
||||
Render.ListPage(self, url)
|
||||
|
||||
elif self.path[1:].startswith("json"):
|
||||
url = self.path[6:]
|
||||
if "?" in url: url = url[:url.find("?")]
|
||||
API.GiveJSON(url, self)
|
||||
|
||||
elif self.path[1:].startswith("markdown"):
|
||||
url = self.path[10:]
|
||||
if "?" in url: url = url[:url.find("?")]
|
||||
if url.startswith(tuple(categories)):
|
||||
try:
|
||||
Render.ArticleMarkdown(self, url)
|
||||
except:
|
||||
Render.NotFound(self)
|
||||
else:
|
||||
Render.NotFound(self)
|
||||
|
||||
elif self.path[1:].startswith(tuple(categories)):
|
||||
url = self.path[1:]
|
||||
if "?" in url: url = url[:url.find("?")]
|
||||
self.isPage = True
|
||||
try:
|
||||
Render.ArticlePage(self, url)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
Render.NotFound(self)
|
||||
|
||||
elif self.path[1:].startswith("account"):
|
||||
self.isPage = True
|
||||
url = self.path[9:]
|
||||
if "?" in url: url = url[:url.find("?")]
|
||||
try:
|
||||
Render.AccountPage(self, url)
|
||||
except:
|
||||
Render.NotFound(self)
|
||||
|
||||
elif self.path[1:].startswith("login"):
|
||||
self.isPage = True
|
||||
Render.LoginPage(self)
|
||||
|
||||
elif self.path[1:].startswith("help"):
|
||||
self.isPage = True
|
||||
Render.Help(self)
|
||||
|
||||
elif self.path[1:].startswith("editor"):
|
||||
self.isPage = True
|
||||
Render.EditorPage(self)
|
||||
|
||||
elif self.path[1:].startswith("theme_editor"):
|
||||
self.isPage = True
|
||||
Themes.Editor(self)
|
||||
|
||||
elif self.path[1:].startswith("contact"):
|
||||
self.isPage = True
|
||||
Render.ContactPage(self)
|
||||
|
||||
elif self.path[1:].startswith("upload_image"):
|
||||
self.isPage = True
|
||||
Render.ImageUploadPage(self)
|
||||
|
||||
elif self.path[1:].startswith("email_verify"):
|
||||
self.isPage = True
|
||||
Email.VerifyPage(self)
|
||||
|
||||
elif self.path[1:].startswith("register"):
|
||||
self.isPage = True
|
||||
Render.RegisterPage(self)
|
||||
|
||||
elif self.path[1:].startswith("settings"):
|
||||
self.isPage = True
|
||||
Render.SettingsPage(self)
|
||||
|
||||
elif self.path[1:].startswith("comment"):
|
||||
Render.DoComment(self)
|
||||
|
||||
elif self.path[1:].startswith("delete_comment"):
|
||||
Render.DeleteComment(self)
|
||||
|
||||
elif self.path[1:].startswith("update_account"):
|
||||
Render.UpdateAccount(self)
|
||||
|
||||
elif self.path[1:].startswith("mastodon_share"):
|
||||
self.isPage = True
|
||||
Render.ShareMastodon(self)
|
||||
|
||||
elif self.path[1:].startswith("mastodon_comment"):
|
||||
self.isPage = True
|
||||
Render.CommentMastodon(self)
|
||||
|
||||
elif self.path[1:].startswith("analytics"):
|
||||
self.isPage = True
|
||||
Render.AnalyticsPage(self)
|
||||
|
||||
elif self.path[1:].startswith("grant_publication_rights"):
|
||||
Render.UpdatePublicationRights(self)
|
||||
|
||||
elif self.path[1:].startswith("create_invite"):
|
||||
Render.CreateInvite(self)
|
||||
|
||||
elif self.path[1:].startswith("cancel_invite"):
|
||||
Render.CancelInvite(self)
|
||||
|
||||
elif self.path[1:].startswith("log_out"):
|
||||
Render.LogOut(self)
|
||||
|
||||
elif self.path[1:].startswith("sign_petition"):
|
||||
self.isPage = True
|
||||
Email.SignPetition(self)
|
||||
|
||||
elif self.path[1:].startswith("read_notification"):
|
||||
Render.ReadNotification(self)
|
||||
|
||||
elif self.path[1:].startswith("search"):
|
||||
self.isPage = True
|
||||
Render.Search(self)
|
||||
|
||||
elif self.path == "/test":
|
||||
self.send_response(200)
|
||||
self.wfile.write("Success!".encode("utf-8"))
|
||||
|
||||
|
||||
elif self.path.startswith("/graph/"):
|
||||
url = self.path[6:]
|
||||
if "?" in url: url = url[:url.find("?")]
|
||||
Render.Graph(self, url)
|
||||
|
||||
elif self.path.startswith("/do_install_plugin/"):
|
||||
sha = self.path[19:]
|
||||
if "?" in sha: sha = sha[:sha.find("?")]
|
||||
Render.DoInstallPlugin(self, sha)
|
||||
|
||||
elif self.path.startswith("/do_delete_plugin/"):
|
||||
sha = self.path[18:]
|
||||
if "?" in sha: sha = sha[:sha.find("?")]
|
||||
Render.DoDeletePlugin(self, sha)
|
||||
|
||||
elif self.path.startswith("/install_plugin/"):
|
||||
sha = self.path[16:]
|
||||
if "?" in sha: sha = sha[:sha.find("?")]
|
||||
Render.InstallPluginPage(self, sha)
|
||||
|
||||
elif self.path.startswith("/rss"):
|
||||
Render.RSS(self)
|
||||
|
||||
elif self.path.startswith("/.well-known/webfinger"):
|
||||
ActivityPub.Webfinger(self)
|
||||
|
||||
elif self.path.startswith("/activitypub/"):
|
||||
url = self.path[12:]
|
||||
if "?" in url: url = url[:url.find("?")]
|
||||
ActivityPub.Parse(self, url)
|
||||
|
||||
elif self.path.startswith("/plugins/"):
|
||||
|
||||
try:
|
||||
folder = Set.Folder()
|
||||
f = open(folder+self.path, "rb")
|
||||
f = f.read()
|
||||
Render.headers(self, 200)
|
||||
self.wfile.write(f)
|
||||
except:
|
||||
Render.NotFound(self)
|
||||
|
||||
elif self.path.startswith("/plugins"):
|
||||
Render.PluginListPage(self)
|
||||
|
||||
elif self.path.startswith("/pictures/"):
|
||||
|
||||
try:
|
||||
folder = Set.Folder()
|
||||
f = open(folder+self.path, "rb")
|
||||
f = f.read()
|
||||
Render.headers(self, 200)
|
||||
self.wfile.write(f)
|
||||
except:
|
||||
Render.NotFound(self)
|
||||
|
||||
elif self.path.startswith("/themes/"):
|
||||
ID = self.path[8:]
|
||||
Themes.ViewPage(self, ID)
|
||||
|
||||
elif self.path.startswith("/themes"):
|
||||
Themes.ListPage(self)
|
||||
|
||||
elif self.path == "/css":
|
||||
|
||||
config = Set.Load()
|
||||
filename = config.get("css", "default.css")
|
||||
cssfile = open(filename, "rb")
|
||||
cssfile = cssfile.read()
|
||||
Render.headers(self, 200)
|
||||
self.wfile.write(cssfile)
|
||||
|
||||
elif self.path == "/css_global":
|
||||
|
||||
Render.CSS_Get(self, "global")
|
||||
|
||||
elif self.path == "/css_posts":
|
||||
|
||||
Render.CSS_Get(self, "posts")
|
||||
|
||||
elif self.path.startswith("/icon/"):
|
||||
|
||||
Folder = Set.Folder()
|
||||
|
||||
if self.path[6:]+".png" in os.listdir(Folder+"/icons"):
|
||||
folder = Folder+"/icons"
|
||||
else:
|
||||
folder = "icons"
|
||||
icon = folder+"/"+self.path[6:]+".png"
|
||||
f = open(icon, "rb")
|
||||
f = f.read()
|
||||
Render.headers(self, 200)
|
||||
self.wfile.write(f)
|
||||
|
||||
elif Plugins.onGET(self):
|
||||
# If plugins return True, there is no 404,
|
||||
# some page is rendered.
|
||||
pass
|
||||
|
||||
else:
|
||||
Render.NotFound(self)
|
||||
|
||||
config = Set.Load()
|
||||
PORT = config.get("port", 8080)
|
||||
class ThreadingSimpleServer(ThreadingMixIn, HTTPServer):
|
||||
pass
|
||||
serve = ThreadingSimpleServer(("", PORT), handler)
|
||||
#serve = HTTPServer(("", PORT), handler)
|
||||
serve.timeout = 5
|
||||
|
||||
|
||||
# Collecting Garbage from the last
|
||||
# use of the server.
|
||||
Render.CleanData()
|
||||
|
||||
|
||||
def Test():
|
||||
|
||||
while True:
|
||||
|
||||
time.sleep(100)
|
||||
|
||||
# Check if the server is running.
|
||||
|
||||
settings = Set.Load()
|
||||
port = settings.get("port", 8080)
|
||||
try:
|
||||
headers = {"User-Agent":"BDServer from Anti-Slowloris Thread"}
|
||||
req = urllib.request.Request("http://localhost:"+str(port)+"/test", headers = headers)
|
||||
f = urllib.request.urlopen(req, timeout=2)
|
||||
|
||||
except Exception as e:
|
||||
|
||||
if "Success!" != str(e):
|
||||
print(" "+consoleForm("Anti-Slowloris"), clr["bold"]+clr["tdrd"]+"Failed to connect to /test!"+clr["norm"], clr["tdyl"],e, clr["norm"], "Restarting server!" )
|
||||
|
||||
os.execl(sys.executable, *sys.orig_argv)
|
||||
|
||||
try:
|
||||
|
||||
print("Importing Plugins...")
|
||||
Plugins.Import()
|
||||
|
||||
|
||||
print("Starting Federation Thread...")
|
||||
t = threading.Thread(target=Federation.FederationThread, daemon=True)
|
||||
t.start()
|
||||
|
||||
print("Starting Anti-Slowloris Thread...")
|
||||
t2 = threading.Thread(target=Test, daemon=True)
|
||||
t2.start()
|
||||
|
||||
print("Starting Server...")
|
||||
print()
|
||||
print("You are serving on http://localhost:"+str(PORT))
|
||||
print()
|
||||
|
||||
serve.serve_forever()
|
||||
except KeyboardInterrupt:
|
||||
print("\nServer Stopped!")
|
||||
@@ -0,0 +1,413 @@
|
||||
# AGPL 3 or any later version
|
||||
# (C) J.Y.Amihud ( Blender Dumbass )
|
||||
|
||||
from modules.Common import *
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
|
||||
def Folder():
|
||||
|
||||
conf = os.environ.get("PROJECT", "BDServer")
|
||||
|
||||
try:
|
||||
data_dir = os.environ["XDG_DATA_HOME"] + "/" + conf
|
||||
except:
|
||||
data_dir = os.path.expanduser("~/.local/share/"+ conf)
|
||||
|
||||
try:
|
||||
os.makedirs(data_dir)
|
||||
except:
|
||||
pass
|
||||
|
||||
# Adding nessesary subcategories
|
||||
for i in ["pictures", "tabs", "logs", "accounts", "plugins", "deleted_plugins",
|
||||
"icons", "themes",
|
||||
"activitypub/actors",
|
||||
"activitypub/webfingers"]:
|
||||
try:
|
||||
os.makedirs(data_dir+"/"+i)
|
||||
except:
|
||||
pass
|
||||
|
||||
return data_dir
|
||||
|
||||
def Load():
|
||||
|
||||
try:
|
||||
filename = Folder()+"/config.json"
|
||||
with ThreadLock(filename):
|
||||
with open(Folder()+"/config.json") as o:
|
||||
data = json.load(o)
|
||||
except Exception as e:
|
||||
data = {}
|
||||
print(clr["bold"]+clr["tdyl"]+"Error:"+clr["norm"]+" Config doesn't exist! Making new config.")
|
||||
|
||||
return data
|
||||
|
||||
def Save(data):
|
||||
|
||||
try:
|
||||
filename = Folder()+"/config.json"
|
||||
with ThreadLock(filename):
|
||||
with open(filename, "w") as save:
|
||||
json.dump(data, save, indent=4)
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Cannot save config!", e)
|
||||
|
||||
def Set():
|
||||
|
||||
anything = False
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
from modules import Help
|
||||
Help.Set()
|
||||
anything = True
|
||||
|
||||
if "--title" in sys.argv:
|
||||
anything = True
|
||||
try:
|
||||
title = sys.argv[ sys.argv.index("--title") + 1]
|
||||
if "--" in title: 1/0 # Failing this for the error message.
|
||||
Title(title)
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Title Wasn't Specified!")
|
||||
print('Use: $ python3 run.py --set --title "My Website"')
|
||||
|
||||
if "--account" in sys.argv:
|
||||
anything = True
|
||||
try:
|
||||
account = sys.argv[ sys.argv.index("--account") + 1]
|
||||
if "--" in account: 1/0 # Failing this for the error message.
|
||||
MainAccount(account)
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Account Wasn't Specified!")
|
||||
print('Use: $ python3 run.py --set --account blenderdumbass')
|
||||
|
||||
if "--tagline" in sys.argv:
|
||||
anything = True
|
||||
try:
|
||||
tagline = sys.argv[ sys.argv.index("--tagline") + 1]
|
||||
if "--" in tagline: 1/0 # Failing this for the error message.
|
||||
Tagline(tagline)
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Tagline Wasn't Specified!")
|
||||
print('Use: $ python3 run.py --set --title "Ultimate Club for Ultimate Hackers"')
|
||||
|
||||
if "--domain" in sys.argv:
|
||||
anything = True
|
||||
try:
|
||||
domain = sys.argv[ sys.argv.index("--domain") + 1]
|
||||
if "--" in domain: 1/0 # Failing this for the error message.
|
||||
Domain(domain)
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Didn't specify the domain.")
|
||||
print('Use: $ python3 run.py --set --domain blenderdumbass.org')
|
||||
|
||||
if "--tor" in sys.argv:
|
||||
anything = True
|
||||
try:
|
||||
domain = sys.argv[ sys.argv.index("--tor") + 1]
|
||||
if "--" in domain: 1/0 # Failing this for the error message.
|
||||
Tor(domain)
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Didn't specify the tor domain.")
|
||||
print('Use: $ python3 run.py --set --domain blenderdumbass.org')
|
||||
|
||||
if "--editor" in sys.argv:
|
||||
anything = True
|
||||
try:
|
||||
editor = sys.argv[ sys.argv.index("--editor") + 1]
|
||||
if "--" in editor: 1/0 # Failing this for the error message.
|
||||
Editor(editor)
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Didn't specify the editor!")
|
||||
print('Use: $ python3 run.py --set --editor emacs')
|
||||
|
||||
if "--email" in sys.argv:
|
||||
anything = True
|
||||
|
||||
Email()
|
||||
|
||||
|
||||
if "--port" in sys.argv:
|
||||
anything = True
|
||||
try:
|
||||
port = int(sys.argv[ sys.argv.index("--port") + 1])
|
||||
Port(port)
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Port Wasn't Specified Correctly!")
|
||||
print('Use: $ python3 run.py --set --port 8080')
|
||||
|
||||
if "--add_tab" in sys.argv:
|
||||
anything = True
|
||||
try:
|
||||
tab = sys.argv[ sys.argv.index("--add_tab") + 1]
|
||||
if "--" in tab: 1/0 # Failing this for the error message.
|
||||
AddTab(tab)
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Tab Name Wasn't Specified!")
|
||||
print('Use: $ python3 run.py --set --add_tab "Articles"')
|
||||
|
||||
if "--edit_tab" in sys.argv:
|
||||
anything = True
|
||||
try:
|
||||
tab = sys.argv[ sys.argv.index("--edit_tab") + 1]
|
||||
if "--" in tab: 1/0 # Failing this for the error message.
|
||||
EditTab(tab)
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Tab Name Wasn't Specified!")
|
||||
print('Use: $ python3 run.py --set --edit_tab "Articles"')
|
||||
|
||||
if "--tab_rows" in sys.argv:
|
||||
anything = True
|
||||
try:
|
||||
tab_rows = int(sys.argv[ sys.argv.index("--tab_rows") + 1])
|
||||
TabRows(tab_rows)
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Number Wasn't Specified Correctly!")
|
||||
print('Use: $ python3 run.py --set --tab_rows 2')
|
||||
|
||||
if "--css" in sys.argv:
|
||||
anything = True
|
||||
try:
|
||||
filename = sys.argv[ sys.argv.index("--css") + 1]
|
||||
if "--" in filename: 1/0 # Failing this for the error message.
|
||||
SetCSS(filename)
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" CSS File Wasn't Specified!", e)
|
||||
print('Use: $ python3 run.py --set --css ~/mycssfile.css')
|
||||
|
||||
if "--favicon" in sys.argv:
|
||||
anything = True
|
||||
try:
|
||||
filename = sys.argv[ sys.argv.index("--favicon") + 1]
|
||||
if "--" in filename: 1/0 # Failing this for the error message.
|
||||
SetFavicon(filename)
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" File Wasn't Specified!", e)
|
||||
print('Use: $ python3 run.py --set --css ~/favicon.png')
|
||||
|
||||
if "--css_edit" in sys.argv:
|
||||
anything = True
|
||||
config = Load()
|
||||
os.system("nano "+config.get("css", "default.css"))
|
||||
|
||||
|
||||
if "--fc_api" in sys.argv:
|
||||
anything = True
|
||||
try:
|
||||
api = sys.argv[ sys.argv.index("--fc_api") + 1]
|
||||
if "--" in api: 1/0 # Failing this for the error message.
|
||||
FreeCompetitors(api)
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" API Link Wasn't Specified!")
|
||||
print('Use: $ python3 run.py --set --fc_api https://sudo.madiator.com')
|
||||
|
||||
if not anything:
|
||||
print(clr["bold"]+clr["tdrd"]+"Unknown command!"+clr["norm"])
|
||||
|
||||
|
||||
def Title(title):
|
||||
|
||||
data = Load()
|
||||
data["title"] = title
|
||||
|
||||
Save(data)
|
||||
|
||||
print(clr["bold"]+clr["tbyl"]+title+clr["norm"]+" is set as title.")
|
||||
|
||||
def Tagline(tagline):
|
||||
|
||||
data = Load()
|
||||
data["tagline"] = tagline
|
||||
|
||||
Save(data)
|
||||
|
||||
print(clr["bold"]+clr["tbyl"]+tagline+clr["norm"]+" is set as tagline.")
|
||||
|
||||
def Domain(domain):
|
||||
|
||||
data = Load()
|
||||
data["domain"] = domain
|
||||
|
||||
Save(data)
|
||||
|
||||
print(clr["bold"]+clr["tbyl"]+domain+clr["norm"]+" is set as domain.")
|
||||
|
||||
def Tor(domain):
|
||||
|
||||
data = Load()
|
||||
data["tor"] = domain
|
||||
|
||||
Save(data)
|
||||
|
||||
print(clr["bold"]+clr["tbyl"]+domain+clr["norm"]+" is set as tor domain.")
|
||||
|
||||
def Editor(editor):
|
||||
|
||||
data = Load()
|
||||
data["editor"] = editor
|
||||
|
||||
Save(data)
|
||||
|
||||
print(clr["bold"]+clr["tbyl"]+editor+clr["norm"]+" is set as the editor.")
|
||||
|
||||
|
||||
def Port(port):
|
||||
|
||||
data = Load()
|
||||
data["port"] = port
|
||||
|
||||
Save(data)
|
||||
|
||||
print(clr["bold"]+clr["tbyl"]+str(port)+clr["norm"]+" is set as the website's port.")
|
||||
|
||||
def AddTab(tab):
|
||||
|
||||
# Tab's Folder
|
||||
|
||||
tabsFolder = Folder()+"/tabs/"+Simplify(tab)
|
||||
try: os.makedirs(tabsFolder)
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Could not add tab!", e)
|
||||
return
|
||||
|
||||
# Tab's Config File
|
||||
data = {"title":tab,
|
||||
"icon":"folder"}
|
||||
try:
|
||||
with open(tabsFolder+"/config.json", "w") as save:
|
||||
json.dump(data, save, indent=4, sort_keys=True)
|
||||
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Cannot save tab's config!", e)
|
||||
return
|
||||
|
||||
print(clr["bold"]+clr["tbyl"]+tab+clr["norm"]+" tab is added as "+clr["bold"]+"/"+Simplify(tab)+clr["norm"])
|
||||
|
||||
def EditTab(tab):
|
||||
|
||||
tabsFolder = Folder()+"/tabs/"+Simplify(tab)
|
||||
os.system(Load().get("editor", "nano")+" "+tabsFolder+"/config.json")
|
||||
|
||||
def SetCSS(filename):
|
||||
|
||||
try:
|
||||
f = open(filename, "r")
|
||||
f = f.read()
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Cannot Read CSS file!", e)
|
||||
return
|
||||
|
||||
css = open(Folder()+"/style.css", "w")
|
||||
css.write(f)
|
||||
css.close()
|
||||
|
||||
config = Load()
|
||||
config["css"] = Folder()+"/style.css"
|
||||
Save(config)
|
||||
|
||||
print("New CSS is set at "+clr["bold"]+Folder()+"/style.css"+clr["norm"])
|
||||
|
||||
def SetFavicon(filename):
|
||||
|
||||
try:
|
||||
f = open(filename, "rb")
|
||||
f = f.read()
|
||||
except Exception as e:
|
||||
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Cannot Read Image!", e)
|
||||
return
|
||||
|
||||
css = open(Folder()+"/pictures/favicon.png", "wb")
|
||||
css.write(f)
|
||||
css.close()
|
||||
|
||||
config = Load()
|
||||
config["favicon"] = "/pictures/favicon.png"
|
||||
Save(config)
|
||||
|
||||
print("New favicon is set at "+clr["bold"]+Folder()+"/pictures/favicon.png"+clr["norm"])
|
||||
|
||||
def Email():
|
||||
|
||||
print("Please make sure your email provider supports")
|
||||
print("SMTP protocol.")
|
||||
|
||||
server = input("SMTP server : ")
|
||||
port = input("SMTP port : ")
|
||||
username = input("Username : ")
|
||||
password = input("Password : ")
|
||||
sender = input("Sender Address : ")
|
||||
visible = input("Visible Address: ")
|
||||
|
||||
config = Load()
|
||||
|
||||
email = {
|
||||
"server":server,
|
||||
"port":port,
|
||||
"username":username,
|
||||
"password":password,
|
||||
"sender":sender,
|
||||
"visible":visible
|
||||
}
|
||||
|
||||
config["email"] = email
|
||||
|
||||
Save(config)
|
||||
|
||||
print("Email credentials saved!")
|
||||
|
||||
def TabRows(tab_rows):
|
||||
|
||||
data = Load()
|
||||
data["tab_rows"] = tab_rows
|
||||
|
||||
Save(data)
|
||||
|
||||
print(clr["bold"]+clr["tbyl"]+str(tab_rows)+clr["norm"]+" is set as amount of rows to render tabs.")
|
||||
|
||||
def MainAccount(account):
|
||||
|
||||
data = Load()
|
||||
data["main_account"] = account
|
||||
|
||||
Save(data)
|
||||
|
||||
print(clr["bold"]+clr["tbyl"]+account+clr["norm"]+" is set as main account.")
|
||||
|
||||
def FreeCompetitors(api):
|
||||
|
||||
data = Load()
|
||||
data["free_competitors"] = api
|
||||
|
||||
Save(data)
|
||||
|
||||
print(clr["bold"]+clr["tbyl"]+api+clr["norm"]+" is set as Free Competitors API.")
|
||||
|
||||
def AddBannedCookie(cookie):
|
||||
|
||||
data = Load()
|
||||
if "banned_cookies" not in data:
|
||||
data["banned_cookies"] = []
|
||||
data["banned_cookies"].append(cookie)
|
||||
|
||||
Save(data)
|
||||
|
||||
print(clr["bold"]+clr["tbyl"]+cookie+clr["norm"]+" is banned.")
|
||||
print(clr["bold"]+clr["tdrd"]+"To take effect, restart the server."+clr["norm"])
|
||||
@@ -0,0 +1,325 @@
|
||||
# AGPL 3 or any later version
|
||||
# (C) J.Y.Amihud ( Blender Dumbass )
|
||||
|
||||
import os
|
||||
import json
|
||||
|
||||
from modules import Set
|
||||
from modules import Render
|
||||
from modules import markdown
|
||||
from modules.Common import *
|
||||
|
||||
def themes():
|
||||
|
||||
# Returns the metadata of all themes.
|
||||
|
||||
folder = Set.Folder()+"/themes/"
|
||||
|
||||
themes = {}
|
||||
|
||||
for theme in list(os.walk(folder))[0][1]:
|
||||
try:
|
||||
with open(folder+theme+"/theme.json") as o:
|
||||
data = json.load(o)
|
||||
data["id"] = theme
|
||||
themes[theme] = data
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
return themes
|
||||
|
||||
def load(ID):
|
||||
|
||||
# Load the raw CSS of a specific ID.
|
||||
|
||||
folder = Set.Folder()+"/themes/"+ID
|
||||
CSS = ""
|
||||
CSS = open(folder+"/theme.css", "r").read()
|
||||
|
||||
return CSS
|
||||
|
||||
def get_theme(server, config, user, author={}):
|
||||
|
||||
# Load a theme for a specific user
|
||||
|
||||
# Getting the server defaults
|
||||
default = config.get("css", "default.css")
|
||||
posts = author.get("css_posts")
|
||||
personal = user.get("css_personal")
|
||||
|
||||
if user.get("css_user_global_priority") or not posts:
|
||||
theme = personal
|
||||
|
||||
elif posts:
|
||||
theme = posts
|
||||
|
||||
try:
|
||||
cssfile = load(theme)
|
||||
except Exception as e:
|
||||
if theme:
|
||||
print(e)
|
||||
cssfile = open(default, "r").read()
|
||||
|
||||
return cssfile
|
||||
|
||||
|
||||
def Preview(server, ID, user={}, download=True, set_website=False, set_personal=False, set_posts=False):
|
||||
|
||||
if not user:
|
||||
user = Render.validate(server.cookie)
|
||||
|
||||
|
||||
html = '<!-- Themes.Preview -->'
|
||||
html = html + '<div class="dark_box" style="min-height:180px;"><br>'
|
||||
|
||||
metadata = themes().get(ID, {})
|
||||
|
||||
thumbnail = metadata.get("thumbnail", "")
|
||||
if thumbnail:
|
||||
html = html + '<img alt="[avatar]" style="height:150px;float:left;margin-right:20px;margin-bottom:20px" src="'+thumbnail+'">'
|
||||
|
||||
name = Safe(metadata.get("name", "Theme"))
|
||||
html = html + '<b>' + name + '</b>\n'
|
||||
|
||||
author = Safe(metadata.get("author", ""))
|
||||
if author:
|
||||
html = html + "<br><br>" + Render.User(author)
|
||||
|
||||
description = Safe(metadata.get("description", ""))
|
||||
html = html + '<small>'+markdown.convert(description, False)+"</small><br>"
|
||||
|
||||
html = html + "<br><br><br>"
|
||||
|
||||
# Doing things with themes
|
||||
|
||||
if download:
|
||||
html = html + Render.Button("View", "/themes/"+ID, "view", newpage=True)
|
||||
|
||||
# Editing
|
||||
|
||||
edits = Render.editsIn(user.get("username", ""), "Themes_CSS")
|
||||
|
||||
if edits and metadata.get("author") == user.get("username"):
|
||||
|
||||
html = html + Render.Button("Edit", "/theme_editor?id="+ID, "edit")
|
||||
|
||||
if edits:
|
||||
|
||||
html = html + Render.Button("Fork", "/theme_editor?id="+ID+"&fork=True", "checklist_new")
|
||||
|
||||
if set_personal:
|
||||
|
||||
icon = "unchecked"
|
||||
if user.get("css_personal") == ID:
|
||||
icon = "checked"
|
||||
|
||||
html = html + Render.Button("Personal", "/settings?set_personal_theme="+ID+"#css", icon)
|
||||
|
||||
if set_posts:
|
||||
|
||||
icon = "unchecked"
|
||||
if user.get("css_posts") == ID:
|
||||
icon = "checked"
|
||||
|
||||
html = html + Render.Button("Posts", "/settings?set_posts_theme="+ID+"#css", icon)
|
||||
|
||||
html = html + '</div><!-- /Themes.Preview -->'
|
||||
|
||||
return html
|
||||
|
||||
|
||||
def ViewPage(server, ID):
|
||||
|
||||
metadata = themes().get(ID, {})
|
||||
cssfile = load(ID)
|
||||
|
||||
|
||||
config = Set.Load()
|
||||
|
||||
html = Render.head(server,
|
||||
title = metadata.get("name", "Theme"),
|
||||
description = metadata.get("description", ""),
|
||||
image = metadata.get("thumbnail", ""),
|
||||
config = config,
|
||||
author = metadata.get("author")
|
||||
)
|
||||
|
||||
html = html + Render.Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet"))
|
||||
html = html + Render.Button("Themes", "/themes", "theme")
|
||||
|
||||
html = html + '<!-- Themes.ViewPage -->'
|
||||
|
||||
html = html + '<div class="middle_box">'
|
||||
|
||||
html = html + Preview(server, ID, download=False)
|
||||
|
||||
html = html + "<br><br>"
|
||||
|
||||
html = html + "<pre>" + markdown.code_highlight(cssfile) + "</pre><br><br>"
|
||||
|
||||
html = html + '</div>'
|
||||
|
||||
html = html + Render.LoginButton(server)
|
||||
|
||||
Render.send(server, html, 200)
|
||||
|
||||
|
||||
def Editor(server):
|
||||
|
||||
|
||||
config = Set.Load()
|
||||
|
||||
html = Render.head(server,
|
||||
title = "Theme Editor",
|
||||
description = "Theme Editor",
|
||||
config = config
|
||||
)
|
||||
|
||||
html = html + Render.Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet"))
|
||||
|
||||
html = html + '<!-- Themes.Editor -->'
|
||||
|
||||
ID = server.parsed.get("id", [""])[0]
|
||||
FORK = server.parsed.get("fork", [""])[0]
|
||||
|
||||
if ID:
|
||||
metadata = themes().get(ID, {})
|
||||
text = load(ID)
|
||||
else:
|
||||
metadata = {}
|
||||
text = open("default.css", "r").read()
|
||||
|
||||
html = html + """
|
||||
|
||||
<div class="middle_section_article">
|
||||
|
||||
<div class="dark_box">
|
||||
|
||||
<form action="do_theme_edit" method="post">"""
|
||||
|
||||
if FORK:
|
||||
metadata["name"] = "Fork of "+metadata.get("name", "Theme")
|
||||
|
||||
if ID and not FORK:
|
||||
html = html + '<input type="hidden" name="id" value="'+ID.replace("'", "'")+'">'
|
||||
|
||||
html = html + """
|
||||
|
||||
<img style="vertical-align: middle" src="/icon/theme">
|
||||
<input class="button" style="width:90%" required="" name="name" placeholder="Name" value='"""+metadata.get("name", "").replace("'", "'")+"""'>
|
||||
|
||||
<img style="vertical-align: middle" src="/icon/image_link">
|
||||
<input class="button" style="width:90%" name="thumbnail"
|
||||
placeholder="Screenshot"
|
||||
title="A link to the image which will be rendered alongside the title. A visual representation of the theme."
|
||||
value='"""+metadata.get("thumbnail", "").replace("'", "'")+"""'>
|
||||
|
||||
<center>
|
||||
<textarea class="toot" rows="5" style="width:95%" name="description" placeholder="Description">"""+metadata.get("description", "")+"""</textarea>
|
||||
|
||||
<br>
|
||||
|
||||
<textarea class="pre" rows="30" style="width:95%" required="" name="text" placeholder="Text of your post">"""+Safe(text)+"""</textarea>
|
||||
|
||||
</center>
|
||||
|
||||
<br>
|
||||
<button class="button" type="submit">
|
||||
<img class="icon" style="vertical-align: middle" src="/icon/ok">
|
||||
Publish
|
||||
</button>
|
||||
|
||||
|
||||
</div>
|
||||
"""
|
||||
|
||||
|
||||
|
||||
html = html + '</div>'
|
||||
|
||||
html = html + '<div class="checklist_section_article">'
|
||||
html = html + Render.Button("+ Image", "/upload_image", "image_new", newpage=True)+'<br><br>'
|
||||
html = html + '</div>'
|
||||
|
||||
html = html + Render.LoginButton(server)
|
||||
|
||||
Render.send(server, html, 200)
|
||||
|
||||
|
||||
def DoThemeEdit(server):
|
||||
|
||||
user = Render.validate(server.cookie)
|
||||
|
||||
if not user or not Render.editsIn(user.get("username", ""), "Themes_CSS"):
|
||||
Render.AccessDenied(server)
|
||||
return
|
||||
|
||||
ID = server.parsed.get("id", [""])[0]
|
||||
name = server.parsed.get("name", [""])[0]
|
||||
thumbnail = server.parsed.get("thumbnail", [""])[0]
|
||||
description = server.parsed.get("description", [""])[0]
|
||||
text = server.parsed.get("text", [""])[0]
|
||||
|
||||
if ID:
|
||||
metadata = themes().get(ID, {})
|
||||
|
||||
if user.get("username") != metadata.get("author"):
|
||||
Render.AccessDenied(server)
|
||||
return
|
||||
|
||||
else:
|
||||
|
||||
ID = RandString()
|
||||
metadata = {"author":user.get("username", "")}
|
||||
|
||||
metadata["name"] = name
|
||||
metadata["thumbnail"] = thumbnail
|
||||
metadata["description"] = description
|
||||
|
||||
folder = Set.Folder()+"/themes/"+ID
|
||||
|
||||
# Making the folders
|
||||
try: os.makedirs(folder)
|
||||
except: pass
|
||||
|
||||
# Saving the CSS file
|
||||
filename = folder+"/theme.css"
|
||||
with ThreadLock(filename):
|
||||
with open(filename, "w") as save:
|
||||
save.write(text)
|
||||
|
||||
# Saving the metadata
|
||||
filename = folder+"/theme.json"
|
||||
with ThreadLock(filename):
|
||||
with open(filename, "w") as save:
|
||||
json.dump(metadata, save, indent=4)
|
||||
|
||||
Render.Redirect(server, "themes/"+ID)
|
||||
|
||||
|
||||
def ListPage(server):
|
||||
|
||||
user = Render.validate(server.cookie)
|
||||
config = Set.Load()
|
||||
|
||||
html = Render.head(server,
|
||||
title = "Themes",
|
||||
description = "Themes",
|
||||
config = config
|
||||
)
|
||||
|
||||
html = html + Render.Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet"))
|
||||
|
||||
html = html + '<!-- Themes.ListPage -->'
|
||||
html = html + '<div class="middle_box">'
|
||||
|
||||
allThemes = themes()
|
||||
for theme in allThemes:
|
||||
html = html + Preview(server, theme, user)
|
||||
|
||||
html = html + '</div>'
|
||||
|
||||
html = html + Render.Footer(server)
|
||||
html = html + Render.LoginButton(server)
|
||||
|
||||
Render.send(server, html, 200)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,712 @@
|
||||
# THIS SOFTWARE IS A PART OF FASTLBRY PROJECT
|
||||
# THIS SPECIFIC FILE IS UNDER GNU GPLv3 or later
|
||||
|
||||
import os
|
||||
import json
|
||||
from subprocess import *
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
from urllib import request, parse
|
||||
|
||||
from modules import Render
|
||||
from modules import Set
|
||||
from modules import ActivityPub
|
||||
|
||||
config = Set.Load()
|
||||
domain = config.get("domain", "")
|
||||
|
||||
|
||||
preicons = os.listdir(os.getcwd()+"/icons")
|
||||
icons = []
|
||||
for i in preicons:
|
||||
icons.append(i.replace(".png",""))
|
||||
|
||||
|
||||
################################################################################
|
||||
|
||||
# Markdown. Or .md file format is an easy way to give your simple text documents
|
||||
# a bit of flare. Stuff like links, images and quotes are supported. Also bold
|
||||
# an italic characters.
|
||||
def lbrynet(method="", params={}):
|
||||
|
||||
# First we will make an attempt to use the request module
|
||||
try:
|
||||
|
||||
# To test the SDK subprocess thing I have this little switch
|
||||
# 1/0 # Division by zero will trigger and error in this 'try'
|
||||
# and will skip it, to test the other method.
|
||||
|
||||
data = {"method":method,
|
||||
"params":params}
|
||||
|
||||
# The port of the LBRY SDK could be different for each user
|
||||
# so here I'm trying to solve this issue
|
||||
|
||||
sdk_url = "http://localhost:5279"
|
||||
try:
|
||||
settings = open(os.path.expanduser('~/.local/share/lbry/lbrynet/daemon_settings.yml'))
|
||||
for line in settings:
|
||||
if line.startswith("api:"):
|
||||
sdk_url = "http://"+line[5:]
|
||||
except:
|
||||
pass
|
||||
|
||||
data = str(json.dumps(data))
|
||||
data = data.encode('utf-8')
|
||||
req = request.Request(sdk_url, data=data)
|
||||
resp = json.loads(request.urlopen(req).read())
|
||||
|
||||
try:
|
||||
resp = resp["result"]
|
||||
except:
|
||||
pass
|
||||
|
||||
return resp
|
||||
|
||||
except Exception as e:
|
||||
print("Failed", e)
|
||||
return False
|
||||
|
||||
|
||||
def odysee_get(link):
|
||||
|
||||
if link.startswith("lbry://"):
|
||||
with open("lbry.json") as json_file:
|
||||
lbry = json.load(json_file)
|
||||
newlink = link.replace("lbry://", "/lbry/").replace("#", ":")
|
||||
if newlink not in lbry["hosting"]:
|
||||
out = lbrynet("resolve",
|
||||
{"urls":[link]}
|
||||
)
|
||||
out = out[link]
|
||||
|
||||
try:
|
||||
price = out["value"]["fee"]["amount"]
|
||||
except:
|
||||
price = 0
|
||||
|
||||
if price != 0:
|
||||
return link
|
||||
|
||||
dpath = lbrynet("get", {"uri":link, "save_file":True}).get("download_path")
|
||||
|
||||
|
||||
lbry["hosting"][newlink] = dpath
|
||||
if dpath:
|
||||
with open("lbry.json", 'w') as f:
|
||||
json.dump(lbry, f, indent=4)
|
||||
else:
|
||||
return link
|
||||
|
||||
|
||||
return newlink+"\n"
|
||||
|
||||
return link
|
||||
|
||||
def Open(md, AP=False):
|
||||
|
||||
# Spliting it for the read.
|
||||
md = "\n\n"+md
|
||||
md = md.split("\n")
|
||||
|
||||
# First thing is I was to read the headings and convert it into a tree.
|
||||
|
||||
|
||||
tree = []
|
||||
indent = 1
|
||||
c = []
|
||||
|
||||
skip = 0
|
||||
|
||||
for n,line in enumerate(md):
|
||||
|
||||
if skip > n:
|
||||
continue
|
||||
|
||||
# Mentions
|
||||
line2 = line
|
||||
line = ""
|
||||
for word in line2.split(" "):
|
||||
if word.startswith("@"):
|
||||
|
||||
href = ""
|
||||
# possible Activitypub account
|
||||
if "@" in word[1:]:
|
||||
actor = ActivityPub.GetRemoteActor(word)
|
||||
href = actor.get("url", actor.get("id", ""))
|
||||
|
||||
if not AP:
|
||||
|
||||
if not href:
|
||||
href = '/account/'+word.replace("@", "")
|
||||
word = '['+word+']('+href+')'
|
||||
else:
|
||||
if not href:
|
||||
href = 'https://'+domain+'/activitypub/account/'+word.replace("@", "")
|
||||
word = '<span class="h-card" translate="no"><a href="'+href+'" class="u-url mention">'+word+'</a></span>'
|
||||
|
||||
|
||||
|
||||
line = line + " " + word
|
||||
|
||||
line = line[1:]
|
||||
|
||||
ty = "text"
|
||||
te = line
|
||||
|
||||
# Here I want to simply get a type of each line. Later we going to parse
|
||||
# the links and other things. But first. Let's parse stuff based on
|
||||
# lines.
|
||||
|
||||
if line.startswith("```"):
|
||||
|
||||
# THREE ``` aka code block
|
||||
|
||||
# This tag will block any other tags
|
||||
# untill it's untagged
|
||||
|
||||
code = ""
|
||||
#print("#####", n)
|
||||
for l in md[n+1:]:
|
||||
if not l.startswith("```"):
|
||||
code = code + l + "\n"
|
||||
|
||||
else:
|
||||
skip = n + code.count("\n") + 2
|
||||
break
|
||||
#print("!!!!!!!!!", skip)
|
||||
tree.append(["text_cm", code])
|
||||
te = ""
|
||||
|
||||
elif line.startswith("---") or line.startswith("___"):
|
||||
|
||||
# Line break in markdown. In this case will be done like this:
|
||||
te = '</div><div class="dark_box">'
|
||||
|
||||
elif line.startswith("#"):
|
||||
# The titles of the chapter. The Headers are usually written similar
|
||||
# to how here in python you write comments. It's a # , space, and the
|
||||
# text.
|
||||
|
||||
# The amount of hashes. ## or ### gives different sized text. Officialy
|
||||
# it should support up to 6 hashes. ######. But why not make it more
|
||||
# just in case.
|
||||
|
||||
ty = line.count("#") # This might give bugs
|
||||
|
||||
|
||||
|
||||
tree.append([ty, te+"\n"])
|
||||
|
||||
# Now the stage 0 is over and we parsed the basic things. Now is the hard
|
||||
# part to parse out all the images and stuff inside them. It's going to be
|
||||
# done per part. And we are going to use the same technique I used for the
|
||||
# conversion of the legacy projects. See : studio/story.py ( in VCStudio )
|
||||
|
||||
# We are going to itterate over each letter. And decide what to do by that
|
||||
|
||||
newtree = []
|
||||
|
||||
for block in tree:
|
||||
|
||||
if block[0] == "text_cm":
|
||||
newtree.append(block)
|
||||
continue
|
||||
|
||||
part = ""
|
||||
skip = 0
|
||||
|
||||
for n, l in enumerate(block[-1]):
|
||||
|
||||
if skip > n:
|
||||
continue
|
||||
|
||||
part = part + l
|
||||
|
||||
# Here we are going to do something if a give condition is met.
|
||||
# Usually I gonna do something if [part] ends with a given markdown
|
||||
# thing. I don't have a manual of markdown on me. So please make it
|
||||
# more supported. I guess. I might forget things I rarely use.
|
||||
|
||||
# Links are made with [stuff you click on](https://example.com)
|
||||
# but similar to it. Images are done 
|
||||
# and even weirder you can put one into the other. Like
|
||||
# [](https://example.com)
|
||||
# Which going to give you a clickable image.
|
||||
|
||||
# For this version what we are going to do is next.
|
||||
# If we got [![ then it's a clickable image
|
||||
# If we got ![ then it's just image
|
||||
# and if we got [ then it's a link.
|
||||
|
||||
if part.endswith("[!["):
|
||||
|
||||
# IMAGE LINK
|
||||
newtree.append([block[0], part[:-3]])
|
||||
|
||||
tooltip = ""
|
||||
imageurl = ""
|
||||
url = ""
|
||||
t = False
|
||||
iu = False
|
||||
skip = n
|
||||
for le in block[-1][n:]: # For letters in the rest of text
|
||||
|
||||
skip = skip + 1
|
||||
|
||||
if le == "]":
|
||||
t = True
|
||||
elif le == ")" and t and not iu:
|
||||
iu = True
|
||||
elif le == ")" and t and iu:
|
||||
break
|
||||
elif not t:
|
||||
tooltip = tooltip +le
|
||||
elif t and not iu:
|
||||
imageurl = imageurl + le
|
||||
else:
|
||||
url = url+le
|
||||
|
||||
tooltip = tooltip[tooltip.find("[")+1:]
|
||||
imageurl = imageurl[imageurl.find("(")+1:]
|
||||
url = url[url.find("(")+1:]
|
||||
|
||||
apnd = ["image_link", imageurl, url]
|
||||
|
||||
newtree.append(apnd)
|
||||
|
||||
part = ""
|
||||
|
||||
|
||||
elif part.endswith("!["):
|
||||
|
||||
# IMAGE
|
||||
|
||||
newtree.append([block[0], part[:-2]])
|
||||
|
||||
tooltip = ""
|
||||
url = ""
|
||||
t = False
|
||||
skip = n
|
||||
for le in block[-1][n:]: # For letters in the rest of text
|
||||
|
||||
skip = skip + 1
|
||||
|
||||
if le == "]":
|
||||
t = True
|
||||
elif le == ")" and t:
|
||||
break
|
||||
elif not t:
|
||||
tooltip = tooltip +le
|
||||
else:
|
||||
url = url+le
|
||||
|
||||
tooltip = tooltip[tooltip.find("[")+1:]
|
||||
url = url[url.find("(")+1:]
|
||||
|
||||
apnd = ["image", tooltip, url]
|
||||
newtree.append(apnd)
|
||||
|
||||
part = ""
|
||||
|
||||
|
||||
elif part.endswith("[") and not block[-1][n:].startswith('[!['):
|
||||
|
||||
# LINK
|
||||
newtree.append([block[0], part[:-1]])
|
||||
|
||||
|
||||
tooltip = ""
|
||||
url = ""
|
||||
t = False
|
||||
skip = n
|
||||
for le in block[-1][n:]: # For letters in the rest of text
|
||||
|
||||
skip = skip + 1
|
||||
|
||||
if le == "]":
|
||||
t = True
|
||||
elif le == ")" and t:
|
||||
break
|
||||
elif not t:
|
||||
tooltip = tooltip +le
|
||||
else:
|
||||
url = url+le
|
||||
|
||||
tooltip = tooltip[tooltip.find("[")+1:]
|
||||
url = url[url.find("(")+1:]
|
||||
|
||||
apnd = ["link", tooltip, url]
|
||||
newtree.append(apnd)
|
||||
|
||||
part = ""
|
||||
|
||||
|
||||
|
||||
# Now I want to deal with `, *, ** and ***. If you want to help me you
|
||||
# can implement other types. Such as _, __, ___ and so on. Markdown is
|
||||
# a very rich language. I'm going to use the cut down version I see other
|
||||
# people use.
|
||||
|
||||
# BTW this is the time. Feb 28. When I switched from Gedit to GNU Emacs.
|
||||
# Interesting feeling using this programm. I kind a love it even tho
|
||||
# so many stuff in not intuitive. Like saving is not Ctrl - S but
|
||||
# Ctrl - X -> Ctrl - S.
|
||||
|
||||
# Things like Alt-; to comment multiple lines at ones is HUGE. Also it
|
||||
# was built by programmers for programmers. So it's a very good tool.
|
||||
|
||||
elif part.endswith("**") and not block[-1][n+2:].startswith('*'):
|
||||
|
||||
# DOUBLE **
|
||||
|
||||
newtree.append([block[0], part[:-2]])
|
||||
|
||||
if block[0] == "text":
|
||||
block[0] = "text_b"
|
||||
else:
|
||||
block[0] = "text"
|
||||
|
||||
part = ""
|
||||
|
||||
elif part.endswith("*") and not block[-1][n+1:].startswith('*'):
|
||||
|
||||
# SINGLE *
|
||||
|
||||
newtree.append([block[0], part[:-1]])
|
||||
|
||||
if block[0] == "text":
|
||||
block[0] = "text_i"
|
||||
else:
|
||||
block[0] = "text"
|
||||
|
||||
part = ""
|
||||
|
||||
|
||||
elif part.endswith("`"):
|
||||
|
||||
# SINGLE `
|
||||
|
||||
newtree.append([block[0], part[:-1]])
|
||||
|
||||
if block[0] == "text":
|
||||
block[0] = "text_c"
|
||||
else:
|
||||
block[0] = "text"
|
||||
|
||||
part = ""
|
||||
|
||||
newtree.append([block[0], part])
|
||||
|
||||
|
||||
#newtree.append(["text", "\n"*20+" [END OF DOCUMENT] "])
|
||||
tree = newtree
|
||||
|
||||
return(tree)
|
||||
|
||||
def search_convert(s):
|
||||
|
||||
# This function convers a chapter name into a link
|
||||
# such links are use in notabug.org to link to chapters
|
||||
# for example example.com/file.md#chapter-name
|
||||
# With this url it will load the example.com/file.md and
|
||||
# then skip to the "Chapter Name" chapter.
|
||||
|
||||
# This function transforms "Chapter Name" into "chapter-name"
|
||||
|
||||
l = " ./\|[]{}()?!@#$%^&*`~:;'\"=,<>"
|
||||
s = s.lower().replace(" ","-")
|
||||
r = ""
|
||||
for i in s:
|
||||
if i not in l:
|
||||
r = r + i
|
||||
return r
|
||||
|
||||
def code_highlight(text):
|
||||
|
||||
codes = {
|
||||
'"':""",
|
||||
"'":"'",
|
||||
"`":"`",
|
||||
"\n":" ",
|
||||
"<":"<",
|
||||
">":">"
|
||||
}
|
||||
def R(tag):
|
||||
for c in codes:
|
||||
tag = tag.replace(c, codes[c])
|
||||
return tag
|
||||
|
||||
text.replace("&", "&")
|
||||
|
||||
for c in ("<", ">"):
|
||||
text = text.replace(c, codes[c])
|
||||
|
||||
text = text.replace("\r","")
|
||||
|
||||
# green tags
|
||||
|
||||
ntext = ""
|
||||
for tag in ['"""', "'''", '"', "'", "```", "`", ]:
|
||||
while text.count(tag) > 1:
|
||||
ntext = ntext + text[:text.find(tag)]+'<span class="text_string">'+R(tag)
|
||||
text = text[text.find(tag)+len(tag):]
|
||||
|
||||
|
||||
ntext = ntext + text[:text.find(tag)]+R(tag)+'</span>'
|
||||
text = text[text.find(tag)+len(tag):]
|
||||
ntext = ntext + text
|
||||
text = ntext
|
||||
|
||||
|
||||
# purple tags
|
||||
ntext = ""
|
||||
for n, line in enumerate(text.split("\n")):
|
||||
|
||||
if "=" in line.replace("class=", "class>") and " " not in line[:line.find("=")].strip():
|
||||
line = '<span class="text_variable">'+line[:line.replace("class=", "class>").find("=")]+'</span><span class="text_command">=</span>'+line[line.replace("class=", "class>").find("=")+1:]
|
||||
|
||||
if n !=0:
|
||||
ntext = ntext + "\n" + line
|
||||
else:
|
||||
ntext = line
|
||||
|
||||
text = ntext
|
||||
|
||||
# comments
|
||||
ntext = ""
|
||||
for n, line in enumerate(text.split("\n")):
|
||||
|
||||
if "#" in line.replace("&#", " "):
|
||||
line = line[:line.replace("&#", " ").find("#")]+'<span class="text_comment">'+line[line.replace("&#", " ").find("#"):]+'</span>'
|
||||
|
||||
if n !=0:
|
||||
ntext = ntext + "\n" + line
|
||||
else:
|
||||
ntext = line
|
||||
|
||||
# yellow tags
|
||||
for tag in ["(", ")", "[", "]", "{", "}", "-", "+", ":", "==", "!=", "!", "$",
|
||||
" / ", "*","try", "except","elif","if", "else", "print", "return",
|
||||
"def", "for", "while", "range", "True", "False", "true", "false"]:
|
||||
|
||||
ntext = ntext.replace(tag, '<span class="text_command">'+tag+'</span>')
|
||||
|
||||
return ntext.replace("\n", codes["\n"])
|
||||
|
||||
def convert(filename, isfile=True, fullpath=False, comments={}, AP=False):
|
||||
|
||||
|
||||
if fullpath:
|
||||
from modules import Set
|
||||
config = Set.Load()
|
||||
domain = config.get("domain", "")
|
||||
|
||||
textReturn = ""
|
||||
if isfile:
|
||||
text = open(filename)
|
||||
text = text.read()
|
||||
else:
|
||||
text = filename
|
||||
md = Open(text, AP=AP)
|
||||
close_div = False
|
||||
for n, i in enumerate(md):
|
||||
|
||||
close_not_here = False
|
||||
|
||||
if i[-1].startswith(">") or i[-1].startswith(">") and not close_div:
|
||||
|
||||
i[-1] = '<div class="quote">' + i[-1].replace(">", "").replace(">", "")
|
||||
|
||||
close_div = True
|
||||
elif close_div:
|
||||
i[-1] = i[-1].replace(">", "").replace(">", "")
|
||||
try:
|
||||
NEXT = md[n+1]
|
||||
except:
|
||||
NEXT = ["text", ""]
|
||||
if NEXT[-1].startswith(">") or NEXT[-1].startswith(">"):
|
||||
close_not_here = True
|
||||
|
||||
if close_div and "\n" in i[-1] and not close_not_here:
|
||||
i[-1] = i[-1]+"</div>"
|
||||
close_div = False
|
||||
|
||||
if type(i[0]) == str and i[0].startswith("text") and not i[0] == "text_cm":
|
||||
|
||||
tag = ""
|
||||
ctag = ""
|
||||
for f in ["i", "b", "c"]:
|
||||
if f in i[0]:
|
||||
f = f.replace("c", "code").replace("q", "blockquote")
|
||||
if f == "code": i[-1] = code_highlight(i[-1])
|
||||
tag = "<"+f+">"
|
||||
ctag = "</"+f+">"
|
||||
if i[-1].startswith("lbry://"):
|
||||
tag = '<a target="_blank" href="https://odysee.com/'+i[-1].replace("lbry://", "")[:-1]+'">'
|
||||
ctag = "</a>"
|
||||
if i[-1].startswith("http"):
|
||||
tag = '<a target="_blank" href="'+i[-1][:-1]+'">'
|
||||
ctag = "</a>"
|
||||
if i[-1].startswith("c:"):
|
||||
|
||||
if AP:
|
||||
i[-1] = ""
|
||||
continue
|
||||
|
||||
try: N = int(i[-1].replace("c:",""))
|
||||
except:N = None
|
||||
try:
|
||||
comment = Render.getcomment(N, comments.get("comments", []))
|
||||
URL = comments.get("url","")
|
||||
COMMENTS = comments.get("comments", [])
|
||||
NOREFERENCE = comments.get("noreference", None)
|
||||
try:
|
||||
comment = Render.Comment(comment, URL, n=N, comments=COMMENTS, noreplies=True,
|
||||
noreference=N,
|
||||
nohashlink=True)
|
||||
except:
|
||||
comment = ""
|
||||
|
||||
if NOREFERENCE == None:
|
||||
TEXT = """
|
||||
<details>
|
||||
<summary>
|
||||
... c:"""+str(N)+"""
|
||||
</summary>
|
||||
|
||||
"""+comment+"""
|
||||
|
||||
</details>
|
||||
"""
|
||||
|
||||
i[-1] = TEXT.replace("\n", "")
|
||||
|
||||
elif NOREFERENCE == N:
|
||||
i[-1] = ''
|
||||
|
||||
else:
|
||||
i[-1] = '<small>'+i[-1]+'</small>'
|
||||
|
||||
except Exception as e: print(e)
|
||||
|
||||
|
||||
textReturn = textReturn + tag + i[-1] + ctag
|
||||
|
||||
elif i[0] == "text_cm":
|
||||
tag = '<pre>'
|
||||
ctag = "</pre>"
|
||||
textReturn = textReturn + tag + code_highlight(i[-1]) + ctag
|
||||
|
||||
elif type(i[0]) == int:
|
||||
if textReturn.strip():
|
||||
textReturn = textReturn + '\n</div><div class="dark_box"><br>'
|
||||
textReturn = textReturn + '<a href="#'+search_convert(i[-1].replace("#", "").replace("\n", "")[1:])+'"><h'+str(i[0])+' id="'+search_convert(i[-1].replace("#", "").replace("\n", "")[1:])+'">' + i[-1].replace("#", "") +"</h"+str(i[0])+"></a>"
|
||||
|
||||
elif i[0] == "image_link":
|
||||
|
||||
if fullpath and i[-1].startswith("/"):
|
||||
i[-1] = "https://"+domain+i[-1]
|
||||
|
||||
textReturn = textReturn + '\n<center><a target="_blank" href="'+i[-1]+'">'+'<img src="'+i[1]+'">'+"</a></center>"
|
||||
|
||||
elif i[0] == "image":
|
||||
|
||||
|
||||
if fullpath and i[-1].startswith("/"):
|
||||
i[-1] = "https://"+domain+i[-1]
|
||||
|
||||
if i[1] == "audio":
|
||||
try:
|
||||
i[-1] = odysee_get(i[-1])
|
||||
textReturn = textReturn + '\n<audio controls="controls" width="20%" src="'+i[-1]+'"></audio>'
|
||||
except:
|
||||
textReturn = textReturn + '<details class="button"><summary>Failed to resolve.</summary> <code>'+i[-1]+'</code><br> You can try <a href="https://odysee.com/'+i[-1].replace("lbry://", "").replace("#", ":")+'">Odysee</a>. </details>'
|
||||
|
||||
elif i[1] == "video":
|
||||
i[-1] = i[-1].replace("lbry://", "https://player.odycdn.com/speech/")
|
||||
textReturn = textReturn + """\n<video width="100%" controls>
|
||||
<source src=\""""+i[-1].replace("#", ":")+"""\" type="video/mp4">
|
||||
</video> """
|
||||
|
||||
elif i[1] == "peertube-video":
|
||||
i[-1] = i[-1].replace("/w/", "/videos/embed/")
|
||||
textReturn = textReturn + """
|
||||
<center>
|
||||
<iframe style="width:100%; aspect-ratio:16 / 9;" src=\""""+i[-1]+"""" frameborder="0"" allowfullscreen="" sandbox="allow-same-origin allow-scripts allow-popups allow-forms"></iframe>
|
||||
</center>
|
||||
"""
|
||||
|
||||
elif i[1] in icons:
|
||||
|
||||
textReturn = textReturn + '<a target="_blank" href="'+i[-1]+'"><img style="vertical-align: middle" src="/icon/'+i[1]+'"></a>'
|
||||
|
||||
else:
|
||||
i[-1] = i[-1].replace("lbry://", "https://spee.ch/")
|
||||
textReturn = textReturn + '\n<center><a target="_blank" href="'+i[-1]+'"><img title="'+i[1].replace('"', """)+'" alt="[embedded image]" src="'+i[-1]+'"></a></center>'
|
||||
|
||||
|
||||
elif i[0] == "link":
|
||||
|
||||
|
||||
if not i[-1]:
|
||||
i[-1] = "/search?text="+urllib.parse.quote_plus(i[1])
|
||||
|
||||
if fullpath and i[-1].startswith("/"):
|
||||
i[-1] = "https://"+domain+i[-1]
|
||||
|
||||
|
||||
textReturn = textReturn + '<a target="_blank" href="'+i[-1]+'">'+i[1]+"</a>"
|
||||
|
||||
|
||||
|
||||
|
||||
textReturn = textReturn.replace("\n", "\n<br>")
|
||||
|
||||
textReturn = textReturn + "\n\n<!-- Raw Version of the markdown text\n\n"
|
||||
textReturn = textReturn + text.replace("-->", "( somebody was trying to pentest the site here with exiting the comment early. Good job )") + "\n\n"
|
||||
textReturn = textReturn + " -->\n\n"
|
||||
|
||||
|
||||
return textReturn
|
||||
|
||||
def toMarkdown(text):
|
||||
|
||||
newtext = ""
|
||||
|
||||
tag = ""
|
||||
link = ""
|
||||
write = True
|
||||
|
||||
for letter in text:
|
||||
|
||||
if letter == "<":
|
||||
write = False
|
||||
continue
|
||||
|
||||
elif letter == ">":
|
||||
write = True
|
||||
|
||||
if tag.startswith("a"):
|
||||
link = tag[tag.find('href="')+6:]
|
||||
link = link[:link.find('"')]
|
||||
newtext = newtext + "["
|
||||
elif tag.startswith("/a"):
|
||||
newtext = newtext + ']('+link+')'
|
||||
|
||||
elif tag.startswith("b") or tag.startswith("/b"):
|
||||
newtext = newtext + "**"
|
||||
|
||||
elif tag.startswith("i") or tag.startswith("/i"):
|
||||
newtext = newtext + "*"
|
||||
|
||||
tag = ""
|
||||
continue
|
||||
|
||||
if write:
|
||||
newtext = newtext + letter
|
||||
else:
|
||||
tag = tag + letter
|
||||
|
||||
|
||||
return newtext
|
||||
@@ -0,0 +1,167 @@
|
||||
# GPLv3 or later.
|
||||
# (C) J.Y.Amihud 2025
|
||||
|
||||
# Program that troubleshoots execution of python scrips.
|
||||
try:
|
||||
import importlib.util
|
||||
except:
|
||||
NotFoundError("importlib.util")
|
||||
exit()
|
||||
|
||||
try:
|
||||
import os
|
||||
except:
|
||||
NotFoundError("os")
|
||||
exit()
|
||||
|
||||
import __main__
|
||||
|
||||
count = 0
|
||||
broken = 0
|
||||
|
||||
checked = []
|
||||
|
||||
def run():
|
||||
|
||||
global count
|
||||
global broken
|
||||
|
||||
mainfilename = __main__.__file__
|
||||
check(mainfilename)
|
||||
|
||||
if broken:
|
||||
print()
|
||||
print(" "+str(broken)+" missing modules found.")
|
||||
print(" It is "+str(int(round(broken/count*100)))+"% of the program broken.")
|
||||
print(" Running may introduce issues.")
|
||||
print()
|
||||
print(" You can try to run:")
|
||||
print(" $ python3 -m pip install {module}")
|
||||
print(" To install missing modules.")
|
||||
print()
|
||||
|
||||
yn = input(clr["tdyl"]+"[ ? ]"+clr["norm"]+" Try to run anyway? ( Y/n ): ")
|
||||
if "y" not in yn.lower():
|
||||
exit()
|
||||
|
||||
def check(filename):
|
||||
|
||||
global count
|
||||
global broken
|
||||
global checked
|
||||
|
||||
checked.append(filename)
|
||||
checkalso = []
|
||||
|
||||
text = open(filename, "r")
|
||||
text = text.read()
|
||||
|
||||
if __file__ in filename:
|
||||
return
|
||||
|
||||
for line in text.split("\n"):
|
||||
if "import" in line and __name__ not in line:
|
||||
|
||||
if line.strip().startswith("from"):
|
||||
line = line.strip()[5:]
|
||||
line = line.replace(" import ", ".").strip()
|
||||
|
||||
if line.endswith("*"):
|
||||
line = line[:-2]
|
||||
|
||||
elif line.strip().startswith("import"):
|
||||
line = line.replace("import", "").strip()
|
||||
|
||||
else:
|
||||
continue
|
||||
|
||||
if "," in line:
|
||||
line = line[:line.find(",")].strip()
|
||||
|
||||
count += 1
|
||||
|
||||
try:
|
||||
spec = importlib.util.find_spec(line)
|
||||
except:
|
||||
|
||||
try:
|
||||
line = line[:line.rfind(".")]
|
||||
spec = importlib.util.find_spec(line)
|
||||
except:
|
||||
|
||||
print(clr["tdyl"]+"[ ? ]"+clr["norm"], line)
|
||||
continue
|
||||
|
||||
|
||||
if spec:
|
||||
#Found(spec)
|
||||
|
||||
if os.getcwd() in str(spec.origin):
|
||||
checkalso.append(spec.origin)
|
||||
|
||||
|
||||
else:
|
||||
NotFoundError(line, filename)
|
||||
broken += 1
|
||||
|
||||
if checkalso:
|
||||
for f in checkalso:
|
||||
if f not in checked:
|
||||
try:
|
||||
check(f)
|
||||
except:
|
||||
pass
|
||||
|
||||
def Found(spec):
|
||||
|
||||
print(clr["tdgr"]+"[ v ]"+clr["norm"], spec.name)
|
||||
|
||||
def NotFoundError(module, filename=""):
|
||||
|
||||
print(clr["tdrd"]+"[ x ]"+clr["norm"], module, clr["tbbl"]+"in "+filename+clr["norm"])
|
||||
|
||||
|
||||
clr = {
|
||||
"norm":"\033[00m", # Reset to normal
|
||||
"bold":"\033[01m", # Bold Text
|
||||
"ital":"\033[03m", # Italic Text
|
||||
"undr":"\033[04m", # Underlined
|
||||
"blnk":"\033[05m", # Blinking
|
||||
|
||||
# Text
|
||||
"tdbl":"\033[30m", # Dark Black
|
||||
"tdrd":"\033[31m", # Dark Red
|
||||
"tdgr":"\033[32m", # Dark Green
|
||||
"tdyl":"\033[33m", # Dark Yellow
|
||||
"tdbu":"\033[34m", # Dark Blue
|
||||
"tdma":"\033[35m", # Dark Magenta
|
||||
"tdcy":"\033[36m", # Dark Cyan
|
||||
"tdwh":"\033[37m", # Dark White
|
||||
|
||||
"tbbl":"\033[90m", # Bright Black
|
||||
"tbrd":"\033[91m", # Bright Red
|
||||
"tbgr":"\033[92m", # Bright Green
|
||||
"tbyl":"\033[93m", # Bright Yellow
|
||||
"tbbu":"\033[94m", # Bright Blue
|
||||
"tbma":"\033[95m", # Bright Magenta
|
||||
"tbcy":"\033[96m", # Bright Cyan
|
||||
"tbwh":"\033[97m", # Bright White
|
||||
# Background
|
||||
"bdbl":"\033[40m", # Dark Black
|
||||
"bdrd":"\033[41m", # Dark Red
|
||||
"bdgr":"\033[42m", # Dark Green
|
||||
"bdyl":"\033[43m", # Dark Yellow
|
||||
"bdbu":"\033[44m", # Dark Blue
|
||||
"bdma":"\033[45m", # Dark Magenta
|
||||
"bdcy":"\033[46m", # Dark Cyan
|
||||
"bdwh":"\033[47m", # Dark White
|
||||
|
||||
"bbbl":"\033[100m", # Bright Black
|
||||
"bbrd":"\033[101m", # Bright Red
|
||||
"bbgr":"\033[102m", # Bright Green
|
||||
"bbyl":"\033[103m", # Bright Yellow
|
||||
"bbbu":"\033[104m", # Bright Blue
|
||||
"bbma":"\033[105m", # Bright Magenta
|
||||
"bbcy":"\033[106m", # Bright Cyan
|
||||
"bbwh":"\033[108m" # Bright White
|
||||
}
|
||||
Reference in New Issue
Block a user