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.
 

201 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)
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 word: len(word) > 0 and not re.search(r'[- A-Z]', word), 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 word: not is_suffixed(word), words))
self.plain_words = ["".join(word) for word in words]
with open("porthyph.txt", "r") as f:
lines = [line.strip() for line in f.readlines()]
words = [line.split("=") for line in lines]
words = sorted(words, key = lambda w: len(w), reverse = True)
self.alt_words = words
self.plain_words.append(["".join(word) for word 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].lower()
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) or first_end.startswith(w[0].lower()), second_dict)
else:
second_iter = filter(lambda w: w[0].lower().startswith(first_end), second_dict)
second_words = list(second_iter)
#if len(second_words) < 8:
# return None
while len(second_words) > 0:
second_word_orig = self.get_one_word(second_words)
second_word = [s.lower() for s in second_word_orig]
second_words.remove(second_word_orig)
word = [*first_word[:-1], *second_word]
if not "".join(word) 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)
end = word[-1]
times = target_times
while times > 0:
next_word = self.get_second_word(word)
if next_word is None:
break
else:
word = next_word
times -= 1
if times > 0:
continue
else:
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"
client.api.status_post("\n".join(words), visibility = visibility)
dt = next_dt()
print("Scheduling at", dt)
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)
scheduler.enterabs(dt.timestamp(), 1, post)
scheduler.run()