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.

205 lines
5.9 KiB

3 years ago
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
3 years ago
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)
3 years ago
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]
3 years ago
with open("porthyph.txt", "r") as f:
lines = [line.strip() for line in f.readlines()]
lines = filter(lambda l: len(l) > 0, lines)
3 years ago
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])
3 years ago
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]
3 years ago
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)
3 years ago
else:
second_iter = filter(lambda w: w[0].lower().startswith(first_end.lower()), second_dict)
3 years ago
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]
3 years ago
word = [*first_word[:-1], *second_word]
if not "".join(word).lower() in self.plain_words:
3 years ago
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:
3 years ago
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)
3 years ago
dt = next_dt()
print("Scheduling at", dt)
if TEST:
scheduler.enter(1, 1, post)
else:
scheduler.enterabs(dt.timestamp(), 1, post)
3 years ago
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)
3 years ago
scheduler.run()