Portmanteau bot for Mastodon
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

204 lines
5.9 KiB

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()