Pull out all strings the bot says to a class

Also implement a randomised wrapper
This commit is contained in:
Marc Di Luzio 2024-08-17 21:57:27 +01:00
parent 934c2e24fc
commit 125105469a
6 changed files with 150 additions and 53 deletions

View file

@ -12,6 +12,7 @@ import matchy.matching as matching
from matchy.state import State, AuthScope from matchy.state import State, AuthScope
import matchy.util as util import matchy.util as util
import matchy.state as state import matchy.state as state
import matchy.cogs.strings as strings
logger = logging.getLogger("cog") logger = logging.getLogger("cog")
@ -41,8 +42,8 @@ class MatcherCog(commands.Cog):
self.state.set_user_active_in_channel( self.state.set_user_active_in_channel(
interaction.user.id, interaction.channel.id) interaction.user.id, interaction.channel.id)
await interaction.response.send_message( await interaction.response.send_message(
f"Roger roger {interaction.user.mention}!\n" strings.acknowledgement(interaction.user.mention) + "\n"
+ f"Added you to {interaction.channel.mention}!", + strings.user_added(interaction.channel.mention),
ephemeral=True, silent=True) ephemeral=True, silent=True)
@app_commands.command(description="Leave the matchees for this channel") @app_commands.command(description="Leave the matchees for this channel")
@ -54,7 +55,7 @@ class MatcherCog(commands.Cog):
self.state.set_user_active_in_channel( self.state.set_user_active_in_channel(
interaction.user.id, interaction.channel.id, False) interaction.user.id, interaction.channel.id, False)
await interaction.response.send_message( await interaction.response.send_message(
f"No worries {interaction.user.mention}. Come back soon :)", ephemeral=True, silent=True) strings.user_leave(interaction.user.mention), ephemeral=True, silent=True)
@app_commands.command(description="Pause your matching in this channel for a number of days") @app_commands.command(description="Pause your matching in this channel for a number of days")
@commands.guild_only() @commands.guild_only()
@ -69,8 +70,8 @@ class MatcherCog(commands.Cog):
self.state.set_user_paused_in_channel( self.state.set_user_paused_in_channel(
interaction.user.id, interaction.channel.id, until) interaction.user.id, interaction.channel.id, until)
await interaction.response.send_message( await interaction.response.send_message(
f"Sure thing {interaction.user.mention}!\n" strings.acknowledgement(interaction.user.mention) + "\n"
+ f"Paused you until {util.datetime_as_discord_time(until)}!", + strings.paused(until),
ephemeral=True, silent=True) ephemeral=True, silent=True)
@app_commands.command(description="List the matchees for this channel") @app_commands.command(description="List the matchees for this channel")
@ -86,23 +87,19 @@ class MatcherCog(commands.Cog):
if matchees: if matchees:
mentions = [m.mention for m in matchees] mentions = [m.mention for m in matchees]
msg += f"There are {len(matchees)} active matchees:\n" msg += strings.active_matchees(mentions) + "\n"
msg += f"{util.format_list(mentions)}\n"
if paused: if paused:
mentions = [m.mention for m in paused] mentions = [m.mention for m in paused]
msg += f"\nThere are {len(mentions)} paused matchees:\n" msg += "\n" + strings.paused_matchees(mentions) + "\n"
msg += f"{util.format_list([m.mention for m in paused])}\n"
tasks = self.state.get_channel_match_tasks(interaction.channel.id) tasks = self.state.get_channel_match_tasks(interaction.channel.id)
for (day, hour, min) in tasks: for (day, hour, min) in tasks:
next_run = util.get_next_datetime(day, hour) next_run = util.get_next_datetime(day, hour)
date_str = util.datetime_as_discord_time(next_run) msg += "\n" + strings.scheduled(next_run, min) + "\n"
msg += f"\nA match is scheduled at {date_str}"
msg += f" with {min} members per group\n"
if not msg: if not msg:
msg = "There are no matchees in this channel and no scheduled matches :(" msg = strings.no_scheduled()
await interaction.response.send_message(msg, ephemeral=True, silent=True) await interaction.response.send_message(msg, ephemeral=True, silent=True)
@ -129,7 +126,7 @@ class MatcherCog(commands.Cog):
# Bail if not a matcher # Bail if not a matcher
if not self.state.get_user_has_scope(interaction.user.id, AuthScope.MATCHER): if not self.state.get_user_has_scope(interaction.user.id, AuthScope.MATCHER):
await interaction.response.send_message("You'll need the 'matcher' scope to schedule a match", await interaction.response.send_message(strings.need_matcher_scope(),
ephemeral=True, silent=True) ephemeral=True, silent=True)
return return
@ -141,10 +138,9 @@ class MatcherCog(commands.Cog):
logger.info("Scheduled new match task in %s with min %s weekday %s hour %s", logger.info("Scheduled new match task in %s with min %s weekday %s hour %s",
channel_id, members_min, weekday, hour) channel_id, members_min, weekday, hour)
next_run = util.get_next_datetime(weekday, hour) next_run = util.get_next_datetime(weekday, hour)
date_str = util.datetime_as_discord_time(next_run)
await interaction.response.send_message( await interaction.response.send_message(
f"Done :) Next run will be at {date_str}", strings.scheduled_success(next_run),
ephemeral=True, silent=True) ephemeral=True, silent=True)
@app_commands.command(description="Cancel all scheduled matches in this channel") @app_commands.command(description="Cancel all scheduled matches in this channel")
@ -153,7 +149,7 @@ class MatcherCog(commands.Cog):
"""Cancel scheduled matches in this channel""" """Cancel scheduled matches in this channel"""
# Bail if not a matcher # Bail if not a matcher
if not self.state.get_user_has_scope(interaction.user.id, AuthScope.MATCHER): if not self.state.get_user_has_scope(interaction.user.id, AuthScope.MATCHER):
await interaction.response.send_message("You'll need the 'matcher' scope to remove scheduled matches", await interaction.response.send_message(strings.need_matcher_scope(),
ephemeral=True, silent=True) ephemeral=True, silent=True)
return return
@ -162,8 +158,7 @@ class MatcherCog(commands.Cog):
self.state.remove_channel_match_tasks(channel_id) self.state.remove_channel_match_tasks(channel_id)
await interaction.response.send_message( await interaction.response.send_message(
"Done, all scheduled matches cleared in this channel!", strings.cancelled(), ephemeral=True, silent=True)
ephemeral=True, silent=True)
@app_commands.command(description="Match up matchees") @app_commands.command(description="Match up matchees")
@commands.guild_only() @commands.guild_only()
@ -185,24 +180,23 @@ class MatcherCog(commands.Cog):
# Let the user know when there's nobody to match # Let the user know when there's nobody to match
if not groups: if not groups:
await interaction.response.send_message("Nobody to match up :(", ephemeral=True, silent=True) await interaction.response.send_message(strings.nobody_to_match(), ephemeral=True, silent=True)
return return
# Post about all the groups with a button to send to the channel # Post about all the groups with a button to send to the channel
groups_list = '\n'.join( groups_list = '\n'.join(
", ".join([m.mention for m in g]) for g in groups) ", ".join([m.mention for m in g]) for g in groups)
msg = f"Roger! I've generated example groups for ya:\n\n{groups_list}" msg = strings.generated_groups(groups_list)
view = discord.utils.MISSING view = discord.utils.MISSING
if self.state.get_user_has_scope(interaction.user.id, AuthScope.MATCHER): if self.state.get_user_has_scope(interaction.user.id, AuthScope.MATCHER):
# Otherwise set up the button # Otherwise set up the button
msg += "\n\nClick the button to match up groups and send them to the channel.\n" msg += "\n\n" + strings.click_to_match() + "\n"
view = discord.ui.View(timeout=None) view = discord.ui.View(timeout=None)
view.add_item(DynamicGroupButton(members_min)) view.add_item(DynamicGroupButton(members_min))
else: else:
# Let a non-matcher know why they don't have the button # Let a non-matcher know why they don't have the button
msg += f"\n\nYou'll need the {AuthScope.MATCHER}" msg += "\n\n" + strings.need_matcher_to_post()
msg += " scope to post this to the channel, sorry!"
await interaction.response.send_message(msg, ephemeral=True, silent=True, view=view) await interaction.response.send_message(msg, ephemeral=True, silent=True, view=view)
@ -215,13 +209,12 @@ class MatcherCog(commands.Cog):
for (channel, min) in self.state.get_active_match_tasks(): for (channel, min) in self.state.get_active_match_tasks():
logger.info("Scheduled match task triggered in %s", channel) logger.info("Scheduled match task triggered in %s", channel)
msg_channel = self.bot.get_channel(int(channel)) msg_channel = self.bot.get_channel(int(channel))
await matching.match_groups_in_channel(self.state, msg_channel, min) await match_groups_in_channel(self.state, msg_channel, min)
for (channel, _) in self.state.get_active_match_tasks(datetime.now() + timedelta(days=1)): for (channel, _) in self.state.get_active_match_tasks(datetime.now() + timedelta(days=1)):
logger.info("Reminding about scheduled task in %s", channel) logger.info("Reminding about scheduled task in %s", channel)
msg_channel = self.bot.get_channel(int(channel)) msg_channel = self.bot.get_channel(int(channel))
await msg_channel.send("Arf arf! just a reminder I'll be doin a matcherino in here in T-24hrs!" await msg_channel.send(strings.reminder())
+ "\nUse /join if you haven't already, or /pause if you want to skip a week :)")
# Increment when adjusting the custom_id so we don't confuse old users # Increment when adjusting the custom_id so we don't confuse old users
@ -260,7 +253,30 @@ class DynamicGroupButton(discord.ui.DynamicItem[discord.ui.Button],
intrctn.guild.name, intrctn.channel.name) intrctn.guild.name, intrctn.channel.name)
# Let the user know we've recieved the message # Let the user know we've recieved the message
await intrctn.response.send_message(content="Matchy is matching matchees...", ephemeral=True) await intrctn.response.send_message(content=strings.matching(), ephemeral=True)
# Perform the match # Perform the match
await matching.match_groups_in_channel(self.state, intrctn.channel, self.min) await match_groups_in_channel(self.state, intrctn.channel, self.min)
async def match_groups_in_channel(state: State, channel: discord.channel, min: int):
"""Match up the groups in a given channel"""
groups = matching.active_members_to_groups(state, channel, min)
# Send the groups
for group in groups:
message = await channel.send(
f"Matched up {util.format_list([m.mention for m in group])}!")
# Set up a thread for this match if the bot has permissions to do so
if channel.permissions_for(channel.guild.me).create_public_threads:
await channel.create_thread(
name=util.format_list([m.display_name for m in group]),
message=message,
reason="Creating a matching thread")
# Close off with a message
await channel.send(strings.matching_done())
# Save the groups to the history
state.log_groups(groups)
logger.info("Done! Matched into %s groups.", len(groups))

