from os import getenv from urllib.parse import urljoin from html import unescape from textwrap import wrap as text_wrap import pituophis from pituophis import Item, Request from requests import get from parse import parse from bs4 import BeautifulSoup from unidecode import unidecode wordpress_url = getenv("URL") handlers = {} formatters = {} def wrap(text): return "\n".join(text_wrap(text, width=int(getenv("WIDTH", 80)))) def register_handler(path: str): def decorator_handler(func): handlers[path] = func return func return decorator_handler def register_formatter(func): formatters[func.__name__] = func return func @register_formatter def h1(tag): return f"\n === {tag.get_text()} === \n" @register_formatter def h2(tag): return f"\n == {tag.get_text()} == \n" @register_formatter def h3(tag): return f"\n = {tag.get_text()} = \n" @register_formatter def p(tag): return tag.get_text() @register_formatter def img(tag): # TODO: Use image to ascii here if "alt" in tag: return f"" return "" def format_post(post): post = post[0] header = f""" ** {post["title"]["rendered"]} ** """ # TODO: Hit the API to get the author's name # {post["date"]} - {post["author"]} # TODO: Nicer date formatting soup = BeautifulSoup(post["content"]["rendered"], features="html.parser") tags = soup.find_all(formatters.keys()) return header + "\n".join( wrap(formatters[tag.name](tag)) for tag in tags ) @register_handler("/post/{slug}") def post(request: Request, slug: str): post = get(urljoin(wordpress_url, f"wp-json/wp/v2/posts?slug={slug}")).json() return format_post(post) @register_handler("/page/{slug}") def post(request: Request, slug: str): page = get(urljoin(wordpress_url, f"wp-json/wp/v2/pages?slug={slug}")).json() return format_post(page) def format_excerpt(post): text = BeautifulSoup(post["excerpt"]["rendered"], features="html.parser").get_text() cutoff = text.find("…") fudge_factor = 100 # Deal with the weird [html] link generated by the penny toys article return wrap(unidecode(text[:cutoff - fudge_factor] + "...")) def handle(request): print(f"path: {request.path}") for path, handler in handlers.items(): parse_result = parse(path, request.path) if parse_result is not None: return handler(request, **parse_result.named) menu = [getenv("HEADER_TEXT")] posts = get(urljoin(wordpress_url, "wp-json/wp/v2/posts?filter[posts_per_page]=-1")).json() #pages = get(urljoin(wordpress_url, "wp-json/wp/v2/pages?filter[posts_per_page]=-1")).json() for heading, items, url in ( #("Pages", pages, "/page/"), ("Posts", posts, "/post/"), ): menu.append(f"<==={heading}===>") for post in items: menu.append(Item( itype=0, path=f"{url}{post['slug']}", text=unidecode(unescape(post['title']['rendered'])), host=request.host, port=request.port) ) menu.append(format_excerpt(post)) return menu if __name__ == '__main__': pituophis.serve(getenv("HOST","127.0.0.1"), int(getenv("PORT")), handler=handle)