text (rooms): Migrate to scheduled sessions.

This commit is contained in:
2022-01-20 10:31:44 +02:00
parent f2e4cfae8b
commit 0483233c25
4 changed files with 84 additions and 81 deletions

View File

@@ -99,7 +99,7 @@ class TimeSlot:
colour=discord.Colour.orange(), colour=discord.Colour.orange(),
timestamp=self.start_time timestamp=self.start_time
).set_footer( ).set_footer(
text="About to start!\nJoin the session with {}rooms book".format(client.prefix) text="About to start!\nJoin the session with {}schedule book".format(client.prefix)
) )
if self.members: if self.members:
@@ -111,7 +111,7 @@ class TimeSlot:
) )
) )
else: else:
embed.description = "No members booked for this session!" embed.description = "No members scheduled this session!"
return embed return embed
@@ -125,7 +125,7 @@ class TimeSlot:
description="Finishing <t:{}:R>.".format(timestamp + 3600), description="Finishing <t:{}:R>.".format(timestamp + 3600),
colour=discord.Colour.orange(), colour=discord.Colour.orange(),
timestamp=self.start_time timestamp=self.start_time
).set_footer(text="Join the next session using {}rooms book".format(client.prefix)) ).set_footer(text="Join the next session using {}schedule book".format(client.prefix))
if self.members: if self.members:
classifications = { classifications = {
@@ -158,7 +158,7 @@ class TimeSlot:
if value: if value:
embed.add_field(name=field, value='\n'.join(value)) embed.add_field(name=field, value='\n'.join(value))
else: else:
embed.description = "No members booked for this session!" embed.description = "No members scheduled this session!"
return embed return embed
@@ -210,7 +210,7 @@ class TimeSlot:
if value: if value:
embed.add_field(name=field, value='\n'.join(value)) embed.add_field(name=field, value='\n'.join(value))
else: else:
embed.description = "No members booked this session!" embed.description = "No members scheduled this session!"
return embed return embed
@@ -316,13 +316,13 @@ class TimeSlot:
if self.data and not self.channel: if self.data and not self.channel:
try: try:
self.channel = await self.guild.create_voice_channel( self.channel = await self.guild.create_voice_channel(
"Upcoming Accountability Study Room", "Upcoming Scheduled Session",
overwrites=overwrites, overwrites=overwrites,
category=self.category category=self.category
) )
except discord.HTTPException: except discord.HTTPException:
GuildSettings(self.guild.id).event_log.log( GuildSettings(self.guild.id).event_log.log(
"Failed to create the accountability voice channel. Skipping this session.", "Failed to create the scheduled session voice channel. Skipping this session.",
colour=discord.Colour.red() colour=discord.Colour.red()
) )
return None return None
@@ -337,7 +337,7 @@ class TimeSlot:
) )
except discord.HTTPException: 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 scheduled session lobby {}.\n"
"Skipping this session.".format(self.lobby.mention), "Skipping this session.".format(self.lobby.mention),
colour=discord.Colour.red() colour=discord.Colour.red()
) )
@@ -351,7 +351,7 @@ class TimeSlot:
Ghost pings the session members in the lobby channel. Ghost pings the session members in the lobby channel.
""" """
if self.members: if self.members:
content = content or "Your accountability session has opened! Please join!" content = content or "Your scheduled session has started! Please join!"
out = "{}\n\n{}".format( out = "{}\n\n{}".format(
content, content,
' '.join('<@{}>'.format(memid) for memid, mem in self.members.items() if not mem.has_attended) ' '.join('<@{}>'.format(memid) for memid, mem in self.members.items() if not mem.has_attended)
@@ -366,7 +366,7 @@ class TimeSlot:
""" """
if self.channel: if self.channel:
try: try:
await self.channel.edit(name="Accountability Study Room") await self.channel.edit(name="Scheduled Session Room")
await self.channel.set_permissions(self.guild.default_role, view_channel=True, connect=False) await self.channel.set_permissions(self.guild.default_role, view_channel=True, connect=False)
except discord.HTTPException: except discord.HTTPException:
pass pass
@@ -388,7 +388,7 @@ class TimeSlot:
await asyncio.sleep(delay) await asyncio.sleep(delay)
embed = discord.Embed( embed = discord.Embed(
title="Your accountability session has started!", title="The scheduled session you booked has started!",
description="Please join {}.".format(self.channel.mention), description="Please join {}.".format(self.channel.mention),
colour=discord.Colour.orange() colour=discord.Colour.orange()
).set_footer( ).set_footer(

View File

@@ -9,19 +9,19 @@ from .tracker import AccountabilityGuild as AG
@GuildSettings.attach_setting @GuildSettings.attach_setting
class accountability_category(settings.Channel, settings.GuildSetting): class accountability_category(settings.Channel, settings.GuildSetting):
category = "Accountability Rooms" category = "Scheduled Sessions"
attr_name = "accountability_category" attr_name = "accountability_category"
_data_column = "accountability_category" _data_column = "accountability_category"
display_name = "accountability_category" display_name = "session_category"
desc = "Category in which to make the accountability rooms." desc = "Category in which to make the scheduled session rooms."
_default = None _default = None
long_desc = ( long_desc = (
"\"Accountability\" category channel.\n" "\"Schedule session\" category channel.\n"
"The accountability voice channels will be created here." "Scheduled sessions will be held in voice channels created under this category."
) )
_accepts = "A category channel." _accepts = "A category channel."
@@ -33,9 +33,9 @@ class accountability_category(settings.Channel, settings.GuildSetting):
# TODO Move this somewhere better # TODO Move this somewhere better
if self.id not in AG.cache: if self.id not in AG.cache:
AG(self.id) AG(self.id)
return "The accountability category has been changed to **{}**.".format(self.value.name) return "The session category has been changed to **{}**.".format(self.value.name)
else: else:
return "The accountability system has been started in **{}**.".format(self.value.name) return "The scheduled session system has been started in **{}**.".format(self.value.name)
else: else:
if self.id in AG.cache: if self.id in AG.cache:
aguild = AG.cache.pop(self.id) aguild = AG.cache.pop(self.id)
@@ -43,26 +43,26 @@ class accountability_category(settings.Channel, settings.GuildSetting):
asyncio.create_task(aguild.current_slot.cancel()) asyncio.create_task(aguild.current_slot.cancel())
if aguild.upcoming_slot: if aguild.upcoming_slot:
asyncio.create_task(aguild.upcoming_slot.cancel()) asyncio.create_task(aguild.upcoming_slot.cancel())
return "The accountability system has been shut down." return "The scheduled session system has been shut down."
else: else:
return "The accountability category has been unset." return "The scheduled session category has been unset."
@GuildSettings.attach_setting @GuildSettings.attach_setting
class accountability_lobby(settings.Channel, settings.GuildSetting): class accountability_lobby(settings.Channel, settings.GuildSetting):
category = "Accountability Rooms" category = "Scheduled Sessions"
attr_name = "accountability_lobby" attr_name = "accountability_lobby"
_data_column = attr_name _data_column = attr_name
display_name = attr_name display_name = "session_lobby"
desc = "Category in which to post accountability session status updates." desc = "Category in which to post scheduled session notifications updates."
_default = None _default = None
long_desc = ( long_desc = (
"Accountability session updates will be posted here, and members will be notified in this channel.\n" "Scheduled session updates will be posted here, and members will be notified in this channel.\n"
"The channel will be automatically created in the accountability category if it does not exist.\n" "The channel will be automatically created in the configured `session_category` if it does not exist.\n"
"Members do not need to be able to write in the channel." "Members do not need to be able to write in the channel."
) )
_accepts = "Any text channel." _accepts = "Any text channel."
@@ -76,65 +76,65 @@ class accountability_lobby(settings.Channel, settings.GuildSetting):
@GuildSettings.attach_setting @GuildSettings.attach_setting
class accountability_price(settings.Integer, GuildSetting): class accountability_price(settings.Integer, GuildSetting):
category = "Accountability Rooms" category = "Scheduled Sessions"
attr_name = "accountability_price" attr_name = "accountability_price"
_data_column = attr_name _data_column = attr_name
display_name = attr_name display_name = "session_price"
desc = "Cost of booking an accountability time slot." desc = "Cost of booking a scheduled session."
_default = 100 _default = 100
long_desc = ( long_desc = (
"The price of booking each one hour accountability room slot." "The price of booking each one hour scheduled session slot."
) )
_accepts = "An integer number of coins." _accepts = "An integer number of coins."
@property @property
def success_response(self): def success_response(self):
return "Accountability slots now cost `{}` coins.".format(self.value) return "Scheduled session slots now cost `{}` coins.".format(self.value)
@GuildSettings.attach_setting @GuildSettings.attach_setting
class accountability_bonus(settings.Integer, GuildSetting): class accountability_bonus(settings.Integer, GuildSetting):
category = "Accountability Rooms" category = "Scheduled Sessions"
attr_name = "accountability_bonus" attr_name = "accountability_bonus"
_data_column = attr_name _data_column = attr_name
display_name = attr_name display_name = "session_bonus"
desc = "Bonus given when all accountability members attend a time slot." desc = "Bonus given when everyone attends a scheduled session slot."
_default = 1000 _default = 1000
long_desc = ( long_desc = (
"The extra bonus given when all the members who have booked an accountability time slot attend." "The extra bonus given to each scheduled session member when everyone who booked attended the session."
) )
_accepts = "An integer number of coins." _accepts = "An integer number of coins."
@property @property
def success_response(self): def success_response(self):
return "Accountability members will now get `{}` coins if everyone joins.".format(self.value) return "Scheduled session members will now get `{}` coins if everyone joins.".format(self.value)
@GuildSettings.attach_setting @GuildSettings.attach_setting
class accountability_reward(settings.Integer, GuildSetting): class accountability_reward(settings.Integer, GuildSetting):
category = "Accountability Rooms" category = "Scheduled Sessions"
attr_name = "accountability_reward" attr_name = "accountability_reward"
_data_column = attr_name _data_column = attr_name
display_name = attr_name display_name = "session_reward"
desc = "Reward given for attending a booked accountability slot." desc = "The individual reward given when a member attends their booked scheduled session."
_default = 200 _default = 200
long_desc = ( long_desc = (
"Amount given to a member who books an accountability slot and attends it." "Reward given to a member who attends a booked scheduled session."
) )
_accepts = "An integer number of coins." _accepts = "An integer number of coins."
@property @property
def success_response(self): def success_response(self):
return "Accountability members will now get `{}` coins at the end of their slot.".format(self.value) return "Members will now get `{}` coins when they attend their scheduled session.".format(self.value)

View File

@@ -62,28 +62,29 @@ def ensure_exclusive(ctx):
@module.cmd( @module.cmd(
name="rooms", name="schedule",
desc="Schedule an accountability study session.", desc="View your schedule, and get rewarded for attending scheduled sessions!",
group="Productivity" group="Productivity",
aliases=('rooms', 'sessions')
) )
@in_guild() @in_guild()
async def cmd_rooms(ctx): async def cmd_rooms(ctx):
""" """
Usage``: Usage``:
{prefix}rooms {prefix}schedule
{prefix}rooms book {prefix}schedule book
{prefix}rooms cancel {prefix}schedule cancel
Description: Description:
View your accountability profile with `{prefix}rooms`. View your schedule with `{prefix}schedule`.
Use `{prefix}rooms book` to book an accountability session! Use `{prefix}schedule book` to schedule a session at a selected time..
Use `{prefix}rooms cancel` to cancel a booked session. Use `{prefix}schedule cancel` to cancel a scheduled session.
""" """
lower = ctx.args.lower() lower = ctx.args.lower()
splits = lower.split() splits = lower.split()
command = splits[0] if splits else None command = splits[0] if splits else None
if not ctx.guild_settings.accountability_category.value: if not ctx.guild_settings.accountability_category.value:
return await ctx.error_reply("The accountability system isn't set up!") return await ctx.error_reply("The scheduled session system isn't set up!")
# First grab the sessions the member is booked in # First grab the sessions the member is booked in
joined_rows = accountability_member_info.select_where( joined_rows = accountability_member_info.select_where(
@@ -94,7 +95,7 @@ async def cmd_rooms(ctx):
if command == 'cancel': if command == 'cancel':
if not joined_rows: if not joined_rows:
return await ctx.error_reply("You have no bookings to cancel!") return await ctx.error_reply("You have no scheduled sessions to cancel!")
# Show unbooking menu # Show unbooking menu
lines = [ lines = [
@@ -102,9 +103,9 @@ async def cmd_rooms(ctx):
for i, row in enumerate(joined_rows) for i, row in enumerate(joined_rows)
] ]
out_msg = await ctx.reply( out_msg = await ctx.reply(
content="Please reply with the number(s) of the rooms you want to cancel. E.g. `1, 3, 5` or `1-3, 7-8`.", content="Please reply with the number(s) of the sessions you want to cancel. E.g. `1, 3, 5` or `1-3, 7-8`.",
embed=discord.Embed( embed=discord.Embed(
title="Please choose the bookings you want to cancel.", title="Please choose the sessions you want to cancel.",
description='\n'.join(lines), description='\n'.join(lines),
colour=discord.Colour.orange() colour=discord.Colour.orange()
).set_footer( ).set_footer(
@@ -116,7 +117,7 @@ async def cmd_rooms(ctx):
await ctx.cancellable( await ctx.cancellable(
out_msg, out_msg,
cancel_message="Cancel menu closed, no accountability sessions were cancelled.", cancel_message="Cancel menu closed, no scheduled sessions were cancelled.",
timeout=70 timeout=70
) )
@@ -133,7 +134,7 @@ async def cmd_rooms(ctx):
await out_msg.edit( await out_msg.edit(
content=None, content=None,
embed=discord.Embed( embed=discord.Embed(
description="Cancel menu timed out, no accountability sessions were cancelled.", description="Cancel menu timed out, no scheduled sessions were cancelled.",
colour=discord.Colour.red() colour=discord.Colour.red()
) )
) )
@@ -156,7 +157,7 @@ async def cmd_rooms(ctx):
for index in parse_ranges(message.content) if index < len(joined_rows) for index in parse_ranges(message.content) if index < len(joined_rows)
] ]
if not to_cancel: if not to_cancel:
return await ctx.error_reply("No valid bookings selected for cancellation.") return await ctx.error_reply("No valid sessions selected for cancellation.")
elif any(row['start_at'] < utc_now() for row in to_cancel): elif any(row['start_at'] < utc_now() for row in to_cancel):
return await ctx.error_reply("You can't cancel a running session!") return await ctx.error_reply("You can't cancel a running session!")
@@ -189,7 +190,7 @@ async def cmd_rooms(ctx):
remaining = [row for row in joined_rows if row['slotid'] not in slotids] remaining = [row for row in joined_rows if row['slotid'] not in slotids]
if not remaining: if not remaining:
await ctx.embed_reply("Cancelled all your upcoming accountability sessions!") await ctx.embed_reply("Cancelled all your upcoming scheduled sessions!")
else: else:
next_booked_time = min(row['start_at'] for row in remaining) next_booked_time = min(row['start_at'] for row in remaining)
if len(to_cancel) > 1: if len(to_cancel) > 1:
@@ -245,9 +246,11 @@ async def cmd_rooms(ctx):
# TODO: Nicer embed # TODO: Nicer embed
# TODO: Don't allow multi bookings if the member has a bad attendance rate # TODO: Don't allow multi bookings if the member has a bad attendance rate
out_msg = await ctx.reply( out_msg = await ctx.reply(
content="Please reply with the number(s) of the rooms you want to join. E.g. `1, 3, 5` or `1-3, 7-8`.", content=(
"Please reply with the number(s) of the sessions you want to book. E.g. `1, 3, 5` or `1-3, 7-8`."
),
embed=discord.Embed( embed=discord.Embed(
title="Please choose the sessions you want to book.", title="Please choose the sessions you want to schedule.",
description='\n'.join(lines), description='\n'.join(lines),
colour=discord.Colour.orange() colour=discord.Colour.orange()
).set_footer( ).set_footer(
@@ -354,10 +357,10 @@ async def cmd_rooms(ctx):
# Ack purchase # Ack purchase
embed = discord.Embed( embed = discord.Embed(
title="You have booked the following session{}!".format('s' if len(to_book) > 1 else ''), title="You have scheduled the following session{}!".format('s' if len(to_book) > 1 else ''),
description=( description=(
"*Please attend all your booked sessions!*\n" "*Please attend all your scheduled sessions!*\n"
"*If you can't attend, cancel with* `{}rooms cancel`\n\n{}" "*If you can't attend, cancel with* `{}schedule cancel`\n\n{}"
).format( ).format(
ctx.best_prefix, ctx.best_prefix,
'\n'.join(time_format(time) for time in to_book), '\n'.join(time_format(time) for time in to_book),
@@ -365,7 +368,7 @@ async def cmd_rooms(ctx):
colour=discord.Colour.orange() colour=discord.Colour.orange()
).set_footer( ).set_footer(
text=( text=(
"Use {prefix}rooms to see your current bookings.\n" "Use {prefix}schedule to see your current schedule.\n"
).format(prefix=ctx.best_prefix) ).format(prefix=ctx.best_prefix)
) )
try: try:
@@ -400,10 +403,10 @@ async def cmd_rooms(ctx):
if not (history or joined_rows): if not (history or joined_rows):
# First-timer information # First-timer information
about = ( about = (
"You haven't joined any accountability sessions yet!\n" "You haven't scheduled any study sessions yet!\n"
"Book a session by typing **`{}rooms book`** and selecting " "Schedule a session by typing **`{}schedule book`** and selecting "
"the hours you intend to study, " "the hours you intend to study, "
"then attend by joining the accountability voice channel when the session starts!\n" "then attend by joining the session voice channel when it starts!\n"
"Only if everyone attends will they get the bonus of `{}` LionCoins!\n" "Only if everyone attends will they get the bonus of `{}` LionCoins!\n"
"Let's all do our best and keep each other accountable 🔥" "Let's all do our best and keep each other accountable 🔥"
).format( ).format(
@@ -492,7 +495,7 @@ async def cmd_rooms(ctx):
total_count, total_count,
(attended_count * 100) / total_count, (attended_count * 100) / total_count,
), ),
"Time": "**{:02}:{:02}** spent in accountability rooms.".format( "Time": "**{:02}:{:02}** in scheduled sessions.".format(
total_duration // 3600, total_duration // 3600,
(total_duration % 3600) // 60 (total_duration % 3600) // 60
), ),
@@ -558,16 +561,16 @@ async def cmd_rooms(ctx):
) )
booked_field = ( booked_field = (
"{}\n\n" "{}\n\n"
"*If you can't make your booking, please cancel using `{}rooms cancel`!*" "*If you can't make your session, please cancel using `{}schedule cancel`!*"
).format(booked_list, ctx.best_prefix) ).format(booked_list, ctx.best_prefix)
# Temporary footer for acclimatisation # Temporary footer for acclimatisation
# footer = "All times are displayed in your own timezone!" # footer = "All times are displayed in your own timezone!"
footer = "Book another session using {}rooms book".format(ctx.best_prefix) footer = "Book another session using {}schedule book".format(ctx.best_prefix)
else: else:
booked_field = ( booked_field = (
"Your schedule is empty!\n" "Your schedule is empty!\n"
"Book another session using `{}rooms book`." "Book another session using `{}schedule book`."
).format(ctx.best_prefix) ).format(ctx.best_prefix)
footer = "Please keep your DMs open for notifications!" footer = "Please keep your DMs open for notifications!"
@@ -576,7 +579,7 @@ async def cmd_rooms(ctx):
colour=discord.Colour.orange(), colour=discord.Colour.orange(),
description=desc, description=desc,
).set_author( ).set_author(
name="Accountability profile for {}".format(ctx.author.name), name="Schedule statistics for {}".format(ctx.author.name),
icon_url=ctx.author.avatar_url icon_url=ctx.author.avatar_url
).set_footer( ).set_footer(
text=footer, text=footer,

View File

@@ -94,9 +94,9 @@ async def open_next(start_time):
if not slot.category: if not slot.category:
# Log and unload guild # Log and unload guild
aguild.guild_settings.event_log.log( aguild.guild_settings.event_log.log(
"The accountability category couldn't be found!\n" "The scheduled session category couldn't be found!\n"
"Shutting down the accountability system in this server.\n" "Shutting down the scheduled session system in this server.\n"
"To re-activate, please reconfigure `config accountability_category`." "To re-activate, please reconfigure `config session_category`."
) )
AccountabilityGuild.cache.pop(aguild.guildid, None) AccountabilityGuild.cache.pop(aguild.guildid, None)
await slot.cancel() await slot.cancel()
@@ -106,16 +106,16 @@ async def open_next(start_time):
# Create a new lobby # Create a new lobby
try: try:
channel = await guild.create_text_channel( channel = await guild.create_text_channel(
name="accountability-lobby", name="session-lobby",
category=slot.category, category=slot.category,
reason="Automatic creation of accountability lobby." reason="Automatic creation of scheduled session lobby."
) )
aguild.guild_settings.accountability_lobby.value = channel aguild.guild_settings.accountability_lobby.value = channel
slot.lobby = channel slot.lobby = channel
except discord.HTTPException: except discord.HTTPException:
# Event log failure and skip session # Event log failure and skip session
aguild.guild_settings.event_log.log( aguild.guild_settings.event_log.log(
"Failed to create the accountability lobby text channel.\n" "Failed to create the scheduled session lobby text channel.\n"
"Please set the lobby channel manually with `config`." "Please set the lobby channel manually with `config`."
) )
await slot.cancel() await slot.cancel()
@@ -123,7 +123,7 @@ async def open_next(start_time):
# Event log creation # Event log creation
aguild.guild_settings.event_log.log( aguild.guild_settings.event_log.log(
"Automatically created an accountability lobby channel {}.".format(channel.mention) "Automatically created a scheduled session lobby channel {}.".format(channel.mention)
) )
results = await slot.open() results = await slot.open()
@@ -221,7 +221,7 @@ async def turnover():
movement_tasks = ( movement_tasks = (
mem.member.edit( mem.member.edit(
voice_channel=slot.channel, voice_channel=slot.channel,
reason="Moving to booked accountability session." reason="Moving to scheduled session."
) )
for slot in current_slots for slot in current_slots
for mem in slot.members.values() for mem in slot.members.values()
@@ -317,7 +317,7 @@ async def _accountability_loop():
except Exception: except Exception:
# Unknown exception. Catch it so the loop doesn't die. # Unknown exception. Catch it so the loop doesn't die.
client.log( client.log(
"Error while opening new accountability rooms! " "Error while opening new scheduled sessions! "
"Exception traceback follows.\n{}".format( "Exception traceback follows.\n{}".format(
traceback.format_exc() traceback.format_exc()
), ),
@@ -332,7 +332,7 @@ async def _accountability_loop():
except Exception: except Exception:
# Unknown exception. Catch it so the loop doesn't die. # Unknown exception. Catch it so the loop doesn't die.
client.log( client.log(
"Error while starting accountability rooms! " "Error while starting scheduled sessions! "
"Exception traceback follows.\n{}".format( "Exception traceback follows.\n{}".format(
traceback.format_exc() traceback.format_exc()
), ),