import os import sys import time import toml import random import re from datetime import datetime, timedelta, timezone import sched from mastodon import Mastodon, MastodonNotFoundError from fedbot.bot import Bot, BotClient POST_INTERVAL = timedelta(seconds = 15) TEST = False def next_dt(): dt = datetime.now(timezone.utc) dt -= timedelta(hours = -1, minutes = dt.minute, seconds = dt.second, microseconds = dt.microsecond) return dt config_path = os.path.join(os.path.dirname(sys.argv[0]), "config.toml") loaded_config = { "name": "portmanteaubot", **toml.load(config_path)} SUFFIXES = [ ['ly'], ['ing'], ['[bdklmptw]?est$'], ['[^ious]s$'], ['ted'], ['[ei]$', 'ty']] def is_suffixed(word): for suffix in SUFFIXES: if len(suffix) > len(word): continue syllables = list(zip(suffix, word[-len(suffix):])) if all(re.fullmatch(suf, syl) for suf, syl in syllables): #print(word, "matched", suffix) return True return False class WordMaker: def __init__(self): print("Loading dictionaries") with open ("mhyph.txt", "r", encoding = "mac-roman") as f: lines = [line.strip() for line in f.readlines()] lines = filter(lambda w: len(w) > 0 and not re.search(r'[- A-Z]', w), lines) words = [line.split("•") for line in lines] words = sorted(words, key = lambda w: len(w), reverse = True) self.words = words self.first_words = list(filter(lambda w: not is_suffixed(w), words)) self.plain_words = ["".join(w).lower() for w in words] with open("porthyph.txt", "r") as f: lines = [line.strip() for line in f.readlines()] lines = filter(lambda l: len(l) > 0, lines) words = [line.split("=") for line in lines] words = sorted(words, key = lambda w: len(w), reverse = True) self.alt_words = words self.plain_words.extend(["".join(w).lower() for w in words]) def get_one_word(self, words): weights = [int(100.0 * (x + 1.0) / len(words)) for x in range(0, len(words))] return random.choices(words, weights = weights)[0] def get_second_word(self, first_word): first_word = list(first_word) first_end = first_word[-1] if random.randint(0, 100) < 50: second_dict = self.alt_words else: second_dict = self.words if random.randint(0, 100) < 50: second_iter = filter(lambda w: w[0].lower().startswith(first_end.lower()) or first_end.lower().startswith(w[0].lower()), second_dict) else: second_iter = filter(lambda w: w[0].lower().startswith(first_end.lower()), second_dict) second_words = list(second_iter) while len(second_words) > 0: second_word_orig = self.get_one_word(second_words) second_words.remove(second_word_orig) second_word = [s.lower() for s in second_word_orig] word = [*first_word[:-1], *second_word] if not "".join(word).lower() in self.plain_words: return word return None def get_portmanteau(self): target_times = 1 if random.randint(0, 100) > 50: words = self.alt_words else: words = self.first_words while True: while True: word = self.get_one_word(words) times = target_times while times > 0: next_word = self.get_second_word(word) if next_word is None: break word = next_word times -= 1 if times == 0: break word_str = "".join(word) if len(word_str) < 15: break print(word_str) return word_str def get_portmanteaus(self, count = 10): return [self.get_portmanteau() for x in range(0, count)] class PortBotClient(BotClient): def __init__(self, bot, config): config = { "app_name": "PortmanteuBot", "rate_limit": 3, "retry_rate": 60, "poll_interval": 15, **config} super().__init__(bot, config) self.my_id = None def on_start(self): self.log("Starting") self.my_id = self.api.me()["id"] pass def on_poll(self): pass def on_status(self, status): if status["account"]["id"] != self.my_id: return if status["created_at"] < datetime.now(timezone.utc) - timedelta(hours = 24) and status["reblogs_count"] == 0 and status["favourites_count"] == 0: try: print("Deleting", status["created_at"], status["content"]) self.api.status_delete(status["id"]) time.sleep(2) except MastodonNotFoundError: pass pass def post(): for client_name, client in bot.clients.items(): words = wm.get_portmanteaus(1) if random.randint(0, 100) <= 100: visibility = "public" else: visibility = "unlisted" if not TEST: client.api.status_post("\n".join(words), visibility = visibility) dt = next_dt() print("Scheduling at", dt) if TEST: scheduler.enter(1, 1, post) else: scheduler.enterabs(dt.timestamp(), 1, post) wm = WordMaker() scheduler = sched.scheduler(timefunc = time.time, delayfunc = time.sleep) bot = Bot(PortBotClient, loaded_config) del loaded_config bot.start() print("Running") dt = next_dt() print("Scheduling at", dt) if TEST: scheduler.enter(1, 1, post) else: scheduler.enterabs(dt.timestamp(), 1, post) scheduler.run()