(sessions): Complete launch and init pathway.
This commit is contained in:
@@ -1,14 +1,23 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import discord
|
import discord
|
||||||
|
import logging
|
||||||
|
import traceback
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
from utils.lib import utc_now
|
from utils.lib import utc_now
|
||||||
|
from data import tables
|
||||||
|
from core import Lion
|
||||||
|
|
||||||
from ..module import module
|
from ..module import module
|
||||||
from .data import current_sessions
|
from .data import current_sessions, SessionChannelType
|
||||||
from .settings import untracked_channels, hourly_reward, hourly_live_bonus, max_daily_study
|
from .settings import untracked_channels, hourly_reward, hourly_live_bonus, max_daily_study
|
||||||
|
|
||||||
|
|
||||||
class Session:
|
class Session:
|
||||||
|
"""
|
||||||
|
A `Session` is a guild member that is currently studying (i.e. that is in a tracked voice channel).
|
||||||
|
This class acts as an opaque interface to the corresponding `sessions` data row.
|
||||||
|
"""
|
||||||
# TODO: Slots
|
# TODO: Slots
|
||||||
sessions = defaultdict(dict)
|
sessions = defaultdict(dict)
|
||||||
|
|
||||||
@@ -37,13 +46,20 @@ class Session:
|
|||||||
if userid in cls.sessions[guildid]:
|
if userid in cls.sessions[guildid]:
|
||||||
raise ValueError("A session for this member already exists!")
|
raise ValueError("A session for this member already exists!")
|
||||||
# TODO: Handle daily study cap
|
# TODO: Handle daily study cap
|
||||||
# TODO: Calculate channel type
|
|
||||||
# TODO: Ensure lion
|
# TODO: More reliable channel type determination
|
||||||
|
if state.channel.id in tables.rented.row_cache:
|
||||||
|
channel_type = SessionChannelType.RENTED
|
||||||
|
elif state.channel.id in tables.accountability_rooms.row_cache:
|
||||||
|
channel_type = SessionChannelType.ACCOUNTABILITY
|
||||||
|
else:
|
||||||
|
channel_type = SessionChannelType.STANDARD
|
||||||
|
|
||||||
current_sessions.create_row(
|
current_sessions.create_row(
|
||||||
guildid=guildid,
|
guildid=guildid,
|
||||||
userid=userid,
|
userid=userid,
|
||||||
channelid=state.channel.id,
|
channelid=state.channel.id,
|
||||||
channel_type=None,
|
channel_type=channel_type,
|
||||||
start_time=now,
|
start_time=now,
|
||||||
live_start=now if (state.self_video or state.self_stream) else None,
|
live_start=now if (state.self_video or state.self_stream) else None,
|
||||||
stream_start=now if state.self_stream else None,
|
stream_start=now if state.self_stream else None,
|
||||||
@@ -101,6 +117,7 @@ async def session_voice_tracker(client, member, before, after):
|
|||||||
Voice update event dispatcher for study session tracking.
|
Voice update event dispatcher for study session tracking.
|
||||||
"""
|
"""
|
||||||
guild = member.guild
|
guild = member.guild
|
||||||
|
Lion.fetch(guild.id, member.id)
|
||||||
session = Session.get(guild.id, member.id)
|
session = Session.get(guild.id, member.id)
|
||||||
|
|
||||||
if before.channel == after.channel:
|
if before.channel == after.channel:
|
||||||
@@ -112,6 +129,7 @@ async def session_voice_tracker(client, member, before, after):
|
|||||||
# Member changed channel
|
# Member changed channel
|
||||||
# End the current session and start a new one, if applicable
|
# End the current session and start a new one, if applicable
|
||||||
# TODO: Max daily study session tasks
|
# TODO: Max daily study session tasks
|
||||||
|
# TODO: Error if before is None but we have a current session
|
||||||
if session:
|
if session:
|
||||||
# End the current session
|
# End the current session
|
||||||
session.finish()
|
session.finish()
|
||||||
@@ -135,8 +153,108 @@ async def _init_session_tracker(client):
|
|||||||
update them depending on the current voice states,
|
update them depending on the current voice states,
|
||||||
and attach the voice event handler.
|
and attach the voice event handler.
|
||||||
"""
|
"""
|
||||||
|
# Ensure the client caches are ready and guilds are chunked
|
||||||
await client.wait_until_ready()
|
await client.wait_until_ready()
|
||||||
|
|
||||||
|
# Pre-cache the untracked channels
|
||||||
await untracked_channels.launch_task(client)
|
await untracked_channels.launch_task(client)
|
||||||
|
|
||||||
|
# Log init start and define logging counters
|
||||||
|
client.log(
|
||||||
|
"Loading ongoing study sessions.",
|
||||||
|
context="SESSION_INIT",
|
||||||
|
level=logging.DEBUG
|
||||||
|
)
|
||||||
|
resumed = 0
|
||||||
|
ended = 0
|
||||||
|
|
||||||
|
# Grab all ongoing sessions from data
|
||||||
|
rows = current_sessions.fetch_rows_where()
|
||||||
|
|
||||||
|
# Iterate through, resume or end as needed
|
||||||
|
for row in rows:
|
||||||
|
if (guild := client.get_guild(row.guildid)) is not None and row.channelid is not None:
|
||||||
|
try:
|
||||||
|
# Load the Session
|
||||||
|
session = Session(row.guildid, row.userid)
|
||||||
|
|
||||||
|
# Find the channel and member voice state
|
||||||
|
voice = None
|
||||||
|
if channel := guild.get_channel(row.channelid):
|
||||||
|
voice = next((member.voice for member in channel.members if member.id == row.userid), None)
|
||||||
|
|
||||||
|
# Resume or end as required
|
||||||
|
if voice and voice.channel:
|
||||||
|
client.log(
|
||||||
|
"Resuming ongoing session: {}".format(row),
|
||||||
|
context="SESSION_INIT",
|
||||||
|
level=logging.DEBUG
|
||||||
|
)
|
||||||
|
Session.sessions[row.guildid][row.userid] = session
|
||||||
|
session.save_live_status(voice)
|
||||||
|
resumed += 1
|
||||||
|
else:
|
||||||
|
client.log(
|
||||||
|
"Ending already completed session: {}".format(row),
|
||||||
|
context="SESSION_INIT",
|
||||||
|
level=logging.DEBUG
|
||||||
|
)
|
||||||
|
session.finish()
|
||||||
|
ended += 1
|
||||||
|
except Exception:
|
||||||
|
# Fatal error
|
||||||
|
client.log(
|
||||||
|
"Fatal error occurred initialising session: {}\n{}".format(row, traceback.format_exc()),
|
||||||
|
context="SESSION_INIT",
|
||||||
|
level=logging.CRITICAL
|
||||||
|
)
|
||||||
|
module.ready = False
|
||||||
|
return
|
||||||
|
|
||||||
|
# Log resumed sessions
|
||||||
|
client.log(
|
||||||
|
"Resumed {} ongoing study sessions, and ended {}.".format(resumed, ended),
|
||||||
|
context="SESSION_INIT",
|
||||||
|
level=logging.INFO
|
||||||
|
)
|
||||||
|
|
||||||
|
# Now iterate through members of all tracked voice channels
|
||||||
|
# Start sessions if they don't already exist
|
||||||
|
tracked_channels = [
|
||||||
|
channel
|
||||||
|
for guild in client.guilds
|
||||||
|
for channel in guild.voice_channels
|
||||||
|
if channel.members and channel.id not in untracked_channels.get(guild.id).data
|
||||||
|
]
|
||||||
|
new_members = [
|
||||||
|
member
|
||||||
|
for channel in tracked_channels
|
||||||
|
for member in channel.members
|
||||||
|
if not Session.get(member.guild.id, member.id)
|
||||||
|
]
|
||||||
|
for member in new_members:
|
||||||
|
client.log(
|
||||||
|
"Starting new session for '{}' (uid: {}) in '{}' (cid: {}) of '{}' (gid: {})".format(
|
||||||
|
member.name,
|
||||||
|
member.id,
|
||||||
|
member.voice.channel.name,
|
||||||
|
member.voice.channel.id,
|
||||||
|
member.guild.name,
|
||||||
|
member.guild.id
|
||||||
|
),
|
||||||
|
context="SESSION_INIT",
|
||||||
|
level=logging.DEBUG
|
||||||
|
)
|
||||||
|
Session.start(member, member.voice)
|
||||||
|
|
||||||
|
# Log newly started sessions
|
||||||
|
client.log(
|
||||||
|
"Started {} new study sessions from current voice channel members.".format(len(new_members)),
|
||||||
|
context="SESSION_INIT",
|
||||||
|
level=logging.INFO
|
||||||
|
)
|
||||||
|
|
||||||
|
# Now that we are in a valid initial state, attach the session event handler
|
||||||
client.add_after_event("voice_state_update", session_voice_tracker)
|
client.add_after_event("voice_state_update", session_voice_tracker)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user