async def my_exec(self, ctx: commands.Context, *args, **kwargs) -> bool: tasks: List[asyncio.Task] = [ asyncio.create_task( ctx.bot.wait_for("message", check=MessagePredicate.cancelled(ctx))), asyncio.create_task(self._my_exec(ctx, *args, **kwargs)), ] async with ctx.typing(): done, pending = await asyncio.wait( tasks, return_when=asyncio.FIRST_COMPLETED) for task in pending: task.cancel() result = done.pop().result() if isinstance(result, bool): return result # wait_for finished # do nothing return False
async def my_exec(self, ctx: commands.Context, *args, **kwargs) -> None: tasks = [ ctx.bot.wait_for("message", check=MessagePredicate.cancelled(ctx)), self._my_exec(ctx, *args, **kwargs), ] async with ctx.typing(): done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED) for task in pending: task.cancel() for task in done: result = task.result() if not result: # _my_exec finished return # wait_for finished assert isinstance(result, discord.Message) if not ctx.channel.permissions_for( ctx.me ).add_reactions or not await ctx.react_quietly("\N{CROSS MARK}"): await ctx.send("Cancelled.")
async def logsfrom( self, ctx, after: Optional[discord.PartialMessage] = None, before: Optional[discord.PartialMessage] = None, *, channel: discord.TextChannel = None, ): """ Logs the specified channel into a file, then uploads the file. The channel will default to the current channel if none is specified. The limit may be the number of messages to log or the ID of the message to start after, exclusive. All timestamps are in UTC. """ if channel: ctxc = copy(ctx) ctxc.channel = channel else: channel = ctx.channel ctxc = ctx if not channel.permissions_for(ctx.me).read_message_history: raise commands.BotMissingPermissions(["read_message_history"]) if not await check_permissions(ctxc, {"read_message_history": True}): raise commands.MissingPermissions(["read_message_history"]) after, before = getattr(after, "id", after), getattr(before, "id", before) cancel_task = asyncio.ensure_future( ctx.bot.wait_for("message", check=MessagePredicate.cancelled(ctx))) async with ctx.typing(): kwargs = {"oldest_first": False} if not after and not before: kwargs["limit"] = 100 elif not before: kwargs.update(after=discord.Object(id=after), limit=after) elif not after: raise RuntimeError("This should never happen.") else: before = min((ctx.message.id, before)) # TODO: wtf should this shit even *mean* if after >= before: kwargs.update(after=discord.Object(id=after), limit=before, oldest_first=True) else: kwargs.update( after=discord.Object(id=after), before=discord.Object(id=before), limit=min((before, after)), ) print(kwargs) stream = io.BytesIO() last_h = MHeaders(None, None, None) message_task = asyncio.ensure_future(history(channel, **kwargs)) done, _ = await asyncio.wait((cancel_task, message_task), return_when=asyncio.FIRST_COMPLETED) if cancel_task in done: message_task.cancel() return await ctx.send(_T("Okay, I've cancelled my logging.")) messages = message_task.result() processed = 0 pop = messages.popleft if kwargs["oldest_first"] else messages.pop while messages: await asyncio.sleep(0) if cancel_task.done(): return await ctx.send( _T("Okay, I've cancelled my logging.")) message = pop() now_h = MHeaders(message.author, message.created_at, message.edited_at) headers = now_h.to_str(last_h) last_h = now_h if headers: stream.write(headers.encode("utf-8")) stream.write(message.clean_content.encode("utf-8")) if message.attachments: stream.write(b"\n") stream.write("; ".join( f"[{a.filename}]({a.url})" for a in message.attachments).encode("utf-8")) stream.write(b"\n") processed += 1 cancel_task.cancel() stream.seek(0) return await ctx.send( content=_T("{} message{s} logged.").format( processed, s=("" if processed == 1 else "s")), file=discord.File(stream, filename=f"{channel.name}.md"), delete_after=300, )