From 8b9a2de9c8f09c57c692d137047f42c2314106c0 Mon Sep 17 00:00:00 2001 From: iAmScienceMan <63004048+iAmScienceMan@users.noreply.github.com> Date: Thu, 11 Jun 2026 15:04:14 +0300 Subject: [PATCH 1/2] =?UTF-8?q?=D0=BD=D0=B5=20=D0=B7=D0=B0=D0=B1=D1=8B?= =?UTF-8?q?=D0=B2=D0=B0=D1=82=D1=8C=20=D0=B2=D0=BE=D0=B9=D1=81-=D0=BA?= =?UTF-8?q?=D0=B0=D0=BD=D0=B0=D0=BB=20=D0=B5=D1=81=D0=BB=D0=B8=20=D1=83?= =?UTF-8?q?=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=81=D0=BE=D1=80?= =?UTF-8?q?=D0=B2=D0=B0=D0=BB=D0=BE=D1=81=D1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit чистим bot_created_channels/channel_creators и сохраняем channels.json только после успешного delete (или NotFound). при любой другой ошибке канал остаётся в отслеживаемых и фоновая очистка доберёт его позже. fixes #20 --- cogs/voice.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/cogs/voice.py b/cogs/voice.py index d62d1d9..56578ad 100644 --- a/cogs/voice.py +++ b/cogs/voice.py @@ -120,16 +120,19 @@ async def check_and_cleanup_channel(self, channel): return try: - self.bot.bot_created_channels.discard(channel.id) - self.bot.channel_creators.pop(channel.id, None) - await self.bot.save_channels() - await channel_obj.delete(reason="Пустой канал созданный ботом") log.info("Удален пустой канал: %s", channel.name) except discord.errors.NotFound: log.info("Канал %s уже удален.", channel.name) except Exception: + # канал не удалился — оставляем его в отслеживаемых, + # фоновая очистка попробует ещё раз log.exception("Cleanup error") + return + + self.bot.bot_created_channels.discard(channel.id) + self.bot.channel_creators.pop(channel.id, None) + await self.bot.save_channels() async def cleanup_empty_home_channels(self, guild): """Удаляет все пустые войсы созданные ботом.""" From 0374805ae309193f6c2acd1118a7d0e918cd85e3 Mon Sep 17 00:00:00 2001 From: iAmScienceMan <63004048+iAmScienceMan@users.noreply.github.com> Date: Thu, 11 Jun 2026 15:18:34 +0300 Subject: [PATCH 2/2] =?UTF-8?q?=D1=82=D0=B5=D1=81=D1=82=D1=8B=20=D0=BD?= =?UTF-8?q?=D0=B0=20check=5Fand=5Fcleanup=5Fchannel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_voice.py | 74 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 tests/test_voice.py diff --git a/tests/test_voice.py b/tests/test_voice.py new file mode 100644 index 0000000..b422aab --- /dev/null +++ b/tests/test_voice.py @@ -0,0 +1,74 @@ +import asyncio +from types import SimpleNamespace + +import discord + +from cogs.voice import VoiceCog + + +class FakeChannel: + def __init__(self, error=None): + self.id = 123 + self.name = "/home/test" + self.members = [] + self.error = error + self.deleted = False + + async def delete(self, reason=None): + if self.error: + raise self.error + self.deleted = True + + +def _make_cog(channel): + cog = VoiceCog.__new__(VoiceCog) + + async def save_channels(): + pass + + cog.bot = SimpleNamespace( + bot_created_channels={channel.id}, + channel_creators={channel.id: 1}, + get_channel=lambda cid: channel, + save_channels=save_channels, + ) + return cog + + +def _run_cleanup(cog, channel): + asyncio.run(cog.check_and_cleanup_channel(channel)) + + +def test_cleanup_removes_tracking_on_success(): + channel = FakeChannel() + cog = _make_cog(channel) + _run_cleanup(cog, channel) + assert channel.deleted + assert channel.id not in cog.bot.bot_created_channels + assert channel.id not in cog.bot.channel_creators + + +def test_cleanup_removes_tracking_on_not_found(): + response = SimpleNamespace(status=404, reason="Not Found") + channel = FakeChannel(error=discord.errors.NotFound(response, "gone")) + cog = _make_cog(channel) + _run_cleanup(cog, channel) + assert channel.id not in cog.bot.bot_created_channels + assert channel.id not in cog.bot.channel_creators + + +def test_cleanup_keeps_tracking_on_delete_error(): + channel = FakeChannel(error=RuntimeError("api down")) + cog = _make_cog(channel) + _run_cleanup(cog, channel) + assert channel.id in cog.bot.bot_created_channels + assert channel.id in cog.bot.channel_creators + + +def test_cleanup_skips_channel_with_members(): + channel = FakeChannel() + channel.members = [object()] + cog = _make_cog(channel) + _run_cleanup(cog, channel) + assert not channel.deleted + assert channel.id in cog.bot.bot_created_channels