import feedparser import json from markdownify import markdownify from mastodon import Mastodon import re import sys import time def process_html(html: str) -> str: return re.sub(r"\n{3,}", "\n\n", re.sub(r"\\(\W)", r"\1", re.sub(r"^(\s*)\* ", r"\1- ", markdownify(html), 0, re.MULTILINE))).strip() def split_notes(text: str, limit: int) -> list[str]: if len(text) <= limit: return [text] notes = [] prev = 0 while len(text[prev:prev + limit]) >= limit: sub = text[prev:prev + limit] i = sub.rfind("\n") if i == -1: i = sub.rfind(" ") notes.append(sub[:i].strip()) prev += i + 1 return notes def post_note(client: Mastodon, content: str, in_reply_to_id: str | None) -> str: note = client.status_post(content, in_reply_to_id, visibility="unlisted") print("Posted:", note["url"]) return note["id"] def post_notes(client: Mastodon, notes: list[str]): ids = [] for note in notes: in_reply_to_id = ids[-1] if len(ids) != 0 else None posted_note = post_note(client, note, in_reply_to_id) ids.append(posted_note) if __name__ == "__main__": if len(sys.argv) < 3: print("Usage:", sys.argv[0], " ") exit(1) account = sys.argv[1] config_path = sys.argv[2] client = Mastodon(access_token=f"accounts/{account}.secret") creds = client.account_verify_credentials() print(f"Logged in:", creds["display_name"], "(@" + creds["fqn"] + ")") instance = client.instance() char_limit = instance["configuration"]["statuses"]["max_characters"] config_file = open(config_path, "r") config = json.load(config_file) config_file.close() for url in config.keys(): print("Updating", url) rss = feedparser.parse(url) rss.entries.sort(key=lambda entry: entry.published_parsed) if config[url] is None: config[url] = 0.0 if time.mktime(rss.entries[-1].published_parsed) <= config[url]: continue for entry in rss.entries: if time.mktime(entry.published_parsed) <= config[url]: continue notes = split_notes("[" + entry.title + "](" + entry.link + ")\n" + process_html( entry.content[0].value), char_limit) post_notes(client, notes) config[url] = time.mktime(rss.entries[-1].published_parsed) print("Saving config", config) config_file = open(config_path, "w") json.dump(config, config_file, indent=4) config_file.close()