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
This commit is contained in:
Yuki Joou 2023-02-26 02:11:42 +01:00
parent 406d3b5c8f
commit 95139026e5
3 changed files with 175 additions and 1 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
token token
data.json

51
data_manager.py Normal file
View file

@ -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()

122
main.py
View file

@ -4,6 +4,8 @@ import discord
import json import json
import random import random
import data_manager
intents = discord.Intents.default() intents = discord.Intents.default()
intents.message_content = True intents.message_content = True
@ -35,12 +37,132 @@ boop - Boops target user
Slash commands: Slash commands:
``` ```
/help - Shows this message /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 "<something went wrong :>"
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 "<something went wrong :>"
await interaction.response.send_message(
f"{mention} didn't wanna get married yet..."
)
self.marriage_accepted = False
self.stop()
# Slash commands # 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() @bot.slash_command()
async def help(ctx: discord.ApplicationContext): async def help(ctx: discord.ApplicationContext):
await ctx.respond(HELP_MESSAGE) await ctx.respond(HELP_MESSAGE)