Merge pull request #10 from StudyLions/patch-startup

Patch startup
This commit is contained in:
Interitio
2021-11-16 09:09:15 +02:00
committed by GitHub
12 changed files with 71 additions and 37 deletions

View File

@@ -13,7 +13,7 @@ class LionCommand(Command):
""" """
Subclass to allow easy attachment of custom hooks and structure to commands. Subclass to allow easy attachment of custom hooks and structure to commands.
""" """
... allow_before_ready = False
class LionModule(Module): class LionModule(Module):
@@ -72,25 +72,38 @@ class LionModule(Module):
""" """
Lion pre-command hook. Lion pre-command hook.
""" """
if not self.ready and not ctx.cmd.allow_before_ready:
try:
await ctx.embed_reply(
"I am currently restarting! Please try again in a couple of minutes."
)
except discord.HTTPException:
pass
raise SafeCancellation(details="Module '{}' is not ready.".format(self.name))
# Check that the channel and guild still exists
if not ctx.client.get_guild(ctx.guild.id) or not ctx.guild.get_channel(ctx.ch.id):
raise SafeCancellation(details='Command channel is no longer reachable.')
# Check global user blacklist # Check global user blacklist
if ctx.author.id in ctx.client.objects['blacklisted_users']: if ctx.author.id in ctx.client.objects['blacklisted_users']:
raise SafeCancellation raise SafeCancellation(details='User is blacklisted.')
if ctx.guild: if ctx.guild:
# Check global guild blacklist # Check global guild blacklist
if ctx.guild.id in ctx.client.objects['blacklisted_guilds']: if ctx.guild.id in ctx.client.objects['blacklisted_guilds']:
raise SafeCancellation raise SafeCancellation(details='Guild is blacklisted.')
# Check guild's own member blacklist # Check guild's own member blacklist
if ctx.author.id in ctx.client.objects['ignored_members'][ctx.guild.id]: if ctx.author.id in ctx.client.objects['ignored_members'][ctx.guild.id]:
raise SafeCancellation raise SafeCancellation(details='User is ignored in this guild.')
# Check channel permissions are sane # Check channel permissions are sane
if not ctx.ch.permissions_for(ctx.guild.me).send_messages: if not ctx.ch.permissions_for(ctx.guild.me).send_messages:
raise SafeCancellation raise SafeCancellation(details='I cannot send messages in this channel.')
if not ctx.ch.permissions_for(ctx.guild.me).embed_links: if not ctx.ch.permissions_for(ctx.guild.me).embed_links:
await ctx.reply("I need permission to send embeds in this channel before I can run any commands!") await ctx.reply("I need permission to send embeds in this channel before I can run any commands!")
raise SafeCancellation raise SafeCancellation(details='I cannot send embeds in this channel.')
# Start typing # Start typing
await ctx.ch.trigger_typing() await ctx.ch.trigger_typing()

View File

