4 Commits

9 changed files with 226 additions and 58 deletions

View File

@@ -287,13 +287,14 @@ CREATE TABLE tasklist(
deleted_at TIMESTAMPTZ,
completed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ,
last_updated_at TIMESTAMPTZ
last_updated_at TIMESTAMPTZ,
duration INTEGER
);
CREATE INDEX tasklist_users ON tasklist (userid);
ALTER TABLE tasklist
ADD CONSTRAINT fk_tasklist_users
FOREIGN KEY (userid)
REFERENCES user_config (userid)
REFERENCES user_profiles (profileid)
ON DELETE CASCADE
NOT VALID;
ALTER TABLE tasklist
@@ -317,6 +318,20 @@ CREATE TABLE tasklist_reward_history(
reward_count INTEGER
);
CREATE INDEX tasklist_reward_history_users ON tasklist_reward_history (userid, reward_time);
CREATE TABLE tasklist_current(
taskid INTEGER PRIMARY KEY REFERENCES tasklist (taskid) ON DELETE CASCADE ON UPDATE CASCADE,
started_at TIMESTAMPTZ NOT NULL
);
CREATE TABLE tasklist_planner(
taskid INTEGER PRIMARY KEY REFERENCES tasklist (taskid) ON DELETE CASCADE ON UPDATE CASCADE,
sortkey INTEGER
);
-- }}}
-- Reminder data {{{

Submodule src/gui updated: 40bc140355...62d2484914

View File

@@ -92,6 +92,10 @@ class LionBot(Bot):
def core(self):
return self.get_cog('CoreCog')
@property
def profiles(self):
return self.get_cog('ProfileCog')
async def _handle_global_dispatch(self, event_name: str, *args, **kwargs):
self.dispatch(event_name, *args, **kwargs)

View File

@@ -13,6 +13,8 @@ if TYPE_CHECKING:
from core.lion_member import LionMember
from core.lion_user import LionUser
from core.lion_guild import LionGuild
from modules.profiles.profile import UserProfile
from modules.profiles.community import Community
logger = logging.getLogger(__name__)
@@ -54,6 +56,8 @@ class LionContext(Context['LionBot']):
lguild: 'LionGuild'
lmember: 'LionMember'
alion: 'LionUser | LionMember'
profile: 'UserProfile'
community: 'Community'
def __repr__(self):
parts = {}

View File

@@ -40,6 +40,17 @@ class ProfileCog(LionCog):
async def cog_check(self, ctx):
return True
async def bot_check_once(self, ctx: LionContext):
"""
Inject the contextual UserProfile and Community into the LionContext.
Creates the profile and community if they do not exist.
"""
if ctx.guild:
ctx.community = await self.fetch_community_discord(ctx.guild)
ctx.profile = await self.fetch_profile_discord(ctx.author)
return True
# Profile API
def add_profile_migrator(self, migrator, name=None):
name = name or migrator.__name__

View File

@@ -7,9 +7,12 @@ from discord.ext import commands as cmds
from discord import app_commands as appcmds
from discord.app_commands.transformers import AppCommandOptionType as cmdopt
from data.queries import JOINTYPE
from meta import LionBot, LionCog, LionContext
from meta.CrocBot import CrocBot
from meta.logger import log_wrap
from meta.errors import UserInputError
from modules.profiles.profile import UserProfile
from utils.lib import utc_now, error_embed
from utils.ui import ChoicedEnum, Transformed, AButton
@@ -126,6 +129,7 @@ class TasklistCog(LionCog):
def __init__(self, bot: LionBot):
self.bot = bot
self.crocbot: CrocBot = bot.crocbot
self.data = bot.db.load_registry(TasklistData())
self.babel = babel
self.settings = TasklistSettings()
@@ -138,10 +142,84 @@ class TasklistCog(LionCog):
self.bot.core.guild_config.register_model_setting(self.settings.task_reward_limit)
self.bot.add_view(TasklistCaller(self.bot))
self.bot.profiles.add_profile_migrator(self.migrate_profiles, name='tasklist-migrator')
configcog = self.bot.get_cog('ConfigCog')
self.crossload_group(self.configure_group, configcog.config_group)
@LionCog.listener('on_tasks_completed')
self._load_twitch_methods(self.crocbot)
async def cog_unload(self):
self.live_tasklists.clear()
if profiles := self.bot.get_cog('ProfileCog'):
profiles.del_profile_migrator('tasklist-migrator')
self._unload_twitch_methods(self.crocbot)
@log_wrap(action="Tasklist Profile Migration")
async def migrate_profiles(self, source_profile: UserProfile, target_profile: UserProfile):
"""
Re-assign all tasklist tasks from source profile to target profile.
TODO: Probably wants some elegant handling of the cached or running tasklists.
"""
results = ["(Tasklist)"]
sourceid = source_profile.profileid
targetid = target_profile.profileid
updated = await self.data.Task.table.update_where(userid=sourceid).set(userid=targetid)
if updated:
results.append(
f"Migrated {len(updated)} task row(s) from source profile."
)
for channel_lists in self.live_tasklists.get(sourceid, []):
for tasklist in list(channel_lists.values()):
await tasklist.close()
self.bot.dispatch('tasklist_update', profileid=targetid, summon=False)
else:
results.append(
"No tasks found in source profile, nothing to migrate!"
)
return ' '.join(results)
async def user_profile_migration(self):
"""
Manual one-shot migration method from old Discord userids to the new profileids.
"""
# First collect all the distinct userids from the tasklist
# Then create a map of userids to profileids, creating the profiles if required
# Then do updates, we can just inefficiently do updates on each distinct userid
# As long as the userids and profileids never overlap, this is fine. Fine for a one-shot
# Extract all the userids that exist in the table
rows = await self.data.Task.table.select_where().select(
userid="DISTINCT(userid)"
).with_no_adapter()
# Fetch or create discord user profiles for them
profile_map = {}
for row in rows:
userid = row['userid']
if userid > 100000:
# Assume a Discord snowflake
profile = await UserProfile.fetch_from_discordid(self.bot, userid)
if not profile:
try:
user = self.bot.get_user(userid)
if user is None:
user = await self.bot.fetch_user(userid)
except discord.HTTPException:
logger.info(f"Skipping user {userid}")
continue
profile = await UserProfile.create_from_discord(self.bot, user)
profile_map[userid] = profile
# Now iterate through
for userid, profile in profile_map.items():
logger.info(f"Migrating userid {userid} to profile {profile}")
await self.data.Task.table.update_where(userid=userid).set(userid=profile.profileid)
# Temporarily disabling integration with userid driven Economy
# @LionCog.listener('on_tasks_completed')
@log_wrap(action="reward tasks completed")
async def reward_tasks_completed(self, member: discord.Member, *taskids: int):
async with self.bot.db.connection() as conn:
@@ -170,6 +248,9 @@ class TasklistCog(LionCog):
)
async def is_tasklist_channel(self, channel) -> bool:
"""
Check whether a given Discord channel is a tasklist channel
"""
if not channel.guild:
return True
channels = (await self.settings.tasklist_channels.get(channel.guild.id)).value
@@ -186,12 +267,16 @@ class TasklistCog(LionCog):
return (channel in channels) or (channel.id in private_channels) or (channel.category in channels)
async def call_tasklist(self, interaction: discord.Interaction):
"""
Given a Discord channel interaction, summon the interacting user's tasklist.
"""
await interaction.response.defer(thinking=True, ephemeral=True)
channel = interaction.channel
guild = channel.guild
userid = interaction.user.id
profile = await self.bot.profiles.fetch_profile_discord(interaction.user)
profileid = profile.profileid
tasklist = await Tasklist.fetch(self.bot, self.data, userid)
tasklist = await Tasklist.fetch(self.bot, self.data, profileid)
if await self.is_tasklist_channel(channel):
# Check we have permissions to send a regular message here
@@ -213,7 +298,7 @@ class TasklistCog(LionCog):
)
await interaction.edit_original_response(embed=error)
else:
tasklistui = TasklistUI.fetch(tasklist, channel, guild, timeout=None)
tasklistui = TasklistUI.fetch(tasklist, channel, guild, caller=interaction.user, timeout=None)
await tasklistui.summon(force=True)
await interaction.delete_original_response()
else:
@@ -222,14 +307,14 @@ class TasklistCog(LionCog):
await tasklistui.run(interaction)
@LionCog.listener('on_tasklist_update')
async def update_listening_tasklists(self, userid, channel=None, summon=True):
async def update_listening_tasklists(self, profileid, channel=None, summon=True):
"""
Propagate a tasklist update to all persistent tasklist UIs for this user.
If channel is given, also summons the UI if the channel is a tasklist channel.
"""
# Do the given channel first, and summon if requested
if channel and (tui := TasklistUI._live_[userid].get(channel.id, None)) is not None:
if channel and (tui := TasklistUI._live_[profileid].get(channel.id, None)) is not None:
try:
if summon and await self.is_tasklist_channel(channel):
await tui.summon()
@@ -240,7 +325,7 @@ class TasklistCog(LionCog):
await tui.close()
# Now do the rest of the listening channels
listening = TasklistUI._live_[userid]
listening = TasklistUI._live_[profileid]
for cid, ui in list(listening.items()):
if channel and channel.id == cid:
# We already did this channel
@@ -275,7 +360,7 @@ class TasklistCog(LionCog):
async def tasklist_group(self, ctx: LionContext):
raise NotImplementedError
async def _task_acmpl(self, userid: int, partial: str, multi=False) -> list[appcmds.Choice]:
async def _task_acmpl(self, profileid: int, partial: str, multi=False) -> list[appcmds.Choice]:
"""
Generate a list of task Choices matching a given partial string.
@@ -284,7 +369,7 @@ class TasklistCog(LionCog):
t = self.bot.translator.t
# Should usually be cached, so this won't trigger repetitive db access
tasklist = await Tasklist.fetch(self.bot, self.data, userid)
tasklist = await Tasklist.fetch(self.bot, self.data, profileid)
# Special case for an empty tasklist
if not tasklist.tasklist:
@@ -392,13 +477,17 @@ class TasklistCog(LionCog):
"""
Shared autocomplete for single task parameters.
"""
return await self._task_acmpl(interaction.user.id, partial, multi=False)
profile = await self.bot.profiles.fetch_profile_discord(interaction.user)
profileid = profile.profileid
return await self._task_acmpl(profileid, partial, multi=False)
async def tasks_acmpl(self, interaction: discord.Interaction, partial: str) -> list[appcmds.Choice]:
"""
Shared autocomplete for multiple task parameters.
"""
return await self._task_acmpl(interaction.user.id, partial, multi=True)
profile = await self.bot.profiles.fetch_profile_discord(interaction.user)
profileid = profile.profileid
return await self._task_acmpl(profileid, partial, multi=True)
@tasklist_group.command(
name=_p('cmd:tasks_new', "new"),
@@ -422,7 +511,7 @@ class TasklistCog(LionCog):
if not ctx.interaction:
return
tasklist = await Tasklist.fetch(self.bot, self.data, ctx.author.id)
tasklist = await Tasklist.fetch(self.bot, self.data, ctx.profile.profileid)
await ctx.interaction.response.defer(thinking=True, ephemeral=True)
# Fetch parent task if required
@@ -453,9 +542,9 @@ class TasklistCog(LionCog):
)
await ctx.interaction.edit_original_response(
embed=embed,
view=None if ctx.channel.id in TasklistUI._live_[ctx.author.id] else TasklistCaller(self.bot)
view=None if ctx.channel.id in TasklistUI._live_[ctx.profile.profileid] else TasklistCaller(self.bot)
)
self.bot.dispatch('tasklist_update', userid=ctx.author.id, channel=ctx.channel)
self.bot.dispatch('tasklist_update', profileid=ctx.profile.profileid, channel=ctx.channel)
tasklist_new_cmd.autocomplete('parent')(task_acmpl)
@@ -523,7 +612,7 @@ class TasklistCog(LionCog):
raise UserInputError(error)
# Contents successfully parsed, update the tasklist.
tasklist = await Tasklist.fetch(self.bot, self.data, ctx.author.id)
tasklist = await Tasklist.fetch(self.bot, self.data, ctx.profile.profileid)
taskinfo = tasklist.parse_tasklist(lines)
@@ -572,9 +661,9 @@ class TasklistCog(LionCog):
)
await ctx.interaction.edit_original_response(
embed=embed,
view=None if ctx.channel.id in TasklistUI._live_[ctx.author.id] else TasklistCaller(self.bot)
view=None if ctx.channel.id in TasklistUI._live_[ctx.profile.profileid] else TasklistCaller(self.bot)
)
self.bot.dispatch('tasklist_update', userid=ctx.author.id, channel=ctx.channel)
self.bot.dispatch('tasklist_update', profileid=ctx.profile.profileid, channel=ctx.channel)
@tasklist_group.command(
name=_p('cmd:tasks_edit', "edit"),
@@ -600,7 +689,7 @@ class TasklistCog(LionCog):
t = self.bot.translator.t
if not ctx.interaction:
return
tasklist = await Tasklist.fetch(self.bot, self.data, ctx.author.id)
tasklist = await Tasklist.fetch(self.bot, self.data, ctx.profile.profileid)
# Fetch task to edit
tid = tasklist.parse_label(taskstr) if taskstr else None
@@ -651,12 +740,12 @@ class TasklistCog(LionCog):
await interaction.response.send_message(
embed=embed,
view=(
discord.utils.MISSING if ctx.channel.id in TasklistUI._live_[ctx.author.id]
discord.utils.MISSING if ctx.channel.id in TasklistUI._live_[ctx.profile.profileid]
else TasklistCaller(self.bot)
),
ephemeral=True
)
self.bot.dispatch('tasklist_update', userid=ctx.author.id, channel=ctx.channel)
self.bot.dispatch('tasklist_update', profileid=ctx.profile.profileid, channel=ctx.channel)
if new_content or new_parent:
# Manual edit route
@@ -688,17 +777,17 @@ class TasklistCog(LionCog):
async def tasklist_clear_cmd(self, ctx: LionContext):
t = ctx.bot.translator.t
tasklist = await Tasklist.fetch(self.bot, self.data, ctx.author.id)
tasklist = await Tasklist.fetch(self.bot, self.data, ctx.profile.profileid)
await tasklist.update_tasklist(deleted_at=utc_now())
await ctx.reply(
t(_p(
'cmd:tasks_clear|resp:success',
"Your tasklist has been cleared."
)),
view=None if ctx.channel.id in TasklistUI._live_[ctx.author.id] else TasklistCaller(self.bot),
view=None if ctx.channel.id in TasklistUI._live_[ctx.profile.profileid] else TasklistCaller(self.bot),
ephemeral=True
)
self.bot.dispatch('tasklist_update', userid=ctx.author.id, channel=ctx.channel)
self.bot.dispatch('tasklist_update', profileid=ctx.profile.profileid, channel=ctx.channel)
@tasklist_group.command(
name=_p('cmd:tasks_remove', "remove"),
@@ -748,7 +837,7 @@ class TasklistCog(LionCog):
await ctx.interaction.response.defer(thinking=True, ephemeral=True)
tasklist = await Tasklist.fetch(self.bot, self.data, ctx.author.id)
tasklist = await Tasklist.fetch(self.bot, self.data, ctx.profile.profileid)
conditions = []
if taskidstr:
@@ -784,7 +873,7 @@ class TasklistCog(LionCog):
elif completed is False:
conditions.append(self.data.Task.completed_at == NULL)
tasks = await self.data.Task.fetch_where(*conditions, userid=ctx.author.id)
tasks = await self.data.Task.fetch_where(*conditions, userid=ctx.profile.profileid)
if not tasks:
await ctx.interaction.edit_original_response(
embed=error_embed(t(_p(
@@ -813,9 +902,9 @@ class TasklistCog(LionCog):
)
await ctx.interaction.edit_original_response(
embed=embed,
view=None if ctx.channel.id in TasklistUI._live_[ctx.author.id] else TasklistCaller(self.bot)
view=None if ctx.channel.id in TasklistUI._live_[ctx.profile.profileid] else TasklistCaller(self.bot)
)
self.bot.dispatch('tasklist_update', userid=ctx.author.id, channel=ctx.channel)
self.bot.dispatch('tasklist_update', profileid=ctx.profile.profileid, channel=ctx.channel)
tasklist_remove_cmd.autocomplete('taskidstr')(tasks_acmpl)
@@ -844,7 +933,7 @@ class TasklistCog(LionCog):
await ctx.interaction.response.defer(thinking=True, ephemeral=True)
tasklist = await Tasklist.fetch(self.bot, self.data, ctx.author.id)
tasklist = await Tasklist.fetch(self.bot, self.data, ctx.profile.profileid)
try:
taskids = tasklist.parse_labels(taskidstr)
@@ -889,9 +978,9 @@ class TasklistCog(LionCog):
)
await ctx.interaction.edit_original_response(
embed=embed,
view=None if ctx.channel.id in TasklistUI._live_[ctx.author.id] else TasklistCaller(self.bot)
view=None if ctx.channel.id in TasklistUI._live_[ctx.profile.profileid] else TasklistCaller(self.bot)
)
self.bot.dispatch('tasklist_update', userid=ctx.author.id, channel=ctx.channel)
self.bot.dispatch('tasklist_update', profileid=ctx.profile.profileid, channel=ctx.channel)
tasklist_tick_cmd.autocomplete('taskidstr')(tasks_acmpl)
@@ -920,7 +1009,7 @@ class TasklistCog(LionCog):
await ctx.interaction.response.defer(thinking=True, ephemeral=True)
tasklist = await Tasklist.fetch(self.bot, self.data, ctx.author.id)
tasklist = await Tasklist.fetch(self.bot, self.data, ctx.profile.profileid)
try:
taskids = tasklist.parse_labels(taskidstr)
@@ -962,9 +1051,9 @@ class TasklistCog(LionCog):
)
await ctx.interaction.edit_original_response(
embed=embed,
view=None if ctx.channel.id in TasklistUI._live_[ctx.author.id] else TasklistCaller(self.bot)
view=None if ctx.channel.id in TasklistUI._live_[ctx.profile.profileid] else TasklistCaller(self.bot)
)
self.bot.dispatch('tasklist_update', userid=ctx.author.id, channel=ctx.channel)
self.bot.dispatch('tasklist_update', profileid=ctx.profile.profileid, channel=ctx.channel)
tasklist_untick_cmd.autocomplete('taskidstr')(tasks_acmpl)

View File

@@ -5,6 +5,7 @@ from data.columns import Integer, String, Timestamp, Bool
class TasklistData(Registry):
class Task(RowModel):
"""
Row model describing a single task in a tasklist.
@@ -14,21 +15,17 @@ class TasklistData(Registry):
CREATE TABLE tasklist(
taskid SERIAL PRIMARY KEY,
userid BIGINT NOT NULL REFERENCES user_config ON DELETE CASCADE,
profileid INTEGER NOT NULL REFERENCES user_profiles ON DELETE CASCADE ON UPDATE CASCADE,
parentid INTEGER REFERENCES tasklist (taskid) ON DELETE SET NULL,
content TEXT NOT NULL,
rewarded BOOL DEFAULT FALSE,
deleted_at TIMESTAMPTZ,
completed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ,
duration INTEGER,
last_updated_at TIMESTAMPTZ
);
CREATE INDEX tasklist_users ON tasklist (userid);
CREATE TABLE tasklist_channels(
guildid BIGINT NOT NULL REFERENCES guild_config (guildid) ON DELETE CASCADE,
channelid BIGINT NOT NULL
);
CREATE INDEX tasklist_channels_guilds ON tasklist_channels (guildid);
"""
_tablename_ = "tasklist"
@@ -41,5 +38,26 @@ class TasklistData(Registry):
created_at = Timestamp()
deleted_at = Timestamp()
last_updated_at = Timestamp()
duration = Integer()
"""
Schema
------
CREATE TABLE tasklist_channels(
guildid BIGINT NOT NULL REFERENCES guild_config (guildid) ON DELETE CASCADE,
channelid BIGINT NOT NULL
);
CREATE INDEX tasklist_channels_guilds ON tasklist_channels (guildid);
"""
channels = Table('tasklist_channels')
"""
Schema
------
CREATE TABLE current_tasks(
taskid PRIMARY KEY REFERENCES tasklist (taskid) ON DELETE CASCADE ON UPDATE CASCADE,
last_started_at TIMESTAMPTZ NOT NULL
);
"""
current_tasks = Table('current_tasks')

View File

@@ -0,0 +1,23 @@
ALTER TABLE tasklist
DROP CONSTRAINT fk_tasklist_users;
ALTER TABLE tasklist
ADD CONSTRAINT fk_tasklist_users
FOREIGN KEY (userid)
REFERENCES user_profiles (profileid)
ON DELETE CASCADE
ON UPDATE CASCADE
NOT VALID;
ALTER TABLE tasklist
ADD COLUMN duration INTEGER;
CREATE TABLE tasklist_current(
taskid INTEGER PRIMARY KEY REFERENCES tasklist (taskid) ON DELETE CASCADE ON UPDATE CASCADE,
started_at TIMESTAMPTZ NOT NULL
);
CREATE TABLE tasklist_planner(
taskid INTEGER PRIMARY KEY REFERENCES tasklist (taskid) ON DELETE CASCADE ON UPDATE CASCADE,
sortkey INTEGER
);

View File

@@ -232,13 +232,18 @@ class TasklistUI(BasePager):
def __init__(self,
tasklist: Tasklist,
channel: discord.abc.Messageable, guild: Optional[discord.Guild] = None, **kwargs):
channel: discord.abc.Messageable,
guild: Optional[discord.Guild] = None,
caller: Optional[discord.User | discord.Member] = None,
**kwargs):
kwargs.setdefault('timeout', 600)
super().__init__(**kwargs)
self.bot = tasklist.bot
self.tasklist = tasklist
self.labelled = tasklist.labelled
self.caller = caller
# NOTE: This is now a profiled
self.userid = tasklist.userid
self.channel = channel
self.guild = guild
@@ -449,9 +454,10 @@ class TasklistUI(BasePager):
cascade=True,
completed_at=utc_now()
)
if self.guild:
if (member := self.guild.get_member(self.userid)):
self.bot.dispatch('tasks_completed', member, *(t.taskid for t in to_complete))
# TODO: Removed economy integration
# if self.guild:
# if (member := self.guild.get_member(self.userid)):
# self.bot.dispatch('tasks_completed', member, *(t.taskid for t in to_complete))
if to_uncomplete:
await self.tasklist.update_tasks(
*(t.taskid for t in to_uncomplete),
@@ -475,7 +481,7 @@ class TasklistUI(BasePager):
if shared_root:
self._subtree_root = labelled[shared_root].taskid
self.bot.dispatch('tasklist_update', userid=self.userid, channel=self.channel, summon=False)
self.bot.dispatch('tasklist_update', profileid=self.userid, channel=self.channel, summon=False)
async def _delete_menu(self, interaction: discord.Interaction, selected: Select, subtree: bool):
await interaction.response.defer()
@@ -486,7 +492,7 @@ class TasklistUI(BasePager):
cascade=True,
deleted_at=utc_now()
)
self.bot.dispatch('tasklist_update', userid=self.userid, channel=self.channel, summon=False)
self.bot.dispatch('tasklist_update', profileid=self.userid, channel=self.channel, summon=False)
async def _edit_menu(self, interaction: discord.Interaction, selected: Select, subtree: bool):
if not selected.values:
@@ -513,7 +519,7 @@ class TasklistUI(BasePager):
self._last_parentid = new_parentid
if not subtree:
self._subtree_root = new_parentid
self.bot.dispatch('tasklist_update', userid=self.userid, channel=self.channel, summon=False)
self.bot.dispatch('tasklist_update', profileid=self.userid, channel=self.channel, summon=False)
await interaction.response.send_modal(editor)
@@ -606,7 +612,7 @@ class TasklistUI(BasePager):
self._subtree_root = pid
await interaction.response.defer()
await self.tasklist.create_task(new_task, parentid=pid)
self.bot.dispatch('tasklist_update', userid=self.userid, channel=self.channel, summon=False)
self.bot.dispatch('tasklist_update', profileid=self.userid, channel=self.channel, summon=False)
await press.response.send_modal(editor)
@@ -667,7 +673,7 @@ class TasklistUI(BasePager):
@editor.add_callback
async def editor_callback(interaction: discord.Interaction):
self.bot.dispatch('tasklist_update', userid=self.userid, channel=self.channel, summon=False)
self.bot.dispatch('tasklist_update', profileid=self.userid, channel=self.channel, summon=False)
if sum(len(line) for line in editor.lines.values()) + len(editor.lines) >= 4000:
await press.response.send_message(
@@ -698,7 +704,7 @@ class TasklistUI(BasePager):
await self.tasklist.update_tasklist(
deleted_at=utc_now(),
)
self.bot.dispatch('tasklist_update', userid=self.userid, channel=self.channel, summon=False)
self.bot.dispatch('tasklist_update', profileid=self.userid, channel=self.channel, summon=False)
async def clear_button_refresh(self):
self.clear_button.label = self.bot.translator.t(_p(
@@ -771,11 +777,12 @@ class TasklistUI(BasePager):
# ----- UI Flow -----
def access_check(self, userid):
return userid == self.userid
return userid in (self.userid, self.caller.id if self.caller else None)
async def interaction_check(self, interaction: discord.Interaction):
t = self.bot.translator.t
if not self.access_check(interaction.user.id):
interaction_profile = await self.bot.profiles.fetch_profile_discord(interaction.user)
if not self.access_check(interaction_profile.profileid):
embed = discord.Embed(
description=t(_p(
'ui:tasklist|error:wrong_user',
@@ -812,10 +819,7 @@ class TasklistUI(BasePager):
total = len(tasks)
completed = sum(t.completed_at is not None for t in tasks)
if self.guild:
user = self.guild.get_member(self.userid)
else:
user = self.bot.get_user(self.userid)
user = self.caller
user_name = user.name if user else str(self.userid)
user_colour = user.colour if user else discord.Color.orange()