Merge pull request #2 from StudyLions/staging
Bugfixes and small UI updates
This commit is contained in:
@@ -46,11 +46,12 @@ async def preload_guild_configuration(client):
|
||||
Loads the plain guild configuration for all guilds the client is part of into data.
|
||||
"""
|
||||
guildids = [guild.id for guild in client.guilds]
|
||||
rows = client.data.guild_config.fetch_rows_where(guildid=guildids)
|
||||
client.log(
|
||||
"Preloaded guild configuration for {} guilds.".format(len(rows)),
|
||||
context="CORE_LOADING"
|
||||
)
|
||||
if guildids:
|
||||
rows = client.data.guild_config.fetch_rows_where(guildid=guildids)
|
||||
client.log(
|
||||
"Preloaded guild configuration for {} guilds.".format(len(rows)),
|
||||
context="CORE_LOADING"
|
||||
)
|
||||
|
||||
|
||||
@module.launch_task
|
||||
@@ -59,11 +60,12 @@ async def preload_studying_members(client):
|
||||
Loads the member data for all members who are currently in voice channels.
|
||||
"""
|
||||
userids = list(set(member.id for guild in client.guilds for ch in guild.voice_channels for member in ch.members))
|
||||
rows = client.data.lions.fetch_rows_where(userid=userids)
|
||||
client.log(
|
||||
"Preloaded member data for {} members.".format(len(rows)),
|
||||
context="CORE_LOADING"
|
||||
)
|
||||
if userids:
|
||||
rows = client.data.lions.fetch_rows_where(userid=userids)
|
||||
client.log(
|
||||
"Preloaded member data for {} members.".format(len(rows)),
|
||||
context="CORE_LOADING"
|
||||
)
|
||||
|
||||
|
||||
@module.launch_task
|
||||
|
||||
@@ -72,6 +72,9 @@ class TimeSlot:
|
||||
connect=False
|
||||
)
|
||||
|
||||
happy_lion = "https://media.discordapp.net/stickers/898266283559227422.png"
|
||||
sad_lion = "https://media.discordapp.net/stickers/898266548421148723.png"
|
||||
|
||||
def __init__(self, guild, start_time, data=None):
|
||||
self.guild: discord.Guild = guild
|
||||
self.start_time: datetime.datetime = start_time
|
||||
@@ -139,13 +142,16 @@ class TimeSlot:
|
||||
else:
|
||||
classifications["Attended"].append(mention)
|
||||
|
||||
all_attended = all(mem.has_attended for mem in self.members.values())
|
||||
bonus_line = (
|
||||
"{tick} All members attended, and will get a `{bonus} LC` completion bonus!".format(
|
||||
"{tick} Everyone attended, and will get a `{bonus} LC` bonus!".format(
|
||||
tick=tick,
|
||||
bonus=GuildSettings(self.guild.id).accountability_bonus.value
|
||||
)
|
||||
if all(mem.has_attended for mem in self.members.values()) else ""
|
||||
if all_attended else ""
|
||||
)
|
||||
if all_attended:
|
||||
embed.set_thumbnail(url=self.happy_lion)
|
||||
|
||||
embed.description += "\n" + bonus_line
|
||||
for field, value in classifications.items():
|
||||
@@ -182,16 +188,22 @@ class TimeSlot:
|
||||
else:
|
||||
classifications["Missing"].append(mention)
|
||||
|
||||
all_attended = all(mem.has_attended for mem in self.members.values())
|
||||
|
||||
bonus_line = (
|
||||
"{tick} All members attended, and received a `{bonus} LC` completion bonus!".format(
|
||||
"{tick} Everyone attended, and received a `{bonus} LC` bonus!".format(
|
||||
tick=tick,
|
||||
bonus=GuildSettings(self.guild.id).accountability_bonus.value
|
||||
)
|
||||
if all(mem.has_attended for mem in self.members.values()) else
|
||||
if all_attended else
|
||||
"{cross} Some members missed the session, so everyone missed out on the bonus!".format(
|
||||
cross=cross
|
||||
)
|
||||
)
|
||||
if all_attended:
|
||||
embed.set_thumbnail(url=self.happy_lion)
|
||||
else:
|
||||
embed.set_thumbnail(url=self.sad_lion)
|
||||
|
||||
embed.description += "\n" + bonus_line
|
||||
for field, value in classifications.items():
|
||||
@@ -292,7 +304,7 @@ class TimeSlot:
|
||||
self.message = await self.lobby.send(
|
||||
embed=self.open_embed
|
||||
)
|
||||
except discord.HTTPException as e:
|
||||
except discord.HTTPException:
|
||||
GuildSettings(self.guild.id).event_log.log(
|
||||
"Failed to post the status message in the accountability lobby {}.\n"
|
||||
"Skipping this session.".format(self.lobby.mention),
|
||||
@@ -322,10 +334,21 @@ class TimeSlot:
|
||||
Update the status message, and launch the DM reminder.
|
||||
"""
|
||||
if self.channel:
|
||||
await self.channel.edit(name="Accountability Study Room")
|
||||
await self.channel.set_permissions(self.guild.default_role, view_channel=True, connect=False)
|
||||
try:
|
||||
await self.channel.edit(name="Accountability Study Room")
|
||||
await self.channel.set_permissions(self.guild.default_role, view_channel=True, connect=False)
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
asyncio.create_task(self.dm_reminder(delay=60))
|
||||
await self.message.edit(embed=self.status_embed)
|
||||
try:
|
||||
await self.message.edit(embed=self.status_embed)
|
||||
except discord.NotFound:
|
||||
try:
|
||||
self.message = await self.lobby.send(
|
||||
embed=self.status_embed
|
||||
)
|
||||
except discord.HTTPException:
|
||||
self.message = None
|
||||
|
||||
async def dm_reminder(self, delay=60):
|
||||
"""
|
||||
|
||||
@@ -232,7 +232,8 @@ async def turnover():
|
||||
|
||||
# Start all the current rooms
|
||||
await asyncio.gather(
|
||||
*(slot.start() for slot in current_slots)
|
||||
*(slot.start() for slot in current_slots),
|
||||
return_exceptions=True
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -353,9 +353,10 @@ class Ticket:
|
||||
Method used to revert the ticket action, e.g. unban or remove mute role.
|
||||
Generally called by `pardon` and `_expire`.
|
||||
|
||||
Must be overriden by the Ticket type, if they implement any revert logic.
|
||||
May be overriden by the Ticket type, if they implement any revert logic.
|
||||
Is a no-op by default.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
return
|
||||
|
||||
async def _expire(self):
|
||||
"""
|
||||
|
||||
@@ -87,9 +87,9 @@ class video_studyban(settings.Boolean, GuildSetting):
|
||||
@property
|
||||
def success_response(self):
|
||||
if self.value:
|
||||
"Members will now be study banned if they don't enable their video in the configured video channels."
|
||||
return "Members will now be study-banned if they don't enable their video in the configured video channels."
|
||||
else:
|
||||
"Members will not be studybanned if they don't enable their video in video channels."
|
||||
return "Members will not be study-banned if they don't enable their video in video channels."
|
||||
|
||||
|
||||
@GuildSettings.attach_setting
|
||||
|
||||
@@ -265,6 +265,13 @@ async def cmd_studybadges(ctx, flags):
|
||||
# Parse the input
|
||||
lines = ctx.args.splitlines()
|
||||
results = [await parse_level(ctx, line) for line in lines]
|
||||
# Check for duplicates
|
||||
_set = set()
|
||||
duplicate = next((time for time, _ in results if time in _set or _set.add(time)), None)
|
||||
if duplicate:
|
||||
return await ctx.error_reply(
|
||||
"Level `{}` provided twice!".format(strfdur(duplicate, short=False))
|
||||
)
|
||||
current_times = set(row.required_time for row in guild_roles)
|
||||
|
||||
# Split up the provided lines into levels to add and levels to edit
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import json
|
||||
import asyncio
|
||||
import datetime
|
||||
import itertools
|
||||
from io import StringIO
|
||||
from enum import IntEnum
|
||||
@@ -12,7 +11,7 @@ from cmdClient.Context import Context
|
||||
from cmdClient.lib import SafeCancellation
|
||||
|
||||
from meta import client
|
||||
from utils.lib import parse_dur, strfdur, strfdelta, prop_tabulate, multiple_replace
|
||||
from utils.lib import parse_dur, strfdur, prop_tabulate, multiple_replace
|
||||
|
||||
from .base import UserInputError
|
||||
|
||||
@@ -100,7 +99,7 @@ class Boolean(SettingType):
|
||||
Looks up the provided string in the truthy and falsey tables.
|
||||
"""
|
||||
_userstr = userstr.lower()
|
||||
if _userstr == "none":
|
||||
if not _userstr or _userstr == "none":
|
||||
return None
|
||||
if _userstr in cls._truthy:
|
||||
return True
|
||||
@@ -154,7 +153,7 @@ class Integer(SettingType):
|
||||
"""
|
||||
Relies on integer casting to convert the user string
|
||||
"""
|
||||
if userstr.lower() == "none":
|
||||
if not userstr or userstr.lower() == "none":
|
||||
return None
|
||||
|
||||
try:
|
||||
@@ -222,7 +221,7 @@ class String(SettingType):
|
||||
Check that the user-entered string is of the correct length.
|
||||
Accept "None" to unset.
|
||||
"""
|
||||
if userstr.lower() == "none":
|
||||
if not userstr or userstr.lower() == "none":
|
||||
# Unsetting case
|
||||
return None
|
||||
elif cls._maxlen is not None and len(userstr) > cls._maxlen:
|
||||
@@ -284,7 +283,7 @@ class Channel(SettingType):
|
||||
Pass to the channel seeker utility to find the requested channel.
|
||||
Handle `0` and variants of `None` to unset.
|
||||
"""
|
||||
if userstr.lower() in ('0', 'none'):
|
||||
if userstr.lower() in ('', '0', 'none'):
|
||||
return None
|
||||
else:
|
||||
channel = await ctx.find_channel(userstr, interactive=True, chan_type=cls._chan_type)
|
||||
@@ -296,13 +295,17 @@ class Channel(SettingType):
|
||||
@classmethod
|
||||
def _format_data(cls, id: int, data: Optional[int], **kwargs):
|
||||
"""
|
||||
Retrieve an artificially created channel mention.
|
||||
If the channel does not exist, this will show up as invalid-channel.
|
||||
Retrieve the channel mention, if the channel still exists.
|
||||
If the channel no longer exists, or cannot be seen by the client, returns None.
|
||||
"""
|
||||
if data is None:
|
||||
return None
|
||||
else:
|
||||
return "<#{}>".format(data)
|
||||
channel = client.get_channel(data)
|
||||
if channel:
|
||||
return channel.mention
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class VoiceChannel(Channel):
|
||||
@@ -375,7 +378,7 @@ class Role(SettingType):
|
||||
Pass to the role seeker utility to find the requested role.
|
||||
Handle `0` and variants of `None` to unset.
|
||||
"""
|
||||
if userstr.lower() in ('0', 'none'):
|
||||
if userstr.lower() in ('', '0', 'none'):
|
||||
return None
|
||||
else:
|
||||
role = await ctx.find_role(userstr, create=cls._parse_create, interactive=True)
|
||||
@@ -452,7 +455,7 @@ class Emoji(SettingType):
|
||||
Pass to the emoji string parser to get the emoji.
|
||||
Handle `0` and variants of `None` to unset.
|
||||
"""
|
||||
if userstr.lower() in ('0', 'none'):
|
||||
if userstr.lower() in ('', '0', 'none'):
|
||||
return None
|
||||
else:
|
||||
return cls._parse_emoji(userstr)
|
||||
@@ -505,7 +508,7 @@ class Timezone(SettingType):
|
||||
Check that the user-entered string is of the correct length.
|
||||
Accept "None" to unset.
|
||||
"""
|
||||
if userstr.lower() == "none":
|
||||
if not userstr or userstr.lower() == "none":
|
||||
# Unsetting case
|
||||
return None
|
||||
try:
|
||||
@@ -585,7 +588,7 @@ class IntegerEnum(SettingType):
|
||||
|
||||
options = {name.lower(): mem.value for name, mem in cls._enum.__members__.items()}
|
||||
|
||||
if userstr == "none":
|
||||
if not userstr or userstr == "none":
|
||||
# Unsetting case
|
||||
return None
|
||||
elif userstr not in options:
|
||||
@@ -654,7 +657,7 @@ class Duration(SettingType):
|
||||
"""
|
||||
Parse the provided duration.
|
||||
"""
|
||||
if userstr.lower() == "none":
|
||||
if not userstr or userstr.lower() == "none":
|
||||
return None
|
||||
|
||||
if cls._default_multiplier and userstr.isdigit():
|
||||
@@ -949,12 +952,13 @@ class SettingList(SettingType):
|
||||
Splits the user string across `,` to break up the list.
|
||||
Handle `0` and variants of `None` to unset.
|
||||
"""
|
||||
if userstr.lower() in ('0', 'none'):
|
||||
if userstr.lower() in ('', '0', 'none'):
|
||||
return []
|
||||
else:
|
||||
data = []
|
||||
for item in userstr.split(','):
|
||||
data.append(await cls._setting._parse_userstr(ctx, id, item.strip()))
|
||||
items = (item.strip() for item in userstr.split(','))
|
||||
items = (item for item in items if item)
|
||||
data = [await cls._setting._parse_userstr(ctx, id, item, **kwargs) for item in items]
|
||||
|
||||
if cls._force_unique:
|
||||
data = list(set(data))
|
||||
|
||||
Reference in New Issue
Block a user