New algorithm

master
Thor 3 years ago
parent d54ca7be51
commit 3c71d99c7c
  1. 184
      portbot.py

@ -4,19 +4,22 @@ import time
import toml
import random
import re
from datetime import datetime, timedelta, timezone
import sched
import math
import string
from datetime import datetime, timedelta, timezone
from mastodon import Mastodon, MastodonNotFoundError
from fedbot.bot import Bot, BotClient
POST_INTERVAL = timedelta(seconds = 15)
TEST = False
TEST = "test" in sys.argv[1:]
PORT_PCT = 67
def next_dt():
dt = datetime.now(timezone.utc)
dt -= timedelta(hours = -1,
minutes = dt.minute,
dt -= timedelta(hours = 0,
minutes = (dt.minute % 15) - 15,
seconds = dt.second,
microseconds = dt.microsecond)
return dt
@ -28,107 +31,126 @@ loaded_config = {
**toml.load(config_path)}
SUFFIXES = [
['ly'],
['ing'],
['[bdklmptw]?est$'],
['[^ious]s$'],
['ted'],
['[ei]$', 'ty']]
'ly$',
'ing$',
'[bdklmptw]?est$',
'[^ious]s$',
'ted$',
'[ei]ty$']
def is_suffixed(word):
for suffix in SUFFIXES:
if len(suffix) > len(word):
continue
return any(re.fullmatch(suf, word) for suf in SUFFIXES)
syllables = list(zip(suffix, word[-len(suffix):]))
if all(re.fullmatch(suf, syl) for suf, syl in syllables):
#print(word, "matched", suffix)
return True
def overlap_words(left_word, right_word):
if left_word == right_word:
return None
return False
offset = 2
attempts = []
while offset + 2 <= len(left_word):
if right_word.lower().startswith(left_word.lower()[offset : offset + 2]):
attempts.append(left_word[:offset] + right_word)
#break
offset += 1
offset = len(right_word) - 2
while offset >= 0:
if left_word.lower().endswith(right_word.lower()[offset : offset + 2]):
attempts.append(left_word + right_word[offset + 2:])
#break
offset -= 1
attempts = sorted(attempts, key = lambda w: len(w), reverse = True)
if len(attempts) == 0:
return None
return pick_one_word(attempts)
def word_weight(index, length, power = 2):
a = pow((index + 1) / length, 2)
return int(100000 * a)
def pick_one_word(words, power = 2, max_len = 12):
words = list(filter(lambda w: len(w) <= max_len, words))
if len(words) == 0:
return None
weights = [word_weight(i, len(words), power = power) for i in range(0, len(words))]
return random.choices(words, weights = weights)[0]
class WordMaker:
def __init__(self):
print("Loading dictionaries")
illegal = set(ch for ch in (string.ascii_uppercase + string.punctuation + string.digits + string.whitespace))
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
lines = [l.strip() for l in f.readlines()]
lines = filter(lambda w: len(w) > 0 and not any(ch in illegal for ch in w), lines)
words = [l.replace("", "") for l in lines]
self.all_words = words
words = list(set(sorted(words, key = lambda w: len(w), reverse = True)))
self.first_words = list(filter(lambda w: not is_suffixed(w), words))
self.plain_words = ["".join(w).lower() for w in words]
self.next_words = 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
words = list(filter(lambda l: len(l) > 0, lines))
self.all_words = list(set(sorted([w.lower() for w in [*self.all_words, *words]], key = lambda w: len(w), reverse = True)))
self.port_words = list(set(sorted(words, key = lambda w: len(w), reverse = True)))
def extend_word2(self, prev_word):
port_dict = random.randint(0, 100) < PORT_PCT
if port_dict:
next_dict = self.port_words
else:
second_dict = self.words
next_dict = self.next_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)
new_words = [overlap_words(prev_word, w) for w in next_dict if overlap_words(prev_word, w)]
while len(new_words) > 0:
new_word = pick_one_word(new_words, power = 2 if port_dict else 4)
if not new_word:
return None
new_words.remove(new_word)
if new_word.lower() not in self.all_words:
return new_word
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
port_dict = random.randint(0, 100) < PORT_PCT
if port_dict:
words = self.port_words
else:
words = self.first_words
while True:
while True:
word = self.get_one_word(words)
word = pick_one_word(words, power = 2 if port_dict else 4)
times = target_times
while times > 0:
next_word = self.get_second_word(word)
if next_word is None:
ext_word = self.extend_word2(word)
if ext_word is None:
break
word = next_word
word = ext_word
times -= 1
if times == 0:
break
word_str = "".join(word)
if len(word_str) < 15:
if len(word) < 15:
break
print(word_str)
word = word.lower()
print(word)
return word_str
return word
def get_portmanteaus(self, count = 10):
return [self.get_portmanteau() for x in range(0, count)]
@ -141,7 +163,6 @@ class PortBotClient(BotClient):
"retry_rate": 60,
"poll_interval": 15,
**config}
super().__init__(bot, config)
@ -159,28 +180,29 @@ class PortBotClient(BotClient):
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
#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
def post():
for client_name, client in bot.clients.items():
words = wm.get_portmanteaus(1)
words = wm.get_portmanteaus(3)
print()
if random.randint(0, 100) <= 100:
visibility = "public"
else:
visibility = "unlisted"
dt = next_dt()
if not TEST:
client.api.status_post("\n".join(words), visibility = visibility)
dt = next_dt()
print("Scheduling at", dt)
print("Scheduling at", dt)
if TEST:
scheduler.enter(1, 1, post)
else:
@ -196,9 +218,9 @@ bot.start()
print("Running")
dt = next_dt()
print("Scheduling at", dt)
if TEST:
scheduler.enter(1, 1, post)
else:
print("Scheduling at", dt)
scheduler.enterabs(dt.timestamp(), 1, post)
scheduler.run()

Loading…
Cancel
Save