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.
|
Loads the plain guild configuration for all guilds the client is part of into data.
|
||||||
"""
|
"""
|
||||||
guildids = [guild.id for guild in client.guilds]
|
guildids = [guild.id for guild in client.guilds]
|
||||||
rows = client.data.guild_config.fetch_rows_where(guildid=guildids)
|
if guildids:
|
||||||
client.log(
|
rows = client.data.guild_config.fetch_rows_where(guildid=guildids)
|
||||||
"Preloaded guild configuration for {} guilds.".format(len(rows)),
|
client.log(
|
||||||
context="CORE_LOADING"
|
"Preloaded guild configuration for {} guilds.".format(len(rows)),
|
||||||
)
|
context="CORE_LOADING"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@module.launch_task
|
@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.
|
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))
|
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)
|
if userids:
|
||||||
client.log(
|
rows = client.data.lions.fetch_rows_where(userid=userids)
|
||||||
"Preloaded member data for {} members.".format(len(rows)),
|
client.log(
|
||||||
context="CORE_LOADING"
|
"Preloaded member data for {} members.".format(len(rows)),
|
||||||
)
|
context="CORE_LOADING"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@module.launch_task
|
@module.launch_task
|
||||||
|
|||||||
@@ -72,6 +72,9 @@ class TimeSlot:
|
|||||||
connect=False
|
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):
|
def __init__(self, guild, start_time, data=None):
|
||||||
self.guild: discord.Guild = guild
|
self.guild: discord.Guild = guild
|
||||||
self.start_time: datetime.datetime = start_time
|
self.start_time: datetime.datetime = start_time
|
||||||
@@ -139,13 +142,16 @@ class TimeSlot:
|
|||||||
else:
|
else:
|
||||||
classifications["Attended"].append(mention)
|
classifications["Attended"].append(mention)
|
||||||
|
|
||||||
|
all_attended = all(mem.has_attended for mem in self.members.values())
|
||||||
bonus_line = (
|
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,
|
tick=tick,
|
||||||
bonus=GuildSettings(self.guild.id).accountability_bonus.value
|
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
|
embed.description += "\n" + bonus_line
|
||||||
for field, value in classifications.items():
|
for field, value in classifications.items():
|
||||||
@@ -182,16 +188,22 @@ class TimeSlot:
|
|||||||
else:
|
else:
|
||||||
classifications["Missing"].append(mention)
|
classifications["Missing"].append(mention)
|
||||||
|
|
||||||
|
all_attended = all(mem.has_attended for mem in self.members.values())
|
||||||
|
|
||||||
bonus_line = (
|
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,
|
tick=tick,
|
||||||
bonus=GuildSettings(self.guild.id).accountability_bonus.value
|
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} Some members missed the session, so everyone missed out on the bonus!".format(
|
||||||
cross=cross
|
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
|
embed.description += "\n" + bonus_line
|
||||||
for field, value in classifications.items():
|
for field, value in classifications.items():
|
||||||
@@ -292,7 +304,7 @@ class TimeSlot:
|
|||||||
self.message = await self.lobby.send(
|
self.message = await self.lobby.send(
|
||||||
embed=self.open_embed
|
embed=self.open_embed
|
||||||
)
|
)
|
||||||
except discord.HTTPException as e:
|
except discord.HTTPException:
|
||||||
GuildSettings(self.guild.id).event_log.log(
|
GuildSettings(self.guild.id).event_log.log(
|
||||||
"Failed to post the status message in the accountability lobby {}.\n"
|
"Failed to post the status message in the accountability lobby {}.\n"
|
||||||
"Skipping this session.".format(self.lobby.mention),
|
"Skipping this session.".format(self.lobby.mention),
|
||||||
@@ -322,10 +334,21 @@ class TimeSlot:
|
|||||||
Update the status message, and launch the DM reminder.
|
Update the status message, and launch the DM reminder.
|
||||||
"""
|
"""
|
||||||
if self.channel:
|
if self.channel:
|
||||||
await self.channel.edit(name="Accountability Study Room")
|
try:
|
||||||
await self.channel.set_permissions(self.guild.default_role, view_channel=True, connect=False)
|
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))
|
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):
|
async def dm_reminder(self, delay=60):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -232,7 +232,8 @@ async def turnover():
|
|||||||
|
|
||||||
# Start all the current rooms
|
# Start all the current rooms
|
||||||
await asyncio.gather(
|
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.
|
Method used to revert the ticket action, e.g. unban or remove mute role.
|
||||||
Generally called by `pardon` and `_expire`.
|
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):
|
async def _expire(self):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -87,9 +87,9 @@ class video_studyban(settings.Boolean, GuildSetting):
|
|||||||
@property
|
@property
|
||||||
def success_response(self):
|
def success_response(self):
|
||||||
if self.value:
|
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:
|
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
|
@GuildSettings.attach_setting
|
||||||
|
|||||||
@@ -265,6 +265,13 @@ async def cmd_studybadges(ctx, flags):
|
|||||||
# Parse the input
|
# Parse the input
|
||||||
lines = ctx.args.splitlines()
|
lines = ctx.args.splitlines()
|
||||||
results = [await parse_level(ctx, line) for line in lines]
|
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)
|
current_times = set(row.required_time for row in guild_roles)
|
||||||
|
|
||||||
# Split up the provided lines into levels to add and levels to edit
|
# Split up the provided lines into levels to add and levels to edit
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import json
|
import json
|
||||||
import asyncio
|
import asyncio
|
||||||
import datetime
|
|
||||||
import itertools
|
import itertools
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
@@ -12,7 +11,7 @@ from cmdClient.Context import Context
|
|||||||
from cmdClient.lib import SafeCancellation
|
from cmdClient.lib import SafeCancellation
|
||||||
|
|
||||||
from meta import client
|
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
|
from .base import UserInputError
|
||||||
|
|
||||||
@@ -100,7 +99,7 @@ class Boolean(SettingType):
|
|||||||
Looks up the provided string in the truthy and falsey tables.
|
Looks up the provided string in the truthy and falsey tables.
|
||||||
"""
|
"""
|
||||||
_userstr = userstr.lower()
|
_userstr = userstr.lower()
|
||||||
if _userstr == "none":
|
if not _userstr or _userstr == "none":
|
||||||
return None
|
return None
|
||||||
if _userstr in cls._truthy:
|
if _userstr in cls._truthy:
|
||||||
return True
|
return True
|
||||||
@@ -154,7 +153,7 @@ class Integer(SettingType):
|
|||||||
"""
|
"""
|
||||||
Relies on integer casting to convert the user string
|
Relies on integer casting to convert the user string
|
||||||
"""
|
"""
|
||||||
if userstr.lower() == "none":
|
if not userstr or userstr.lower() == "none":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -222,7 +221,7 @@ class String(SettingType):
|
|||||||
Check that the user-entered string is of the correct length.
|
Check that the user-entered string is of the correct length.
|
||||||
Accept "None" to unset.
|
Accept "None" to unset.
|
||||||
"""
|
"""
|
||||||
if userstr.lower() == "none":
|
if not userstr or userstr.lower() == "none":
|
||||||
# Unsetting case
|
# Unsetting case
|
||||||
return None
|
return None
|
||||||
elif cls._maxlen is not None and len(userstr) > cls._maxlen:
|
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.
|
Pass to the channel seeker utility to find the requested channel.
|
||||||
Handle `0` and variants of `None` to unset.
|
Handle `0` and variants of `None` to unset.
|
||||||
"""
|
"""
|
||||||
if userstr.lower() in ('0', 'none'):
|
if userstr.lower() in ('', '0', 'none'):
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
channel = await ctx.find_channel(userstr, interactive=True, chan_type=cls._chan_type)
|
channel = await ctx.find_channel(userstr, interactive=True, chan_type=cls._chan_type)
|
||||||
@@ -296,13 +295,17 @@ class Channel(SettingType):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def _format_data(cls, id: int, data: Optional[int], **kwargs):
|
def _format_data(cls, id: int, data: Optional[int], **kwargs):
|
||||||
"""
|
"""
|
||||||
Retrieve an artificially created channel mention.
|
Retrieve the channel mention, if the channel still exists.
|
||||||
If the channel does not exist, this will show up as invalid-channel.
|
If the channel no longer exists, or cannot be seen by the client, returns None.
|
||||||
"""
|
"""
|
||||||
if data is None:
|
if data is None:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return "<#{}>".format(data)
|
channel = client.get_channel(data)
|
||||||
|
if channel:
|
||||||
|
return channel.mention
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class VoiceChannel(Channel):
|
class VoiceChannel(Channel):
|
||||||
@@ -375,7 +378,7 @@ class Role(SettingType):
|
|||||||
Pass to the role seeker utility to find the requested role.
|
Pass to the role seeker utility to find the requested role.
|
||||||
Handle `0` and variants of `None` to unset.
|
Handle `0` and variants of `None` to unset.
|
||||||
"""
|
"""
|
||||||
if userstr.lower() in ('0', 'none'):
|
if userstr.lower() in ('', '0', 'none'):
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
role = await ctx.find_role(userstr, create=cls._parse_create, interactive=True)
|
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.
|
Pass to the emoji string parser to get the emoji.
|
||||||
Handle `0` and variants of `None` to unset.
|
Handle `0` and variants of `None` to unset.
|
||||||
"""
|
"""
|
||||||
if userstr.lower() in ('0', 'none'):
|
if userstr.lower() in ('', '0', 'none'):
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return cls._parse_emoji(userstr)
|
return cls._parse_emoji(userstr)
|
||||||
@@ -505,7 +508,7 @@ class Timezone(SettingType):
|
|||||||
Check that the user-entered string is of the correct length.
|
Check that the user-entered string is of the correct length.
|
||||||
Accept "None" to unset.
|
Accept "None" to unset.
|
||||||
"""
|
"""
|
||||||
if userstr.lower() == "none":
|
if not userstr or userstr.lower() == "none":
|
||||||
# Unsetting case
|
# Unsetting case
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
@@ -585,7 +588,7 @@ class IntegerEnum(SettingType):
|
|||||||
|
|
||||||
options = {name.lower(): mem.value for name, mem in cls._enum.__members__.items()}
|
options = {name.lower(): mem.value for name, mem in cls._enum.__members__.items()}
|
||||||
|
|
||||||
if userstr == "none":
|
if not userstr or userstr == "none":
|
||||||
# Unsetting case
|
# Unsetting case
|
||||||
return None
|
return None
|
||||||
elif userstr not in options:
|
elif userstr not in options:
|
||||||
@@ -654,7 +657,7 @@ class Duration(SettingType):
|
|||||||
"""
|
"""
|
||||||
Parse the provided duration.
|
Parse the provided duration.
|
||||||
"""
|
"""
|
||||||
if userstr.lower() == "none":
|
if not userstr or userstr.lower() == "none":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if cls._default_multiplier and userstr.isdigit():
|
if cls._default_multiplier and userstr.isdigit():
|
||||||
@@ -949,12 +952,13 @@ class SettingList(SettingType):
|
|||||||
Splits the user string across `,` to break up the list.
|
Splits the user string across `,` to break up the list.
|
||||||
Handle `0` and variants of `None` to unset.
|
Handle `0` and variants of `None` to unset.
|
||||||
"""
|
"""
|
||||||
if userstr.lower() in ('0', 'none'):
|
if userstr.lower() in ('', '0', 'none'):
|
||||||
return []
|
return []
|
||||||
else:
|
else:
|
||||||
data = []
|
data = []
|
||||||
for item in userstr.split(','):
|
items = (item.strip() for item in userstr.split(','))
|
||||||
data.append(await cls._setting._parse_userstr(ctx, id, item.strip()))
|
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:
|
if cls._force_unique:
|
||||||
data = list(set(data))
|
data = list(set(data))
|
||||||
|
|||||||
Reference in New Issue
Block a user