async def test_chunked(self) -> None: self.assertEqual( [chunk async for chunk in mit.chunked(_gen(), 2)], [[0, 1], [2, 3], [4]] ) self.assertEqual( [chunk async for chunk in mit.chunked(range(5), 2)], [[0, 1], [2, 3], [4]] )
async def purge(self, ctx, num: int, *targets: User): """Bulk delete messages of a certain amount posted by some targeted Discord users, if not providedit just deletes all the messages which it encounters""" total_deleted = 0 messages = ctx.history(limit=None) await messages.__anext__() # includes the invoker's message, so skip that. # discord has a bulk-deletion method which has limits that, # messages cannot be deleted older than 14 days. # cannot delete more than 100 messages at once. async for chunk in chunked(map(lambda m: m[1], takewhile( lambda m: m[0] < num and (ctx.message.created_at - m[1].created_at).days < 14, filterfalse(lambda m: not(m[1].author in targets or not targets), aenumerate(messages)))), 100): chunk = list(chunk) await ctx.channel.delete_messages(chunk) # delete 100 messages at once. total_deleted += len(chunk) # for the rest follow the manual deletion way. async for msg in messages: if not total_deleted <= num: await ctx.send(f"Purged {num} messages in {ctx.channel.mention}.", delete_after=8) break if msg.author in targets or targets is None: await msg.delete() total_deleted += 1
async def handle_event( self, event: Dict[bytes, bytes]) -> AsyncIterable[Dict[str, Any]]: try: history_dump = HistoryDumpReceived.deserialize(event) await User.get(id=history_dump.user_id) except (ValidationError, AttributeError) as e: if event and "opening" not in event: logger.exception(str(e)) except DoesNotExist: logger.exception("There is no user with id {user_id}", user_id=history_dump.user_id) else: logger.info( "Dump received {dump} n_items:{n_items}", dump=history_dump.id, n_items=len(history_dump.items), ) history_dump = history_dump.dict() async for chunk in chunked(history_dump["items"], HISTORY_DISTRIBUTOR_CHUNK_LENGTH): chunk = { "user_id": history_dump["user_id"], "items": list(chunk) } data = {"data": orjson.dumps(chunk)} yield data logger.info( "Dump distributed {dump} n_items:{n_items}", dump=history_dump["id"], n_items=len(history_dump["items"]), )
async def purge(self, ctx, end: MessageConverter, begin: MessageConverter = None): """Delete a range of messages defined by two sentinel messages (inclusive)""" total = 0 # deleted total ref_time = ctx.message.created_at messages = ctx.history(after=end, before=begin, oldest_first=False) if not begin: messages.__anext__() # skip invoking message # message matching is defined mathematically as, # Selection = [Begin..End] ⊆ Messages # # for bulk-deletion specifically, # BulkDelete = {m ∈ Selection | Age(m) ≤ 14} [(2 ≤ |Selection| ≤ 100)] bulk_msgs = takewhile(lambda m: (ref_time - m.created_at).days < 15, messages) async for bulk in chunked(bulk_msgs, 100): if len(bulk) == 1: messages = chain(bulk, messages) break # queue remaining for manual deletion. await ctx.channel.delete_messages(bulk) # 100 snowflakes/request total += len(bulk) # if can't bulk delete anymore, do manual. async for m in messages: await m.delete() total += 1 await end.delete() await ctx.send(f"Purged {total+1} messages in {ctx.channel.mention}")
async def test_chunked_empty(self) -> None: self.assertEqual([], [chunk async for chunk in mit.chunked(_empty(), 2)])