Fix /leave not working for anyone who's paused in the past
We now clear the re-activate value when a user is unpaused. Added bonus here is various bits of refactor and cleanup, with some tests
This commit is contained in:
parent
946de66f52
commit
f926a36069
5 changed files with 91 additions and 37 deletions
|
@ -79,18 +79,30 @@ class MatcherCog(commands.Cog):
|
|||
logger.info("Handling /list command in %s %s from %s",
|
||||
interaction.guild.name, interaction.channel, interaction.user.name)
|
||||
|
||||
matchees = matching.get_matchees_in_channel(
|
||||
(matchees, paused) = matching.get_matchees_in_channel(
|
||||
self.state, interaction.channel)
|
||||
mentions = [m.mention for m in matchees]
|
||||
msg = "Current matchees in this channel:\n" + \
|
||||
f"{util.format_list(mentions)}"
|
||||
|
||||
msg = ""
|
||||
|
||||
if matchees:
|
||||
mentions = [m.mention for m in matchees]
|
||||
msg += f"There are {len(matchees)} active matchees:\n"
|
||||
msg += f"{util.format_list(mentions)}\n"
|
||||
|
||||
if paused:
|
||||
mentions = [m.mention for m in paused]
|
||||
msg += f"\nThere are {len(mentions)} paused matchees:\n"
|
||||
msg += f"{util.format_list([m.mention for m in paused])}\n"
|
||||
|
||||
tasks = self.state.get_channel_match_tasks(interaction.channel.id)
|
||||
for (day, hour, min) in tasks:
|
||||
next_run = util.get_next_datetime(day, hour)
|
||||
date_str = util.datetime_as_discord_time(next_run)
|
||||
msg += f"\nNext scheduled at {date_str}"
|
||||
msg += f" with {min} members per group"
|
||||
msg += f"\nA match is scheduled at {date_str}"
|
||||
msg += f" with {min} members per group\n"
|
||||
|
||||
if not msg:
|
||||
msg = "There are no matchees in this channel and no scheduled matches :("
|
||||
|
||||
await interaction.response.send_message(msg, ephemeral=True, silent=True)
|
||||
|
||||
|
|
|
@ -207,7 +207,9 @@ def get_matchees_in_channel(state: State, channel: discord.channel):
|
|||
# Reactivate any unpaused users
|
||||
state.reactivate_users(channel.id)
|
||||
# Gather up the prospective matchees
|
||||
return [m for m in channel.members if state.get_user_active_in_channel(m.id, channel.id)]
|
||||
active = [m for m in channel.members if state.get_user_active_in_channel(m.id, channel.id)]
|
||||
paused = [m for m in channel.members if state.get_user_paused_in_channel(m.id, channel.id)]
|
||||
return (active, paused)
|
||||
|
||||
|
||||
def active_members_to_groups(state: State, channel: discord.channel, min_members: int):
|
||||
|
|
|
@ -10,6 +10,7 @@ import pathlib
|
|||
import copy
|
||||
import logging
|
||||
from functools import wraps
|
||||
import matchy.util as util
|
||||
|
||||
logger = logging.getLogger("state")
|
||||
logger.setLevel(logging.INFO)
|
||||
|
@ -275,40 +276,42 @@ class State():
|
|||
Check if a user has an auth scope
|
||||
"owner" users have all scopes
|
||||
"""
|
||||
user = self._users.get(str(id), {})
|
||||
scopes = user.get(_Key.SCOPES, [])
|
||||
scopes = util.get_nested_value(
|
||||
self._users, str(id), _Key.SCOPES, default=[])
|
||||
return scope in scopes
|
||||
|
||||
@safe_write
|
||||
def set_user_active_in_channel(self, id: str, channel_id: str, active: bool = True):
|
||||
"""Set a user as active (or not) on a given channel"""
|
||||
self._set_user_channel_prop(id, channel_id, _Key.ACTIVE, active)
|
||||
util.set_nested_value(
|
||||
self._users, str(id), _Key.CHANNELS, str(channel_id), _Key.ACTIVE, value=active)
|
||||
util.set_nested_value(
|
||||
self._users, str(id), _Key.CHANNELS, str(channel_id), _Key.REACTIVATE, value=None)
|
||||
|
||||
def get_user_active_in_channel(self, id: str, channel_id: str) -> bool:
|
||||
"""Get a users active channels"""
|
||||
user = self._users.get(str(id), {})
|
||||
channels = user.get(_Key.CHANNELS, {})
|
||||
return str(channel_id) in [channel for (channel, props) in channels.items() if props.get(_Key.ACTIVE, False)]
|
||||
"""Get a if a user is active in a channel"""
|
||||
return util.get_nested_value(self._users, str(id), _Key.CHANNELS, str(channel_id), _Key.ACTIVE)
|
||||
|
||||
def get_user_paused_in_channel(self, id: str, channel_id: str) -> str:
|
||||
"""Get a the user reactivate time if it exists"""
|
||||
return util.get_nested_value(self._users, str(id), _Key.CHANNELS, str(channel_id), _Key.REACTIVATE)
|
||||
|
||||
@safe_write
|
||||
def set_user_paused_in_channel(self, id: str, channel_id: str, until: datetime):
|
||||
"""Sets a user as paused in a channel"""
|
||||
# Deactivate the user in the channel first
|
||||
self.set_user_active_in_channel(id, channel_id, False)
|
||||
|
||||
self._set_user_channel_prop(
|
||||
id, channel_id, _Key.REACTIVATE, datetime_to_ts(until))
|
||||
"""Sets a user as inactive in a channel with a reactivation time"""
|
||||
util.set_nested_value(
|
||||
self._users, str(id), _Key.CHANNELS, str(channel_id), _Key.ACTIVE, value=False)
|
||||
util.set_nested_value(
|
||||
self._users, str(id), _Key.CHANNELS, str(channel_id), _Key.REACTIVATE, value=datetime_to_ts(until))
|
||||
|
||||
@safe_write
|
||||
def reactivate_users(self, channel_id: str):
|
||||
"""Reactivate any users who've passed their reactivation time on this channel"""
|
||||
for user in self._users.values():
|
||||
channels = user.get(_Key.CHANNELS, {})
|
||||
channel = channels.get(str(channel_id), {})
|
||||
if channel and not channel[_Key.ACTIVE]:
|
||||
reactivate = channel.get(_Key.REACTIVATE, None)
|
||||
# Check if we've gone past the reactivation time and re-activate
|
||||
if reactivate and datetime.now() > ts_to_datetime(reactivate):
|
||||
channel[_Key.ACTIVE] = True
|
||||
del channel[_Key.REACTIVATE]
|
||||
for user in self._users:
|
||||
reactivate = self.get_user_paused_in_channel(
|
||||
str(user), str(channel_id))
|
||||
if reactivate and datetime.now() > ts_to_datetime(reactivate):
|
||||
self.set_user_active_in_channel(str(user), str(channel_id))
|
||||
|
||||
def get_active_match_tasks(self, time: datetime | None = None) -> Generator[str, int]:
|
||||
"""
|
||||
|
@ -376,14 +379,6 @@ class State():
|
|||
def _tasks(self) -> dict[str]:
|
||||
return self._dict[_Key.TASKS]
|
||||
|
||||
@safe_write
|
||||
def _set_user_channel_prop(self, id: str, channel_id: str, key: str, value):
|
||||
"""Set a user channel property helper"""
|
||||
user = self._users.setdefault(str(id), {})
|
||||
channels = user.setdefault(_Key.CHANNELS, {})
|
||||
channel = channels.setdefault(str(channel_id), {})
|
||||
channel[key] = value
|
||||
|
||||
|
||||
def load_from_file(file: str) -> State:
|
||||
"""
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from datetime import datetime, timedelta
|
||||
from functools import reduce
|
||||
|
||||
|
||||
def get_day_with_suffix(day):
|
||||
|
@ -42,3 +43,19 @@ def iterate_all_shifts(list: list):
|
|||
for _ in range(len(list)-1):
|
||||
list = list[1:] + [list[0]]
|
||||
yield list
|
||||
|
||||
|
||||
def get_nested_value(d, *keys, default=None):
|
||||
"""Helper method for walking down an optional set of nested dicts to get a value"""
|
||||
return reduce(lambda d, key: d.get(key, {}), keys, d) or default
|
||||
|
||||
|
||||
def set_nested_value(d, *keys, value=None):
|
||||
"""Helper method for walking down an optional set of nested dicts to set a value"""
|
||||
for key in keys[:-1]:
|
||||
d = d.setdefault(key, {})
|
||||
leaf = keys[-1]
|
||||
if value is not None:
|
||||
d[leaf] = value
|
||||
elif leaf in d:
|
||||
del d[leaf]
|
||||
|
|
|
@ -10,3 +10,31 @@ def test_iterate_all_shifts():
|
|||
[3, 4, 1, 2],
|
||||
[4, 1, 2, 3],
|
||||
]
|
||||
|
||||
|
||||
def test_get_nested_dict_value():
|
||||
d = {
|
||||
"x": {
|
||||
"y": {
|
||||
"z": {
|
||||
"val": 42
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert 42 == util.get_nested_value(d, "x", "y", "z", "val")
|
||||
assert 16 == util.get_nested_value(d, "x", "y", "z", "vol", default=16)
|
||||
|
||||
|
||||
def test_set_nested_dict_value():
|
||||
d = {
|
||||
"x": {
|
||||
"y": {
|
||||
"z": {
|
||||
"val": 42
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
util.set_nested_value(d, "x", "y", "z", "val", value=52)
|
||||
assert 52 == util.get_nested_value(d, "x", "y", "z", "val")
|
||||
|
|
Loading…
Add table
Reference in a new issue