boards/util.py

207 lines
9.0 KiB
Python

from bottle import request
from skcode.tags.internal import HardNewlineTreeNode as NewLineTreeNode
from cheroot.ssl.builtin import BuiltinSSLAdapter
from skcode import parse_skcode, render_to_html
from bottle import ServerAdapter
from cheroot import wsgi
import datetime
import ssl
import os
import re
from config import boards, default_user, secret, limit
from config import root, cert_path, key_path
from templates import board_file, thread_file, board_box, signin_link, account
from templates import board_post, thread_post, random_post
# BBCODE PARSER
def render(text):
return render_to_html(parse_skcode(text,newline_node_cls=NewLineTreeNode))
# DEFINITIONS
def setup(db, board=None):
db.execute("CREATE TABLE IF NOT EXISTS BOARDS (ID INTEGER PRIMARY KEY,NAME TEXT UNIQUE, DESCRIPTION TEXT,LAST REAL,HIDDEN INTEGER, FIXED INTEGER DEFAULT 0 );")
db.execute(f"CREATE TABLE IF NOT EXISTS POSTS (ID INTEGER PRIMARY KEY, SUBJECT TEXT, AUTHOR TEXT, COMMENT TEXT, TIME REAL,LAST REAL , BOARD TEXT, POST INTEGER );")
db.execute(f"CREATE TABLE IF NOT EXISTS USERS (ID INTEGER PRIMARY KEY, USERNAME TEXT UNIQUE, TIME REAL, PASSWORD TEXT, PAGE TEXT );")
db.execute(f"CREATE TABLE IF NOT EXISTS SESSIONS (USERNAME TEXT UNIQUE, TIME REAL, KEY TEXT);")
db.execute(f"CREATE TABLE IF NOT EXISTS FILES (ID INTEGER PRIMARY KEY, NAME TEXT,POST INTEGER, TYPE TEXT , FILE BLOB, HIDDEN INTEGER);")
db.execute(f"CREATE TABLE IF NOT EXISTS THUMBS (ID INTEGER PRIMARY KEY, NAME TEXT UNIQUE, TYPE TEXT, FILE BLOB);")
for b,d in boards.items():
hide = 0
if "nsfw" in b or "nsfw" in d.lower() :
hide = 1
db.execute("INSERT INTO BOARDS (NAME,DESCRIPTION,HIDDEN,FIXED) VALUES (?,?,?,?) ON CONFLICT(NAME) DO UPDATE SET DESCRIPTION = ?;",[b,d,hide,1,d])
if board:
hide = 0
if "nsfw" in board:
hide = 1
db.execute("INSERT OR IGNORE INTO BOARDS (NAME,HIDDEN) VALUES (?,?);",[board,hide])
def get_user(db):
session_id = request.get_cookie("session_id", secret=secret)
if session_id:
row = db.execute("SELECT * FROM SESSIONS WHERE KEY = ? ;",[session_id]).fetchone()
return row['USERNAME'] if row else None
def get_description(db, board):
return db.execute(f"SELECT DESCRIPTION FROM BOARDS WHERE NAME = ?;",[board]).fetchone()['description'] or ""
def get_title(db,id):
return db.execute(f"SELECT * FROM POSTS WHERE ID = ?;",[id]).fetchone()['SUBJECT']
def user_link(user):
if user == default_user:
return f'<b>Anonymous</b>'
return f'<a href="/u/{user}"><b>{user}</b></a>'
def ddate():
return os.popen('ddate').read()
def header(db, user=None):
b = []
for board in db.execute("SELECT * FROM BOARDS WHERE FIXED = 1 AND HIDDEN != 1;"):
b.append(f"<a style='text-align:center;' href='/.{board['name']}'>{board['name'].upper()} </a>")
for board in db.execute("SELECT * FROM BOARDS WHERE (lAST IS NOT NULL AND FIXED = 0) AND HIDDEN != 1 ORDER BY LAST DESC LIMIT 4;"):
b.append(f"<a style='text-align:center;' href='/.{board['name']}'>{board['name'].upper()} </a>")
return " | ".join(b)
def motd():
return f"No greyfaces allowed! {ddate()}"
def generate_file(file,board=None,checked=False):
t = file["TYPE"].split("/")
fname = file["NAME"]
if t[0] == "image":
src = f'/files/{fname}'
else:
src = f'/thumbs/{fname}'
if board:
return board_file.render(board=board,src=src)
else:
name = file["NAME"]
return thread_file.render(name=name,src=src,checked=checked)
def get_login(user):
return account.render(user=user) if user else signin_link
def get_boards(db):
brds = ""
for b in db.execute("SELECT * FROM BOARDS WHERE LAST IS NOT NULL AND HIDDEN != 1;"):
format = {}
board = b["NAME"]
format['board'] = board
format['threads'] = db.execute("SELECT COUNT(*) FROM POSTS WHERE BOARD = ? AND (ID = POST);",[board]).fetchone()[0]
format['description'] = b["DESCRIPTION"] or ""
brds += board_box.render(**format)
return brds
def get_files(db,t,page=1):
page = page-1
files = ""
tlist = ", ".join(f"'{n}'" for n in t)
for file in db.execute(f"""SELECT DISTINCT NAME, TYPE FROM FILES WHERE TYPE IN ({tlist}) AND HIDDEN != 1 AND NAME NOT IN ( SELECT DISTINCT NAME FROM FILES WHERE TYPE IN ({tlist}) AND HIDDEN != 1 ORDER BY ID DESC LIMIT ?) ORDER BY ID DESC LIMIT ?;""",[limit*1.5*page,limit*1.5]):
files+=generate_file(file)
return files
def get_nav(db, board, id=None, page=1,t=[]):
if id:
count = db.execute("SELECT COUNT(*) FROM POSTS WHERE POST = ?",[id]).fetchone()[0]
pages = []
m = (count % limit)
p = (count - m) / limit
p = int(p) + (1 if m != 0 else 0)
for i in range(1,p+1):
pages.append(f'<a href="/.{board}/thread/{id}/page/{i}">[{i}]</a>')
return f'<div class="nav">{" | ".join(pages)}</div>'
else:
if board == "gallery":
count = db.execute(f"""SELECT COUNT(DISTINCT NAME) FROM FILES WHERE TYPE IN ({", ".join(f"'{n}'" for n in t)});""").fetchone()[0]
m = (count % ((limit * 1.5)))
p = (count - m) / (limit * 1.5)
# p = int(p) + (1 if m != 0 else 0)
elif board == "library":
count = db.execute(f"""SELECT COUNT(DISTINCT NAME) FROM FILES WHERE TYPE IN ({", ".join(f"'{n}'" for n in t)});""").fetchone()[0]
m = (count % (limit * 1.5))
p = (count - m) / (limit * 1.5)
# p = int(p) + (1 if m != 0 else 0)
else:
count = db.execute("SELECT COUNT(*) FROM POSTS WHERE BOARD = ? AND POST = ID;",[board]).fetchone()[0]
m = (count % limit)
p = (count - m) / limit
# p = int(p) + (1 if m != 0 else 0)
pages = []
p = int(p) + (1 if m != 0 else 0)
for i in range(1,p+1):
pages.append(f'<a href="/.{board}/page/{i}">[{i}]</a>')
return f'<div class="nav">{" | ".join(pages)}</div>'
def get_random(db):
posts = ""
for post in db.execute(f"SELECT * FROM POSTS WHERE ID = POST AND BOARD NOT IN ( SELECT NAME FROM BOARDS WHERE HIDDEN = 1) ORDER BY RANDOM() LIMIT ?;",[limit]):
dt = datetime.datetime.fromtimestamp(post['time'])
date = dt.strftime('%d %m %Y')
time = dt.strftime("%H:%M:%S")
sddate = os.popen(f"ddate +'%d/%b/%Y' {date}").read()
files = ""
for file in db.execute("SELECT * FROM FILES WHERE POST = ? LIMIT 1;",[post["ID"]]):
files+=generate_file(file,board=post["BOARD"])
posts += random_post.render(id=post["ID"],board=post["BOARD"],subject=post["SUBJECT"],author=user_link(post["AUTHOR"]),date=sddate,time=time,files=files)
return posts
def get_posts(db,board=None,page=1,id=None):
page = page-1
posts = ""
if id:
for post in db.execute(f"SELECT * FROM POSTS WHERE POST = ? AND ID NOT IN ( SELECT ID FROM POSTS WHERE POST = ? LIMIT ?) LIMIT ?;",[id,id,limit * page,limit]):
dt = datetime.datetime.fromtimestamp(post['time'])
date = dt.strftime('%d %m %y')
time = dt.strftime("%H:%M:%S")
sddate = os.popen(f"ddate +'%d/%b/%Y YOLD' {date}").read()
files = ""
first = True
for file in db.execute("SELECT * FROM FILES WHERE POST = ?;",[post["ID"]]):
files+=generate_file(file,checked=first)
if first:
first = False
comment = render(post['COMMENT'] or "")
posts += thread_post.render(id=post["ID"],author=user_link(post["AUTHOR"]),sddate=sddate,time=time,board=board,post=post["POST"],files=files,comment=comment)
elif board:
for post in db.execute("""
SELECT * FROM POSTS
WHERE (ID = POST AND BOARD = ?)
AND
ID NOT IN ( SELECT ID FROM POSTS WHERE ID = POST AND BOARD = ? ORDER BY LAST DESC LIMIT ? )
ORDER BY LAST DESC LIMIT ?;
""", [board, board, limit * page, limit]):
dt = datetime.datetime.fromtimestamp(post['time'])
date = dt.strftime('%d %m %Y')
time = dt.strftime("%H:%M:%S")
sddate = os.popen(f"ddate +'%d/%b/%Y' {date}").read()
files = ""
for file in db.execute("SELECT * FROM FILES WHERE POST = ? LIMIT 1;",[post["ID"]]):
files+=generate_file(file,board=board)
posts += board_post.render(id=post["ID"],board=post["BOARD"],subject=post["SUBJECT"],author=user_link(post["AUTHOR"]),date=sddate,time=time,files=files)
return posts
def get_style():
return open(f"{root}/css/style.css").read()
check_user = re.compile("^[a-zA-Z][a-zA-Z0-9-_\.]{1,23}$")
class SSLServer(ServerAdapter):
def run(self, handler):
server = wsgi.Server((self.host, self.port), handler)
server.ssl_adapter = BuiltinSSLAdapter(cert_path, key_path)
server.ssl_adapter.context.options |= ssl.OP_NO_TLSv1
server.ssl_adapter.context.options |= ssl.OP_NO_TLSv1_1
try:
server.start()
finally:
server.stop()