diff --git a/data/users_template.json b/data/users_template.json index 9965c0c..c819c06 100644 --- a/data/users_template.json +++ b/data/users_template.json @@ -1,24 +1,13 @@ { "key": "API_KEY", "online": { - "intervals": [ - { - "start": { - "from": 0.00, - "to": 0.00 - }, - "end": { - "from": 24.00, - "to": 24.00 - } - } - ] + "intervals": [] }, "actions": { "posts": { "public": { "percent": 0, - "cooldown": 86400 + "cooldown": 0 }, "specified": { "percent": { @@ -27,7 +16,7 @@ "neutral": 0, "enemies": 0 }, - "cooldown": 86400 + "cooldown": 0 } }, "replies": { diff --git a/generate.py b/generate.py new file mode 100644 index 0000000..09fd4ed --- /dev/null +++ b/generate.py @@ -0,0 +1,30 @@ +import sys +import json +import typing + +from src.User.User import User +from src.Generator.GenerateUser import GenerateUser +from src.Generator.GenerateRelationships import GenerateRelationships + +DEFAULT_GENERATE_USER_COUNT = 5 + +def generate_user() -> User: + while True: + user = GenerateUser() + print(f"1. Create username: {user.username}") + #user.set_api_key(input("2. Paste API key:")) + + yield user.autorun() + +def main(): + users = [] + + for i in (range(sys.argv[1] if len(sys.argv) > 1 else DEFAULT_GENERATE_USER_COUNT)): + print(f"\nGenerating user: {i + 1}") + users.append(next(generate_user())) + i += 1 + + GenerateRelationships(users).autorun() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/src/Dictionary/DictionaryParser.py b/src/Dictionary/DictionaryParser.py index e16cf69..3cc7701 100644 --- a/src/Dictionary/DictionaryParser.py +++ b/src/Dictionary/DictionaryParser.py @@ -16,25 +16,25 @@ class DictionaryParser(Dictionary): tokens = [token.lower() for token in note["text"].split(" ")] return set(tokens) - def get_tone(self) -> NoteTones: + def get_tone(self) -> NoteTones | None: words = self.get_words(Dictionaries.CONTROL)["tone"] if (self.tokens & set(words[NoteTones.INFORMAL.value])): return NoteTones.INFORMAL elif (self.tokens & set(words[NoteTones.FORMAL.value])): return NoteTones.FORMAL - return NoteTones.UNKNOWN + return None - def get_mood(self) -> NoteMoods: + def get_mood(self) -> NoteMoods | None: words = self.get_words(Dictionaries.CONTROL)["mood"] if (self.tokens & set(words[NoteMoods.FUNNY.value])): return NoteMoods.FUNNY elif (self.tokens & set(words[NoteMoods.DECENT.value])): return NoteMoods.DECENT elif (self.tokens & set(words[NoteMoods.ANNOYED.value])): return NoteMoods.ANNOYED - return NoteMoods.UNKNOWN + return None - def get_type(self) -> NoteTypes: + def get_type(self) -> NoteTypes | None: words = self.get_words(Dictionaries.CONTROL)["type"] if (self.tokens & set(words[NoteTypes.QUESTION.value])): return NoteTypes.QUESTION elif (self.tokens & set(words[NoteTypes.EXAGGERATED.value])): return NoteTypes.EXAGGERATED elif (self.tokens & set(words[NoteTypes.STATEMENT.value])): return NoteTypes.STATEMENT - return NoteTypes.UNKNOWN \ No newline at end of file + return None \ No newline at end of file diff --git a/src/Enums.py b/src/Enums.py index 9df50e9..deee485 100644 --- a/src/Enums.py +++ b/src/Enums.py @@ -1,8 +1,5 @@ from enum import Enum -class Traits(Enum): - UNKNOWN = None - class Errors(Enum): WORD_LIST_TOO_LONG = 0 WORD_LIST_HAS_DUPLICATES = 1 @@ -43,16 +40,13 @@ class ControlWords(Enum): class NoteTones(Enum): FORMAL = "formal" INFORMAL = "informal" - UNKNOWN = Traits.UNKNOWN.value class NoteMoods(Enum): FUNNY = "funny" DECENT = "decent" ANNOYED = "annoyed" - UNKNOWN = Traits.UNKNOWN.value class NoteTypes(Enum): QUESTION = "question" EXAGGERATED = "exaggerated" - STATEMENT = "statement" - UNKNOWN = Traits.UNKNOWN.value \ No newline at end of file + STATEMENT = "statement" \ No newline at end of file diff --git a/src/Generator/GenerateRelationships.py b/src/Generator/GenerateRelationships.py new file mode 100644 index 0000000..0ce7f36 --- /dev/null +++ b/src/Generator/GenerateRelationships.py @@ -0,0 +1,70 @@ +import typing +import random + +from ..User.User import USER_CONFIG_DIR, User + +CHANCE_PARTNER = 10 + +RECURSE_LIMIT = 10 + +class GenerateRelationships(): + def __init__(self, users: list = None): + self.users = users + + self.partners = [] + + # Compute diff between a provided target list of users against list of all users + def users_diff(self, target: list) -> list: + return list(set(self.users) - set(target)) + + def pick_random_user_from_list(self, target: list, ignore: User) -> User: + available = self.users_diff(target) + available.remove(ignore) + + return random.choice(available) + + def set_random_partner(self, user: User, i: int = 0) -> None: + # Give up trying to find a partner if we've reached the recurse depth limit + if (i >= RECURSE_LIMIT): + return None + + # Pick a random user + partner = self.pick_random_user_from_list(self.partners, user) + + # Don't partner up with this user if they're already a friend or enemy + if (partner in user.get_enemies() or partner in user.get_friends()): + return self.set_random_partner(user, i + 1) + + # Set partner's usernames in each other's configs + user.config["relationships"]["partner"] = partner.username + partner.config["relationships"]["partner"] = user.username + + # Mark users as partners + self.partners.extend((user, partner)) + + def set_random_friend(self, user: User, i: int = 0) -> None: + # Give up trying to find a friend if we've reached the recurse depth limit + if (i >= RECURSE_LIMIT): + return None + + # Get available friends (not already a friend, enemy, or self) + friend = self.pick_random_user_from_list(user.get_friends() + user.get_enemies(), user) + + if (friend == user.get_partner()): + return set_random_friend(user, i + 1) + + user.config["relationships"]["friends"].append(friend.username) + friend.config["relationships"]["friends"].append(user.username) + + return None + + def autorun(self) -> None: + for user in self.users: + # Find a random partner for user if they don't have one already and if roll is in bounds + if (not user.get_partner()): + self.set_random_partner(user) + + self.set_random_friend(user) + + return None + diff --git a/src/Generator/GenerateUser.py b/src/Generator/GenerateUser.py new file mode 100644 index 0000000..c3f2342 --- /dev/null +++ b/src/Generator/GenerateUser.py @@ -0,0 +1,107 @@ +import math +import json +import typing +import random +from pathlib import Path + +from ..User.User import USER_CONFIG_DIR, User +from ..Enums import RelationshipType, NoteTones, NoteMoods, NoteTypes + +from misskey.enum import NoteVisibility +from random_username.generate import generate_username + +# Use this file as a template for generated users +USER_TEMPLATE_FILE = Path.cwd() / "data" / "users_template.json" + +# Minimum and maximum cooldown time for posting new notes +CONFIG_POST_MIN_COOLDOWN = 300 # 10 minutes +CONFIG_POST_MAX_COOLDOWN = 259200 # 48 hours + +# Available preferred reactions +REACTIONS = [ + "❤", + "👍", + "😆" +] + +class GenerateUser(): + def __init__(self): + self.username = self.gen_username() + + # Load user template file + with open(USER_TEMPLATE_FILE, "r") as f: + self.config = json.load(f) + + # Return a random unique username + @staticmethod + def gen_username() -> str: + username = generate_username(1)[0] + return username if not (USER_CONFIG_DIR / f"{username}.json").exists() else GenerateUser.gen_username() + + # Generate a random time interval + @staticmethod + def gen_interval() -> dict: + def to_time(h: int) -> float: + return float(f"{h}.{random.randint(0, 59)}") + + start_hour_from = random.randint(0, 22) + start_hour_end = start_hour_from + 1 + + end_hour_from = random.randint(min(start_hour_end + 1, 22), 23) + end_hour_to = min(end_hour_from + 1, 23) + + return { + "start": { + "from": to_time(start_hour_from), + "to": to_time(start_hour_end) + }, + "end": { + "from": to_time(end_hour_from), + "to": to_time(end_hour_to) + } + } + + def set_api_key(self, key: str) -> None: + self.config["key"] = key + + def save(self) -> bool: + USER_CONFIG_DIR.mkdir(exist_ok=True) + + with open(USER_CONFIG_DIR / f"{self.username}.json", "w") as f: + json.dump(self.config, f, indent=4, ensure_ascii=False) + + def autorun(self) -> User: + # Generate a random online interval + self.config["online"]["intervals"].append(self.gen_interval()) + + # Set a random percent for user to post new notes + self.config["actions"]["posts"]["public"]["percent"] = random.randint(0, 100) + self.config["actions"]["posts"]["public"]["cooldown"] = random.randint(CONFIG_POST_MIN_COOLDOWN, CONFIG_POST_MAX_COOLDOWN) + + # Set random reply chance for each relationship + for visiblity in [NoteVisibility.PUBLIC, NoteVisibility.SPECIFIED]: + for relationship in RelationshipType: + self.config["actions"]["replies"][visiblity.value]["percent"][relationship.value] = random.randint(0, 100) + + # Set random react chances for each relationship + for relationship in RelationshipType: + self.config["actions"]["reacts"]["percent"][relationship.value] = random.randint(0, 100) + + # Set preferred reactions for each relationship + for relationship in RelationshipType: + self.config["actions"]["reacts"]["prefrerred_reaction"][relationship.value] = random.choice(REACTIONS) + + # Set random personality tone + for x in NoteTones: + self.config["personality"]["tone"]["probability"][x.value] = random.randint(0, 100) + + # Set random personality mood + for x in NoteMoods: + self.config["personality"]["mood"]["probability"][x.value] = random.randint(0, 100) + + # Set random personality type + for x in NoteTypes: + self.config["personality"]["type"]["probability"][x.value] = random.randint(0, 100) + + self.save() + return User(self.username) \ No newline at end of file diff --git a/src/Generator/__init__.py b/src/Generator/__init__.py new file mode 100644 index 0000000..e69de29