From 95139026e5ebf42ce69eccd425cb24c999968389 Mon Sep 17 00:00:00 2001 From: Yuki Joou Date: Sun, 26 Feb 2023 02:11:42 +0100 Subject: [PATCH] main+data_manager: Started work on marriage system This commit adds a data_manager submodule that handles reading/writing data. For now it's backed by a single json file, but in the future we should move to a sqlite3 database The marriage system uses Discord Interactions and views, and works for basic things TODO: - Poly marriage support - Listing marriages through a command --- .gitignore | 1 + data_manager.py | 51 ++++++++++++++++++++ main.py | 124 +++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 data_manager.py diff --git a/.gitignore b/.gitignore index 8d865be..89bc45a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ token +data.json diff --git a/data_manager.py b/data_manager.py new file mode 100644 index 0000000..e1ea63e --- /dev/null +++ b/data_manager.py @@ -0,0 +1,51 @@ +# TODO: If you need to grow this class, consider moving to a sqlite3 database! + +import io +import json + +DATA_PATH = "data.json" + + +def __load_json_or_make_empty_object(fp: io.TextIOWrapper): + data = {} + fp.seek(0) + try: + data = json.load(fp) + except ValueError as exception: + print(exception, DATA_PATH, "wasn't a JSON file, making it one...") + # fp doesn't point to a file with valid json, replacing the content with "{}" + fp.seek(0) + fp.write("{}") + fp.truncate() + fp.flush() + return {} + return data + + +def get_data() -> dict: + with open(DATA_PATH, 'a+') as fp: + return __load_json_or_make_empty_object(fp) + + +# Helper class for writing to the data file in a sage way +class DataWriter: + def __init__(self) -> None: + self.__data_fp: io.TextIOWrapper | None = None + + def __enter__(self): + self.__data_fp = open(DATA_PATH, 'w+') + return self + + def __exit__(self, *_): + if self.__data_fp is not None: + self.__data_fp.close() + + # Set a new value to the data + def set_data(self, json_data: dict): + if self.__data_fp is None: + return + + self.__data_fp.seek(0) + json.dump(json_data, self.__data_fp) + self.__data_fp.truncate() + self.__data_fp.flush() diff --git a/main.py b/main.py index 2738c81..4cb0f63 100755 --- a/main.py +++ b/main.py @@ -4,6 +4,8 @@ import discord import json import random +import data_manager + intents = discord.Intents.default() intents.message_content = True @@ -20,7 +22,7 @@ with open("lines.json") as file: HELP_MESSAGE = """ Gif commands: ``` -hug - Hugs target user +hug - Hugs target user kiss - Kisses target user cuddle - Cuddles with target user hold - Hold target user's hand @@ -35,12 +37,132 @@ boop - Boops target user Slash commands: ``` /help - Shows this message +/marry - Marries someone ``` """ + +def __find_mariage_for_member_id(member_id: int) -> list[int]: + data = data_manager.get_data() + if "marriages" not in data: + data["marriages"] = [] + with data_manager.DataWriter() as writer: + writer.set_data(data) + return [] + + for marriage in data["marriages"]: + if member_id in marriage: + return marriage + + return [] + +# Interraction views + + +class MariageConfirmationView(discord.ui.View): + def __init__(self, target: discord.Member): + super().__init__() + self.timeout = None + self.marriage_accepted: bool | None = None + self.target = target + + @discord.ui.button(label="Accept", style=discord.ButtonStyle.green, row=1) + async def accept(self, _: discord.ui.Button, interaction: discord.Interaction): + if interaction.user != self.target: + print(interaction.user, self.target) + await interaction.response.send_message( + "ur not the one getting married, silly :3" + ) + return + + user_who_replied = interaction.user + mention = user_who_replied.mention \ + if user_who_replied is not None else "" + await interaction.response.send_message( + f"{mention} accepted the proposal :3 lovely" + ) + + self.marriage_accepted = True + self.stop() + + @discord.ui.button(label="Deny", style=discord.ButtonStyle.red, row=1) + async def deny(self, _: discord.ui.Button, interaction: discord.Interaction): + if interaction.user != self.target: + await interaction.response.send_message( + "ur not the one getting married, silly :3" + ) + return + + user_who_replied = interaction.user + mention = user_who_replied.mention \ + if user_who_replied is not None else "" + + await interaction.response.send_message( + f"{mention} didn't wanna get married yet..." + ) + + self.marriage_accepted = False + self.stop() + # Slash commands +@bot.slash_command() +@discord.guild_only() +@discord.option( + "target", type=discord.Member, + description="Which user to marry", + required=False, +) +async def marry( + context: discord.ApplicationContext, member_to_marry: discord.Member +): + # Check if the person asked in mariage is interested + marriage_asker = context.author + if marriage_asker is None: + await context.respond("ow :/ something went wonky wonky, try again!") + return + + marriage_confirmation = MariageConfirmationView(member_to_marry) + await context.respond( + f"{member_to_marry.mention}, would you like to marry" + + f" {marriage_asker.mention}?", view=marriage_confirmation) + + await marriage_confirmation.wait() + if marriage_confirmation.marriage_accepted is None: + await context.respond("silly little bug going on :3 try again l8er :3") + return + + if marriage_confirmation.marriage_accepted == False: + await context.respond( + "no consent == no marriage! consent is key to a happy life :3" + ) + + # Marriage was accepted, yay :3! + askers_marriage = __find_mariage_for_member_id(marriage_asker.id) + askees_marriage = __find_mariage_for_member_id(member_to_marry.id) + + # Now check for polycules + if len(askees_marriage) == 0 and len(askers_marriage) == 0: + # No polycules, just update the records to marry the two :3 + data = data_manager.get_data() + data["marriages"].append([marriage_asker.id, member_to_marry.id]) + with data_manager.DataWriter() as writer: + writer.set_data(data) + + await context.respond( + f"{marriage_asker.mention} married {member_to_marry.mention}" + ) + + return + + await context.respond( + "ooh looks like you're trying to do a ploly marriage..." + + " unfortunately, that's still a work in progress," + + " come back later to register it :3" + ) + + @bot.slash_command() async def help(ctx: discord.ApplicationContext): await ctx.respond(HELP_MESSAGE)