import asyncio import logging from typing import Optional import discord from bot.display import StatsDisplay from bot.reactor import human_jitter from bot.storage import Storage log = logging.getLogger(__name__) async def undo_all_reactions(client: discord.Client, storage: Storage, display: Optional[StatsDisplay] = None) -> None: """Remove every reaction this script has previously logged, then clear those log entries.""" rows = await asyncio.to_thread(storage.get_all_reactions) total = len(rows) log.info("Undo: removing %d previously-added reaction(s)...", total) removed = 0 for i, row in enumerate(rows, start=1): channel_id = int(row["channel_id"]) message_id = int(row["message_id"]) emoji = row["emoji"] should_clear_log = True try: channel = client.get_channel(channel_id) or await client.fetch_channel(channel_id) message = await channel.fetch_message(message_id) await message.remove_reaction(emoji, client.user) removed += 1 log.info("[%d/%d] Removed reaction from message %s", i, total, message_id) except discord.NotFound: log.info("[%d/%d] Message/reaction %s already gone, clearing log entry", i, total, message_id) except discord.HTTPException: log.exception("[%d/%d] Failed to remove reaction from message %s, will retry next run", i, total, message_id) should_clear_log = False if should_clear_log: await asyncio.to_thread(storage.delete_reaction, row["id"]) if display is not None: await display.refresh() await asyncio.sleep(human_jitter()) log.info("Undo complete: removed %d/%d reaction(s)", removed, total)