@@ -53,13 +53,19 @@ def add_pending(pending):
guild_config = RowTable( guild_config = RowTable(
'guild_config', 'guild_config',
('guildid', 'admin_role', 'mod_role', 'event_log_channel', 'alert_channel', ('guildid', 'admin_role', 'mod_role', 'event_log_channel', 'mod_log_channel', 'alert_channel',
'studyban_role',
'min_workout_length', 'workout_reward', 'min_workout_length', 'workout_reward',
'max_tasks', 'task_reward', 'task_reward_limit', 'max_tasks', 'task_reward', 'task_reward_limit',
'study_hourly_reward', 'study_hourly_live_bonus', 'study_hourly_reward', 'study_hourly_live_bonus',
'study_ban_role', 'max_study_bans'), 'renting_price', 'renting_category', 'renting_cap', 'renting_role', 'renting_sync_perms',
'accountability_category', 'accountability_lobby', 'accountability_bonus',
'accountability_reward', 'accountability_price',
'video_studyban', 'video_grace_period',
'greeting_channel', 'greeting_message', 'returning_message',
'starting_funds', 'persist_roles'),
'guildid', 'guildid',
cache=TTLCache(1000, ttl=60*5) cache=TTLCache(2500, ttl=60*5)
) )
unranked_roles = Table('unranked_roles') unranked_roles = Table('unranked_roles')
@@ -72,6 +78,7 @@ lions = RowTable(
('guildid', 'userid', ('guildid', 'userid',
'tracked_time', 'coins', 'tracked_time', 'coins',
'workout_count', 'last_workout_start', 'workout_count', 'last_workout_start',
'revision_mute_count',
'last_study_badgeid', 'last_study_badgeid',
'video_warned', 'video_warned',
'_timestamp' '_timestamp'

View File

@@ -69,7 +69,8 @@ class TimeSlot:
_everyone_overwrite = discord.PermissionOverwrite( _everyone_overwrite = discord.PermissionOverwrite(
view_channel=False, view_channel=False,
connect=False connect=False,
speak=False
) )
happy_lion = "https://media.discordapp.net/stickers/898266283559227422.png" happy_lion = "https://media.discordapp.net/stickers/898266283559227422.png"
@@ -218,6 +219,9 @@ class TimeSlot:
""" """
Load data and update applicable caches. Load data and update applicable caches.
""" """
if not self.guild:
return self
# Load setting data # Load setting data
self.category = GuildSettings(self.guild.id).accountability_category.value self.category = GuildSettings(self.guild.id).accountability_category.value
self.lobby = GuildSettings(self.guild.id).accountability_lobby.value self.lobby = GuildSettings(self.guild.id).accountability_lobby.value
@@ -389,13 +393,14 @@ class TimeSlot:
pass pass
# Reward members appropriately # Reward members appropriately
guild_settings = GuildSettings(self.guild.id) if self.guild:
reward = guild_settings.accountability_reward.value guild_settings = GuildSettings(self.guild.id)
if all(mem.has_attended for mem in self.members.values()): reward = guild_settings.accountability_reward.value
reward += guild_settings.accountability_bonus.value if all(mem.has_attended for mem in self.members.values()):
reward += guild_settings.accountability_bonus.value
for memid in self.members: for memid in self.members:
Lion.fetch(self.guild.id, memid).addCoins(reward) Lion.fetch(self.guild.id, memid).addCoins(reward)
async def cancel(self): async def cancel(self):
""" """

View File

@@ -374,14 +374,15 @@ async def _accountability_system_resume():
None, mow.slotid, mow.userid) None, mow.slotid, mow.userid)
for mow in slot_members[row.slotid] if mow.last_joined_at for mow in slot_members[row.slotid] if mow.last_joined_at
) )
slot = TimeSlot(client.get_guild(row.guildid), row.start_at, data=row).load( if client.get_guild(row.guildid):
memberids=[mow.userid for mow in slot_members[row.slotid]] slot = TimeSlot(client.get_guild(row.guildid), row.start_at, data=row).load(
) memberids=[mow.userid for mow in slot_members[row.slotid]]
)
try:
await slot.close()
except discord.HTTPException:
pass
row.closed_at = now row.closed_at = now
try:
await slot.close()
except discord.HTTPException:
pass
# Load the in-progress room data # Load the in-progress room data
if current_room_data: if current_room_data:
@@ -451,7 +452,7 @@ async def launch_accountability_system(client):
guilds = tables.guild_config.fetch_rows_where( guilds = tables.guild_config.fetch_rows_where(
accountability_category=NOTNULL accountability_category=NOTNULL
) )
[AccountabilityGuild(guild.guildid) for guild in guilds] [AccountabilityGuild(guild.guildid) for guild in guilds if client.get_guild(guild.guildid)]
await _accountability_system_resume() await _accountability_system_resume()
asyncio.create_task(_accountability_loop()) asyncio.create_task(_accountability_loop())

View File

