async def scramble(self, ctx, channel: discord.TextChannel = None, *members: discord.Member): """Generates a message based on the last 1000 messages in a specified channel (or the current one if none was given). """ channel = channel or ctx.channel if channel.is_nsfw() and not ctx.channel.is_nsfw(): return await ctx.send("Cannot post nsfw content in non-nsfw channels.") async with ctx.typing(): if not members: msgs = [m.clean_content async for m in channel.history(limit=1000)] else: msgs = [] c = 0 async for m in channel.history(limit=None): if m.author not in members: continue msgs.append(m.clean_content) c += 1 if c == 1000: break msg = await self.bot.loop.run_in_executor(None, self.generate_message, " ".join(msgs)) if len(msg) >= 2000: await ctx.send("Result was too large! Posting a part of it.") msg = msg[:2000] await ctx.send(msg)
async def _get_auditresults( self, channel: TextChannel, start: datetime, end: Optional[datetime] = None, ) -> AuditResults: """Returns AuditResults from starttime to current, or end if provided""" counter: int = 0 name_set: MutableSet[str] = set() if end is None: history_cor = channel.history(after=start) else: history_cor = channel.history(after=start, before=end) async for past_message in history_cor: counter += 1 name_set.add( f"{past_message.author.display_name},{past_message.author},{past_message.author.id}" # noqa ) return AuditResults( counter=counter, channel=channel.name, channel_id=channel.id, authors=name_set, start=start, end=end, )
async def activity(self, ctx, channel: discord.TextChannel = None): if not channel: channel = ctx.channel text = 'в этом канале' else: text = f'в #{channel.id}' msg = await ctx.send('**Начинаю вычисление...**') counter = 0 yesterday = datetime.datetime.today() - timedelta(days = 1) async for message in channel.history(limit=None, after=yesterday): counter += 1 counter2 = 0 weekago = datetime.datetime.today() - timedelta(weeks = 1) async for message in channel.history(limit=None, after=weekago): counter2 += 1 counter3 = 0 monthago = datetime.datetime.today() - timedelta(weeks = 4) async for message in channel.history(limit=None, after=monthago): counter3 += 1 embed = discord.Embed(title = f'Статиститка сообщений {text}') embed.add_field(name = 'За сегодня:', value = f'{counter}', inline = False) embed.add_field(name = 'За неделю:', value = f'{counter2}', inline = False) embed.add_field(name = 'За месяц:', value = f'{counter3}', inline = False) embed.set_author( name = f'{ctx.author.name}#{ctx.author.discriminator}', icon_url = ctx.author.avatar_url ) embed.set_footer(text = f'Канал: #{channel}') await msg.edit(content = None, embed = embed)
async def is_changed_exercises(exercises: Exercise, channel : discord.TextChannel): async for msg in channel.history(): if len(msg.embeds) > 0: for embed in msg.embeds: # type: discord.Embed if embed.url == exercises.view_url: return True return False
async def wordmap_command(self, ctx: commands.Context, channel: discord.TextChannel): try: wordmap_list = game.Wordmap() async for message in channel.history(limit=1000): words = message.clean_content.split(' ') for word in words: english = True clean_word = '' for letter in word: if letter.isalpha(): clean_word += letter.lower() elif any(letter == x for x in self.IGNORE_SYMBOLS): continue else: english = False break if english and not clean_word.strip() == '': wordmap_list.inc_word(clean_word) top_words = wordmap_list.get_words(100) await ctx.send('```\n' + ' '.join(str(word) for word in top_words) + '\n```') except discord.Forbidden: await ctx.send('I have no access to the channel!')
async def oof( self, ctx: commands.Context, msg_id: int = None, channel: discord.TextChannel = None ) -> None: """ react 🅾🇴🇫 to a message `msg_id` must be the message ID for desited message within the channel `channel` must be the channel where the desired message is defaults to current channel if the bot has manage messages permission it will attempt to delete the command """ emojis = ["🅾", "🇴", "🇫"] if channel is None: channel = ctx.message.channel if msg_id is None: async for messages in channel.history(limit=2): message = messages else: try: message = await channel.fetch_message(msg_id) except AttributeError: message = await channel.get_message(msg_id) # type: ignore # discord.py backwards compatibility support except Exception: await ctx.send( "Message ID {} not found in {}".format(msg_id, channel.mention), delete_after=5 ) return if ctx.channel.permissions_for(ctx.me).manage_messages: await ctx.message.delete() if channel.permissions_for(ctx.me).add_reactions: for emoji in emojis: try: await message.add_reaction(emoji) except discord.errors.Forbidden: return
async def clean_chat(text_channel: TextChannel) -> None: """ Removes all messages in the command channel to prepare for sending a gui. ## Raises ClientException – The number of messages to delete was more than 100. Forbidden – You do not have proper permissions to delete the messages or you’re not using a bot account. NotFound – If single delete, then the message was already deleted. HTTPException – Deleting the messages failed. """ text_channel_messages: List[Message] = [] message: Message async for message in text_channel.history(limit=101): if (datetime.today() - message.created_at).days > 14: raise ClientException( "Text channel contains messages older then 14 days.") if len(text_channel_messages) > 100: raise ClientException("Text channel contains over 100 messages.") text_channel_messages.append(message) logging.info(f"deleted_messages: {str(len(text_channel_messages))}") await text_channel.delete_messages(text_channel_messages)
async def end_competition(self, ctx, channel: TextChannel): """End an already running competition""" query = "SELECT created FROM competition WHERE channel_id = $1" created = await self.bot.pool.fetchval(query, channel.id) if not created: await ctx.send(embed=self.bot.error("No competition in the channel")) return candidates = [] async for message in channel.history(limit=5000, after=created): if message.attachments: if message.attachments[0].height: candidates.append(message) candidates_votes = {} for message in candidates: votes = 0 for reaction in message.reactions: if reaction.emoji == "⬇": votes -= 1 if reaction.emoji == "⬆": votes += 1 candidates_votes[message] = votes ordered = [] for message in sorted(candidates_votes, key=candidates_votes.get, reverse=True): ordered.append({message: candidates_votes[message]}) await ctx.send(embed=self.bot.success(f"And the winnder is {list(ordered[0].keys())[0].author.name}"))
async def connect(channel: discord.TextChannel) -> Connection: if (type(channel) is not discord.TextChannel): raise TypeError("Argument \"channel\" type is not discord.TextChannel") tables: Dict[str, Dict[str, Dict[str, Union[dict, discord.TextChannel]]]] = {} async for message in channel.history(limit=None): loaded_data: dict = json.loads(message.content) table_name: str = loaded_data.get(TABLE_NAME_KEY) data_name: str = loaded_data.get(DATA_NAME_KEY) data: dict = loaded_data.get(DATA_KEY) if table_name is None: continue if data_name is None: continue if data is None: continue if table_name not in tables: tables[table_name] = {} tables[table_name][data_name] = { "data" : data, "message" : message } return Connection(channel, tables)
async def messagecount(ctx, channel: discord.TextChannel = None): channel = channel or ctx.channel count = 0 async for _ in channel.history(limit=None): count += 1 await ctx.send("There were {} messages in {}".format( count, channel.mention))
async def parse_history(self, ctx, *, channel: discord.TextChannel = None): for guild in self.bot.guilds: for channel in guild.text_channels: print("Processing {}.".format(channel.name)) async for message in channel.history(limit=None): self.store_attachments(message)
async def getChHistory(channel: discord.TextChannel) -> None: """ gets rundenvorestellungs history to hand them to the wrapper """ message: discord.Message async for message in channel.history(limit=200): await wrapMsgCheck(message)
async def duplicate(self, ctx, msgs: int, from_channel: discord.TextChannel, to_channel: discord.TextChannel = None): if not to_channel: to_channel = ctx.channel if not ctx.author.permissions_in( from_channel).read_messages or not ctx.author.permissions_in( from_channel).read_message_history: return elif not ctx.guild.me.permissions_in(ctx.channel).manage_webhooks: await ctx.send( content=error_string + ' **Duplicating messages require me to have `Manage Webhooks` permission in the target channel.**' ) elif not ctx.guild.me.permissions_in( from_channel).read_messages or not ctx.guild.me.permissions_in( from_channel).read_message_history: await ctx.send( content=error_string + ' **I do not have enough permissions to fetch messages from** ' + from_channel.mention) else: if msgs > 100: msgs = 100 messages = list() async for msg in from_channel.history(limit=msgs, before=ctx.message): messages.append(msg) webhook = await ctx.channel.create_webhook( name='Message Duplicator') for msg in reversed(messages): await asyncio.sleep(0.5) async with aiohttp.ClientSession() as session: webhook_channel = discord.Webhook.from_url( webhook.url, adapter=discord.AsyncWebhookAdapter(session)) try: await webhook_channel.send( username=msg.author.display_name, avatar_url=msg.author.avatar_url, content=msg.content, embeds=msg.embeds, wait=True) except: continue await webhook.delete()
async def link_list_item(self, ctx, channel: discord.TextChannel, number: int): if number <= 0: await ctx.send(f"Number must be greater than 0.") return if channel.id not in config.list_channels: await ctx.send(f"{channel.mention} is not a list channel.") return counter = 0 async for message in channel.history(limit = None, oldest_first = True): if message.content.strip(): counter += 1 if counter == number: embed = discord.Embed( title = f"Item #{number} in #{channel.name}", description = message.content, url = message.jump_url ) await ctx.send( content = "", embed = embed ) return await ctx.send(f"Unable to find item #{number} in {channel.mention}.")
async def mood(self,ctx,user: discord.Member=None, channel: discord.TextChannel=None): 'Finds out how a user is doing using sentiment analysis' if not user: user = ctx.author if not channel: channel = ctx.channel async with ctx.channel.typing(): counter = 0 # Getting user's last 15 messages serverprefix = prefix(self.bot,ctx.message) user_messages = [] async for message in channel.history(limit=200): if (not message.content.startswith(serverprefix)) and message.author == user: # making sure not to analyze "o!mood" message as well as only adding messages from user user_messages.append(message.content) counter += 1 if counter > 15: break if counter == 0: await ctx.send("You have send 0 messages (not beginning with '{}') within the last 200 messages in this channel.".format(serverprefix)) return sentiments = [] for message in user_messages: opinion = TextBlob(message).sentiment # Getting opinion of message sentiments.append((opinion.polarity + 1) / 2) # Adding message's sentiment to sentiment array (opinion.sentiment.polarity is -1.0 - 1.0, so it is normalized) overall_sentiment = sum(sentiments) / len(sentiments) # Averaging out sentiments await ctx.send("{} is {}% happy!".format(user.display_name, int(overall_sentiment * 100))) # overall_sentiment is converted to an percentage without a fractional
async def getall(self, ctx, channel: discord.TextChannel, message_id: int): """ Gets ALL messages with it's formatting from Aurelia after the specified message. For now, limit is 100 messages """ messages = [] try: message = await channel.fetch_message(message_id) except: await ctx.send("Sorry, that message could not be found.", delete_after=30) return async for m in channel.history(limit=100, after=message.created_at): if m.author == ctx.guild.me: messages.append(m) for message in messages: if message.content == "": await ctx.send("(no message content)") else: await ctx.send("{}".format( escape(message.content, formatting=True, mass_mentions=True))) await asyncio.sleep(0.2)
async def getUserMessagesInChannel(channel:discord.TextChannel,user:Union[discord.Member,discord.User]) -> List[discord.Message]: messages = list() async for message in channel.history(limit=None,oldest_first=True): if message.author == user: messages.append(message) return messages
async def store_latest_chat_messages(self, channel: TextChannel, is_backfill: bool = False) -> None: """ Attempts to load chat messages since the last timestamp in the database. @param channel: The discord text channel. @param is_backfill: Whether or not backfill from beginning of time. """ last_timestamp = self.db_manager.get_last_message_timestamp(channel.id) after = (datetime.utcfromtimestamp(last_timestamp) if last_timestamp else None) if is_backfill: after = None self.db_manager.reset_cache() messages_processed = 0 async for msg in channel.history(limit=None, after=after): self.db_manager.add_new_message( discord_user=DiscordUser(msg.author.name, msg.author.display_name, msg.author.discriminator), timestamp=int( msg.created_at.replace(tzinfo=timezone.utc).timestamp()), message_channel_id=channel.id, message_word_count=len(msg.content.split()), message_char_count=len(msg.content)) messages_processed += 1 if messages_processed % 100 == 0: print(messages_processed)
async def count(self, ctx: Context, channel: TextChannel, limit: int, *kwargs: str) -> None: """Count Messages that contain given keywords.""" count = 0 async for message in channel.history(limit=limit): # Checking for any matching kwargs in normal message content. content = message.content # Checking for any matching kwargs in 'embed' content. # Will be searching for title, description and author. if len(message.embeds) > 0: embed = message.embeds[0] embed_dict = embed.to_dict() content += str(embed.title) + str(embed.description) if 'author' in embed_dict: content += str(embed_dict['author']['name']) if any(word in content.lower() for word in kwargs): count += 1 await ctx.send(f"Count: {count}")
async def total_messages(ctx, channel: discord.TextChannel=None): channel = channel or ctx.channel count = 0 async for _ in channel.history(limit=None): count += 1 embed = discord.Embed(title="Total Messages", description=f"Total messages sent in {channel.mention}: {count}", color=discord.Colour.purple()) await ctx.channel.send(embed=embed)
async def log(self, ctx, number: int, channel: discord.TextChannel): 'Logs past messages of a channel into a text file' async for message in channel.history(limit=number): output = ((((('```' + str(message.author)) + ' at ') + str(message.created_at)) + ': \n') + str(message.content)) + '\n```' await ctx.send(output)
async def fetch_message( self, channel: TextChannel, predicate: Union[Callable[[Message], bool], int, User, ClientUser], step: int = 100, ) -> Message: """Scroll a messageable's history indefinitely until the predicate returns true for a given message. Parameters ---------- channel : :class:`TextChannel` predicate : Optional[Callable[[str, Any], bool]] step : :class:`int` Returns ------- message : :class:`discord.Message` """ before = None if isinstance(predicate, int): snowflake = predicate predicate = lambda message: message.id == snowflake elif isinstance(predicate, (User, ClientUser)): author = predicate predicate = lambda message: message.author == author while True: async for message_ in channel.history(limit=step, before=before): if predicate(message_): return message_ before = message_
async def chatchart(self, ctx, channel: discord.TextChannel = None, messages=5000): """ Generates a pie chart, representing the last 5000 messages in the specified channel. """ e = discord.Embed(description="Loading...", colour=0x00ccff) e.set_thumbnail(url="https://i.imgur.com/vSp4xRk.gif") em = await ctx.send(embed=e) if channel is None: channel = ctx.message.channel history = [] if not channel.permissions_for(ctx.message.author).read_messages == True: await em.delete() return await ctx.send("You're not allowed to access that channel.") try: async for msg in channel.history(limit=messages): history.append(msg) except discord.errors.Forbidden: await em.delete() return await ctx.send("No permissions to read that channel.") msg_data = {"total count": 0, "users": {}} for msg in history: if len(msg.author.name) >= 20: short_name = "{}...".format(msg.author.name[:20]).replace("$", "\$") else: short_name = msg.author.name.replace("$", "\$") whole_name = "{}#{}".format(short_name, msg.author.discriminator) if msg.author.bot: pass elif whole_name in msg_data["users"]: msg_data["users"][whole_name]["msgcount"] += 1 msg_data["total count"] += 1 else: msg_data["users"][whole_name] = {} msg_data["users"][whole_name]["msgcount"] = 1 msg_data["total count"] += 1 if msg_data['users'] == {}: await em.delete() return await ctx.message.channel.send(f'Only bots have sent messages in {channel.mention}') for usr in msg_data["users"]: pd = float(msg_data["users"][usr]["msgcount"]) / float(msg_data["total count"]) msg_data["users"][usr]["percent"] = round(pd * 100, 1) top_ten = heapq.nlargest( 20, [ (x, msg_data["users"][x][y]) for x in msg_data["users"] for y in msg_data["users"][x] if y == "percent" ], key=lambda x: x[1], ) others = 100 - sum(x[1] for x in top_ten) img = self.create_chart(top_ten, others, channel) await em.delete() await ctx.message.channel.send(file=discord.File(img, "chart.png"))
async def oof(self, ctx: commands.Context, msg_id: int = None, channel: discord.TextChannel = None) -> None: """ React 🅾🇴🇫 to a message. `msg_id` must be the message ID for desited message within the channel. `channel` must be the channel where the desired message is defaults to current channel if the bot has manage messages permission it will attempt to delete the command. """ if channel is None: channel = ctx.message.channel if msg_id is None: async for messages in channel.history(limit=2): message = messages else: try: message = await channel.fetch_message(msg_id) except discord.NotFound: return await ctx.send("Message ID {} not found in {}".format( msg_id, channel.mention), delete_after=5) if channel.permissions_for(ctx.me).add_reactions: with contextlib.suppress(discord.HTTPException): for emoji in ("🅾", "🇴", "🇫"): await message.add_reaction(emoji) if ctx.channel.permissions_for(ctx.me).manage_messages: await ctx.message.delete()
async def scan_recount(bot: commands.Bot, channel: discord.TextChannel, messages: int, start_date=None) -> None: async for m in channel.history(limit=messages, before=start_date): if await functions.needs_recount(bot, m): await functions.recount_reactions(bot, m)
def filter_history(channel: discord.TextChannel, **kwargs) -> typing.Coroutine[typing.Any, None, list[discord.Message]]: check = kwargs.pop('check', lambda m: True) limit = kwargs.pop('limit', sys.maxsize) return aioitertools.list(aioitertools.map( operator.itemgetter(1), aioitertools.zip(range(limit), channel.history(limit=None, **kwargs).filter(check)) ))
async def oof(self, ctx, msg_id: int = None, channel: discord.TextChannel = None): """ react 🅾🇴🇫 to a message `msg_id` must be the message ID for desited message within the channel `channel` must be the channel where the desired message is defaults to current channel if the bot has manage messages permission it will attempt to delete the command """ emojis = ["🅾", "🇴", "🇫"] if channel is None: channel = ctx.message.channel if msg_id is None: async for message in channel.history(limit=2): msg_id = message else: try: msg_id = await channel.get_message(msg_id) except: await ctx.send("Message ID {} not found in {}".format( msg_id, channel.mention), delete_after=5) return if ctx.channel.permissions_for(ctx.me).manage_messages: await ctx.message.delete() if channel.permissions_for(ctx.me).add_reactions: for emoji in emojis: try: await msg_id.add_reaction(emoji) except: pass
async def link_list_item(self, ctx, channel: discord.TextChannel, number: int): if number <= 0: await ctx.send(f"Number must be greater than 0.") return False if channel.id not in config.list_channels: await ctx.send(f"{channel.mention} is not a list channel.") return False counter = 0 async for message in channel.history(limit=None, oldest_first=True): if message.content.strip(): counter += 1 if counter == number: embed = discord.Embed( title=f"Item #{number} in #{channel.name}", description=message.content.replace("", "").strip(), ) embed.add_field(name="Jump URL", value=f"[Jump!]({message.jump_url})") await ctx.send(content="", embed=embed) return True await ctx.send(f"Unable to find item #{number} in {channel.mention}.") return False
async def get_messages(self, channel: TextChannel) -> list: """Get messages from active-works channel, Wil only get its own messages.""" messages = [] async for message in channel.history(limit=30): if message.author.id == self.bot.user.id: messages.append(message) return messages
async def react(self, ctx, msg_id: int = None, channel: discord.TextChannel = None): """ react to a message msg_id must be the message ID for desited message within the channel channel must be the channel where the desired message is defaults to current channel if the bot has manage messages permission it will attempt to delete the command """ emojis = [ "<:thinkking:602975162618609676>", "<:aldythink:646061177377390612>", "<:k3llyThinking:631777147064811532>", "🤔" ] if channel is None: channel = ctx.message.channel if msg_id is None: async for message in channel.history(limit=2): msg_id = message else: msg_id = await channel.fetch_message(msg_id) if ctx.channel.permissions_for(ctx.me).manage_messages: await ctx.message.delete() for emoji in emojis: await msg_id.add_reaction(emoji)
async def chatchart(self, ctx, channel: discord.TextChannel = None, messages=5000): """ Generates a pie chart, representing the last 5000 messages in the specified channel. """ e = discord.Embed(description="Loading...", colour=0x00CCFF) e.set_thumbnail(url="https://i.imgur.com/vSp4xRk.gif") em = await ctx.send(embed=e) if channel is None: channel = ctx.message.channel history = [] if not channel.permissions_for(ctx.message.author).read_messages == True: await em.delete() return await ctx.send("You're not allowed to access that channel.") try: async for msg in channel.history(limit=messages): history.append(msg) except discord.errors.Forbidden: await em.delete() return await ctx.send("No permissions to read that channel.") msg_data = {"total count": 0, "users": {}} for msg in history: if len(msg.author.name) >= 20: short_name = "{}...".format(msg.author.name[:20]) else: short_name = msg.author.name whole_name = "{}#{}".format(short_name, msg.author.discriminator) if msg.author.bot: pass elif whole_name in msg_data["users"]: msg_data["users"][whole_name]["msgcount"] += 1 msg_data["total count"] += 1 else: msg_data["users"][whole_name] = {} msg_data["users"][whole_name]["msgcount"] = 1 msg_data["total count"] += 1 for usr in msg_data["users"]: pd = float(msg_data["users"][usr]["msgcount"]) / float(msg_data["total count"]) msg_data["users"][usr]["percent"] = round(pd * 100, 1) top_ten = heapq.nlargest( 20, [ (x, msg_data["users"][x][y]) for x in msg_data["users"] for y in msg_data["users"][x] if y == "percent" ], key=lambda x: x[1], ) others = 100 - sum(x[1] for x in top_ten) img = self.create_chart(top_ten, others, channel) await em.delete() await ctx.message.channel.send(file=discord.File(img, "chart.png"))
async def get_messages_for_deletion( *, channel: discord.TextChannel, number: int = None, check: Callable[[discord.Message], bool] = lambda x: True, before: Union[discord.Message, datetime] = None, after: Union[discord.Message, datetime] = None, delete_pinned: bool = False, ) -> List[discord.Message]: """ Gets a list of messages meeting the requirements to be deleted. Generally, the requirements are: - We don't have the number of messages to be deleted already - The message passes a provided check (if no check is provided, this is automatically true) - The message is less than 14 days old - The message is not pinned Warning: Due to the way the API hands messages back in chunks, passing after and a number together is not advisable. If you need to accomplish this, you should filter messages on the entire applicable range, rather than use this utility. """ # This isn't actually two weeks ago to allow some wiggle room on API limits two_weeks_ago = datetime.utcnow() - timedelta(days=14, minutes=-5) def message_filter(message): return ( check(message) and message.created_at > two_weeks_ago and (delete_pinned or not message.pinned) ) if after: if isinstance(after, discord.Message): after = after.created_at after = max(after, two_weeks_ago) collected = [] async for message in channel.history( limit=None, before=before, after=after, reverse=False ): if message.created_at < two_weeks_ago: break if message_filter(message): collected.append(message) if number and number <= len(collected): break return collected