View file

@ -52,4 +52,4 @@ class OwnerCog(commands.Cog):
logger.info("Granting user %s matcher scope", user) logger.info("Granting user %s matcher scope", user)
await ctx.reply("Done!", ephemeral=True) await ctx.reply("Done!", ephemeral=True)
else: else:
await ctx.reply("Likely not a user...", ephemeral=True) await ctx.reply(f"{user} is not a user?", ephemeral=True)

83
matchy/cogs/strings.py Normal file
View file

@ -0,0 +1,83 @@
"""
All the possible strings for things that matchy can say
Some can be selected randomly to give the bot some flavor
"""
from matchy.util import randomised, datetime_as_discord_time, format_list
from matchy.state import AuthScope
# Acknowledge something
@randomised
def acknowledgement(m): return [
f"Roger roger {m}!",
f"Sure thing {m}!"
]
def user_added(c):
return f"Added you to {c}!"
def user_leave(m):
return f"No worries {m}. Come back soon :)"
def paused(t):
return f"Paused you until {datetime_as_discord_time(t)}!"
def active_matchees(ms):
return f"There are {len(ms)} active matchees:\n{format_list(ms)}"
def paused_matchees(ms):
return f"There are {len(ms)} paused matchees:\n{format_list(ms)}"
def scheduled(next, n):
return f"A match is scheduled at {datetime_as_discord_time(next)} with {n} members per group\n"
def no_scheduled():
return "There are no matchees in this channel and no scheduled matches :("
def need_matcher_scope():
return "You'll need the 'matcher' scope to do this"
def scheduled_success(d):
return f"Done :) Next run will be at {datetime_as_discord_time(d)}"
def cancelled():
return "Done, all scheduled matches cleared in this channel!"
def nobody_to_match():
return "Nobody to match up :("
def generated_groups(g):
return "Roger! I've generated example groups for ya:\n\n{g}"
def click_to_match():
return "Click the button to match up groups and send them to the channel."
def need_matcher_to_post():
return f"You'll need the {AuthScope.MATCHER} scope to post this to the channel, sorry!"
def reminder():
return """Arf arf! just a reminder I'll be doin a matcherino in here in T-24hrs!
Use /join if you haven't already, or /pause if you want to skip a week :)"""
def matching():
return "Matchy is matching matchees..."
def matching_done():
return "That's all folks, happy matching and remember - DFTBA!"

