diff --git a/bot.py b/bot.py index 32755ba..a15734d 100644 --- a/bot.py +++ b/bot.py @@ -325,11 +325,11 @@ async def maint(interaction: discord.Interaction, status: bool = True): await interaction.response.send_message("๐Ÿ› ๏ธ Toggling maintenance mode... please wait") await STATE_MANAGER.set_maint(status=status) - active_guild_ids = STATE_MANAGER.all_active_guild_ids() for guild_id in active_guild_ids: text_channel = bot.get_channel(STATE_MANAGER.get_state(guild_id, 'text_channel_id')) - if status: + is_active = STATE_MANAGER.get_state(guild_id, 'is_active') or False + if status and is_active is True: embed_data = { 'title': "Maintenance", 'color': 0xfce053, @@ -337,20 +337,32 @@ async def maint(interaction: discord.Interaction, status: bool = True): 'timestamp': str(datetime.datetime.now(datetime.UTC)), } await stop_playback(bot.get_guild(guild_id)) + STATE_MANAGER.set_state(guild_id, 'was_active', True) + embed = discord.Embed.from_dict(embed_data) + await text_channel.send(embed=embed) else: - embed_data = { - 'title': "Maintenance", - 'color': 0xfce053, - 'description': "Maintenance has concluded.", - 'timestamp': str(datetime.datetime.now(datetime.UTC)), + was_active = STATE_MANAGER.get_state(guild_id, 'was_active') or False + if was_active is True and status == False: + embed_data = { + 'title': "Maintenance", + 'color': 0xfce053, + 'description': "Maintenance has concluded.", + 'timestamp': str(datetime.datetime.now(datetime.UTC)), } - embed = discord.Embed.from_dict(embed_data) - await text_channel.send(embed=embed) + embed = discord.Embed.from_dict(embed_data) + await text_channel.send(embed=embed) + if status: + await interaction.edit_original_response(content="๐Ÿ’พ saving state...") + await STATE_MANAGER.save_state() + asyncio.sleep(5) await interaction.edit_original_response(content="๐Ÿ‘ท Maintenance mode enabled") else: - STATE_MANAGER.clear_state() + await interaction.edit_original_response(content="๐Ÿงผ Purging State + DB...") + STATE_MANAGER.clear_state(force=True) await STATE_MANAGER.clear_state_db() + asyncio.sleep(5) + await STATE_MANAGER.set_maint(status=status) await interaction.edit_original_response(content="๐Ÿ‘ท Maintenance mode disabled") else: @@ -948,6 +960,7 @@ def stream_finished_callback(error): STATE_MANAGER.set_state(interaction.guild.id, 'current_stream_url', url) STATE_MANAGER.set_state(interaction.guild.id, 'text_channel_id', interaction.channel.id) STATE_MANAGER.set_state(interaction.guild.id, 'start_time', datetime.datetime.now(datetime.UTC)) + STATE_MANAGER.set_state(interaction.guild.id, 'is_active', True) # And let the user know what song is playing await send_song_info(interaction.guild.id) diff --git a/models/models.py b/models/models.py index 8c33fc3..742530b 100644 --- a/models/models.py +++ b/models/models.py @@ -23,19 +23,23 @@ class GuildState(Base): # text_channel = Text channel original play command came from # start_time = Time the current stream started playing # last_active_user_time = Time the last active user was spotted in the voice channel + # is_active = Boolean for if the bot is currently active in the guild True|None + # was_active = Boolean for if the bot was active before going into maintenance True|None # cleaning_up = Boolean for if the bot is currently stopping/cleaning up True|None # health_error_count = Int number of times a health error occurred in a row # ffmpeg_process_pid = PID for the FFMPEG process associated with the guild __tablename__ = "guild_state" guild_id: Mapped[int] = mapped_column(primary_key=True) - current_stream_url: Mapped[str] = mapped_column(String) - private_stream: Mapped[bool] = mapped_column(Boolean) - text_channel_id: Mapped[int] = mapped_column(Integer) - start_time: Mapped[datetime] = mapped_column(DateTime) - last_active_user_time: Mapped[datetime] = mapped_column(DateTime) - cleaning_up: Mapped[bool] = mapped_column(Boolean) - ffmpeg_process_pid: Mapped[int] = mapped_column(Integer) + current_stream_url: Mapped[str] = mapped_column(String, nullable=True) + private_stream: Mapped[bool] = mapped_column(Boolean, nullable=True) + text_channel_id: Mapped[int] = mapped_column(Integer, nullable=True) + start_time: Mapped[datetime] = mapped_column(DateTime, nullable=True) + last_active_user_time: Mapped[datetime] = mapped_column(DateTime, nullable=True) + cleaning_up: Mapped[bool] = mapped_column(Boolean, nullable=True) + is_active: Mapped[bool] = mapped_column(Boolean, nullable=True) + was_active: Mapped[bool] = mapped_column(Boolean, nullable=True) + ffmpeg_process_pid: Mapped[int] = mapped_column(Integer, nullable=True) health_error_count: list[{ErrorStates, int}] = [] class BotState(Base): diff --git a/services/state_manager.py b/services/state_manager.py index a4e3033..835c2a8 100644 --- a/services/state_manager.py +++ b/services/state_manager.py @@ -12,6 +12,8 @@ class StateManager: # text_channel = Text channel original play command came from # start_time = Time the current stream started playing # last_active_user_time = Time the last active user was spotted in the voice channel + # is_active = Boolean for if the bot is currently active in the guild True|None + # was_active = Boolean for if the bot was active before going into maintenance True|None # cleaning_up = Boolean for if the bot is currently stopping/cleaning up True|None # health_error_count = Int number of times a health error occurred in a row # ffmpeg_process_pid = PID for the FFMPEG process associated with the guild @@ -72,7 +74,8 @@ def clear_state(self, guild_id: int=None, force: bool=False): saved_state = { 'text_channel_id': self.guild_state[guild_id].text_channel_id, - 'private_stream': self.guild_state[guild_id].private_stream + 'private_stream': self.guild_state[guild_id].private_stream, + 'was_active': self.guild_state[guild_id].was_active } self.guild_state[guild_id] = GuildState() if not force: @@ -82,7 +85,7 @@ def clear_state(self, guild_id: int=None, force: bool=False): # Update maintenance status async def set_maint(self, status: bool): self.bot_state.maint = status - await self.save_state() + # await self.save_state() def get_maint(self): return self.bot_state.maint @@ -94,11 +97,11 @@ def all_active_guild_ids(self): guild = self.bot.get_guild(guild_id) # Sometimes we need to exclude some state variables when considering if the guild is active - vars_to_exclude = ['cleaning_up', 'text_channel_id', 'private_stream'] + vars_to_exclude = ['cleaning_up', 'text_channel_id', 'private_stream', 'is_active', 'was_active'] temp_state = {key: value for key, value in self.get_state(guild_id).items() if key not in vars_to_exclude} state_active = bool(temp_state) - vc_active = guild and guild.voice_client and guild.voice_client.is_connected() + vc_active = guild and guild.voice_client and guild.voice_client.is_connected() if state_active or vc_active: active_ids.append(guild_id) return active_ids @@ -127,4 +130,5 @@ async def clear_state_db(self): async with self.ASYNC_SESSION_LOCAL() as session: stmt = delete(GuildState) await session.execute(stmt) + session.add(self.bot_state) await session.commit()