@@ -485,7 +485,7 @@ async def cmd_reactionroles(ctx, flags):
await ctx.error_reply( await ctx.error_reply(
"The provided channel no longer exists!" "The provided channel no longer exists!"
) )
elif channel.type != discord.ChannelType.text: elif not isinstance(channel, discord.TextChannel):
await ctx.error_reply( await ctx.error_reply(
"The provided channel is not a text channel!" "The provided channel is not a text channel!"
) )
@@ -821,8 +821,8 @@ async def cmd_reactionroles(ctx, flags):
setting = await setting_class.parse(target.messageid, ctx, flags[flag]) setting = await setting_class.parse(target.messageid, ctx, flags[flag])
except UserInputError as e: except UserInputError as e:
return await ctx.error_reply( return await ctx.error_reply(
title="Couldn't save settings!", "{} {}\nNo settings were modified.".format(cross, e.msg),
description="{} {}\nNo settings were modified.".format(cross, e.msg) title="Couldn't save settings!"
) )
else: else:
update_lines.append( update_lines.append(
@@ -861,8 +861,8 @@ async def cmd_reactionroles(ctx, flags):
setting = await setting_class.parse(reaction.reactionid, ctx, flags[flag]) setting = await setting_class.parse(reaction.reactionid, ctx, flags[flag])
except UserInputError as e: except UserInputError as e:
return await ctx.error_reply( return await ctx.error_reply(
"{} {}\nNo reaction roles were modified.".format(cross, e.msg),
title="Couldn't save reaction role settings!", title="Couldn't save reaction role settings!",
description="{} {}\nNo reaction roles were modified.".format(cross, e.msg)
) )
else: else:
update_lines.append( update_lines.append(

View File

@@ -199,6 +199,7 @@ class price(setting_types.Integer, ReactionSetting):
) )
accepts = "An integer number of coins. Use `0` to make the role free, or `None` to use the message default." accepts = "An integer number of coins. Use `0` to make the role free, or `None` to use the message default."
_max = 2 ** 20
@property @property
def default(self): def default(self):

View File

@@ -272,7 +272,7 @@ class ReactionRoleMessage:
# Fetch the number of applicable roles the user has # Fetch the number of applicable roles the user has
roleids = set(reaction.data.roleid for reaction in self.reactions) roleids = set(reaction.data.roleid for reaction in self.reactions)
member_roleids = set(role.id for role in member.roles) member_roleids = set(role.id for role in member.roles)
if len(roleids.intersection(member_roleids)) > maximum: if len(roleids.intersection(member_roleids)) >= maximum:
# Notify the user # Notify the user
embed = discord.Embed( embed = discord.Embed(
title="Maximum group roles reached!", title="Maximum group roles reached!",

View File

@@ -178,6 +178,8 @@ class Room:
""" """
Expire the room. Expire the room.
""" """
guild_settings = GuildSettings(self.data.guildid)
if self.channel: if self.channel:
# Delete the discord channel # Delete the discord channel
try: try:
@@ -188,7 +190,6 @@ class Room:
# Delete the room from data (cascades to member deletion) # Delete the room from data (cascades to member deletion)
self.delete() self.delete()
guild_settings = GuildSettings(self.data.guildid)
guild_settings.event_log.log( guild_settings.event_log.log(
title="Private study room expired!", title="Private study room expired!",
description="<@{}>'s private study room expired.".format(self.data.ownerid) description="<@{}>'s private study room expired.".format(self.data.ownerid)

View File

@@ -12,7 +12,8 @@ from .module import module
@module.cmd( @module.cmd(
"stats", "stats",
group="Statistics", group="Statistics",
desc="View a summary of your study statistics!" desc="View a summary of your study statistics!",
allow_before_ready=True
) )
@in_guild() @in_guild()
async def cmd_stats(ctx): async def cmd_stats(ctx):

View File

@@ -26,21 +26,22 @@ async def embed_reply(ctx, desc, colour=discord.Colour.orange(), **kwargs):
@Context.util @Context.util
async def error_reply(ctx, error_str, **kwargs): async def error_reply(ctx, error_str, send_args={}, **kwargs):
""" """
Notify the user of a user level error. Notify the user of a user level error.
Typically, this will occur in a red embed, posted in the command channel. Typically, this will occur in a red embed, posted in the command channel.
""" """
embed = discord.Embed( embed = discord.Embed(
colour=discord.Colour.red(), colour=discord.Colour.red(),
description=error_str description=error_str,
**kwargs
) )
message = None message = None
try: try:
message = await ctx.ch.send( message = await ctx.ch.send(
embed=embed, embed=embed,
reference=ctx.msg.to_reference(fail_if_not_exists=False), reference=ctx.msg.to_reference(fail_if_not_exists=False),
**kwargs **send_args
) )
ctx.sent_messages.append(message) ctx.sent_messages.append(message)
return message return message

View File

@@ -182,7 +182,11 @@ async def find_channel(ctx, userstr, interactive=False, collection=None, chan_ty
# Create the collection to search from args or guild channels # Create the collection to search from args or guild channels
collection = collection if collection else ctx.guild.channels collection = collection if collection else ctx.guild.channels
if chan_type is not None: if chan_type is not None:
collection = [chan for chan in collection if chan.type == chan_type] if chan_type == discord.ChannelType.text:
# Hack to support news channels as text channels
collection = [chan for chan in collection if isinstance(chan, discord.TextChannel)]
else:
collection = [chan for chan in collection if chan.type == chan_type]
# If the user input was a number or possible channel mention, extract it # If the user input was a number or possible channel mention, extract it
chanid = userstr.strip('<#@&!>') chanid = userstr.strip('<#@&!>')
@@ -413,7 +417,7 @@ async def find_message(ctx, msgid, chlist=None, ignore=[]):
async def _search_in_channel(channel: discord.TextChannel, msgid: int): async def _search_in_channel(channel: discord.TextChannel, msgid: int):
if channel.type != discord.ChannelType.text: if not isinstance(channel, discord.TextChannel):
return return
try: try:
message = await channel.fetch_message(msgid) message = await channel.fetch_message(msgid)