View file

@ -179,29 +179,6 @@ def members_to_groups(matchees: list[Member],
assert False assert False
async def match_groups_in_channel(state: State, channel: discord.channel, min: int):
"""Match up the groups in a given channel"""
groups = active_members_to_groups(state, channel, min)
# Send the groups
for group in groups:
message = await channel.send(
f"Matched up {util.format_list([m.mention for m in group])}!")
# Set up a thread for this match if the bot has permissions to do so
if channel.permissions_for(channel.guild.me).create_public_threads:
await channel.create_thread(
name=util.format_list([m.display_name for m in group]),
message=message,
reason="Creating a matching thread")
# Close off with a message
await channel.send("That's all folks, happy matching and remember - DFTBA!")
# Save the groups to the history
state.log_groups(groups)
logger.info("Done! Matched into %s groups.", len(groups))
def get_matchees_in_channel(state: State, channel: discord.channel): def get_matchees_in_channel(state: State, channel: discord.channel):
"""Fetches the matchees in a channel""" """Fetches the matchees in a channel"""
# Reactivate any unpaused users # Reactivate any unpaused users

View file

@ -1,5 +1,6 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
from functools import reduce from functools import reduce
import random
def get_day_with_suffix(day): def get_day_with_suffix(day):
@ -59,3 +60,11 @@ def set_nested_value(d, *keys, value=None):
d[leaf] = value d[leaf] = value
elif leaf in d: elif leaf in d:
del d[leaf] del d[leaf]
def randomised(func):
"Randomise which in a list we actually return"
def wrapper(*args, **kwargs):
vals = func(*args, **kwargs)
return random.choice(vals) if isinstance(vals, list) else vals
return wrapper

View file

@ -38,3 +38,15 @@ def test_set_nested_dict_value():
} }
util.set_nested_value(d, "x", "y", "z", "val", value=52) util.set_nested_value(d, "x", "y", "z", "val", value=52)
assert 52 == util.get_nested_value(d, "x", "y", "z", "val") assert 52 == util.get_nested_value(d, "x", "y", "z", "val")
def test_randomized():
def string():
return "foo"
def list():
return ["foo", "bar"]
assert util.randomised(string)() == "foo"
assert util.randomised(list)() in list()