示例#1
0
 async def _url_candidates_from_context(self, ctx: Context):
     # Can't `yield from` in async functions
     async for msg in achain([ctx.message],
                             ctx.history(limit=5, before=ctx.message)):
         for url in _extract_urls_from_message(msg):
             if await self._is_image(url):
                 yield url
示例#2
0
    async def addemoji(self, ctx: commands.Context, emoji: str):
        try:
            await ctx.message.add_reaction(emoji)
        except BaseException:
            await ctx.send(f"<{EMOJIS['XMARK']}> Uh-oh, looks like that emoji doesn't work!")
            return

        channel = session.query(Channel).filter(Channel.channel_id == ctx.channel.id).first()

        if not channel or not channel.poll_channel:
            msg = f"<{EMOJIS['XMARK']}> This channel isn't setup as a poll channel! Please use `!togglepoll` to enable the feature!"
            await ctx.send(msg)
            return

        channel_emoji = session.query(ChannelEmoji).filter(
            and_(
                ChannelEmoji.channel_id == ctx.channel.id,
                ChannelEmoji.emoji == emoji)).first()

        if not channel_emoji:
            channel_emoji = ChannelEmoji(channel_id=ctx.channel.id, emoji=emoji)
            session.add(channel_emoji)
            session.commit()
            await ctx.send(f"<{EMOJIS['CHECK']}> Successfully added {emoji} to the list of emojis to add in this channel!", delete_after=3)

            async for msg in ctx.history(limit=None):
                await msg.add_reaction(emoji)
        else:
            await ctx.send(f"<{EMOJIS['XMARK']}> That emoji is already set for this channel!")
示例#3
0
    async def owo(self, ctx: commands.Context):
        """ Small. """
        async for msg in ctx.history(limit=1, before=ctx.message):
            await ctx.message.delete()
            owomsg = owo.owoify(msg.content)

            if len(msg.embeds) > 0:
                embeds = msg.embeds
                for embed in embeds:
                    if embed.title: embed.title = owo.owoify(embed.title)
                    if embed.description:
                        embed.description = owo.owoify(embed.description)
                    if embed.footer:
                        embed.set_footer(text=owo.owoify(embed.footer.text),
                                         icon_url=embed.footer.icon_url)
                    if embed.author:
                        embed.set_author(name=owo.owoify(embed.author.name),
                                         icon_url=embed.footer.icon_url)
                    for f in range(len(embed.fields)):
                        embed.set_field_at(
                            f,
                            name=owo.owoify(embed.fields[f].name),
                            value=owo.owoify(embed.fields[f].value))

                await ctx.send(owomsg,
                               embed=embeds[0],
                               allowed_mentions=AllowedMentions.none())

                if len(embeds) > 1:
                    for e in embeds[1:]:
                        await ctx.send(embed=e,
                                       allowed_mentions=AllowedMentions.none())

            elif owomsg != msg.content:
                await ctx.send(owomsg, allowed_mentions=AllowedMentions.none())
示例#4
0
 async def _basic_cleanup_strategy(self, ctx: Context, amount: int) -> dict:
     count = 0
     async for msg in ctx.history(limit=amount, before=ctx.message):
         if msg.author == ctx.me:
             await msg.delete()
             count += 1
     return {ctx.me: count}
示例#5
0
    async def delete(self, ctx: commands.Context, count: int = 1):
        """delete the last messages of the bot"""
        await deleteMessage(ctx)
        n = 0
        msgs = []

        if count < 1:
            return

        # Big count means that the int is probably a message id
        if count > 100:
            try:
                msg = await ctx.fetch_message(count)
                if msg.author.id in (self.bot.user.id, ctx.author.id):
                    await msg.delete()
            except discord.NotFound:
                pass
            return

        async for message in ctx.history(limit=100):
            if message.author == self.bot.user:
                msgs.append(message)
                n += 1

                if n >= count:
                    break

        try:
            if not isinstance(ctx.channel, discord.TextChannel):
                raise TypeError
            await ctx.channel.delete_messages(msgs)
        except (discord.errors.Forbidden, AttributeError, TypeError):
            for msg in msgs:
                await msg.delete()
示例#6
0
    async def edit(self, ctx: commands.Context, *args: str):
        """Edits a map according to the passed arguments"""
        subm = None
        if has_map(ctx.message):
            subm = Submission(ctx.message)
        elif ctx.message.reference is not None:
            replied_msg = await ctx.fetch_message(
                ctx.message.reference.message_id)
            if has_map(replied_msg):
                subm = Submission(replied_msg)
        if subm is None:
            map_channel = self.get_map_channel(ctx.channel.id)
            if map_channel is None:
                return
            async for msg in ctx.history():
                if not has_map(msg):
                    continue
                by_mapper = str(msg.author.id) in map_channel.mapper_mentions
                if by_mapper or is_staff(
                        msg.author) or msg.author.id == self.bot.user.id:
                    subm = Submission(msg)
                    break

        if subm is None:
            return
        stdout, file = await subm.edit_map(*args)
        if stdout:
            stdout = "```" + stdout + "```"
        await ctx.channel.send(stdout, file=file)
示例#7
0
    async def default(self, ctx: commands.Context, param: str) -> discord.Embed:
        # No idea when this would apply
        if ctx.message.embeds:
            return ctx.message.embeds[0]

        async for message in ctx.history():
            if message.embeds:
                return message.embeds[0]

        raise commands.MissingRequiredArgument(param)
示例#8
0
async def get_nearest(ctx: commands.Context,
                      limit: int = 20,
                      lookup: Callable[[discord.Message],
                                       Union[bytes, str]] = get_msg_image,
                      **lookup_kwargs) -> Union[bytes, str]:
    look = await lookup(ctx.message, **lookup_kwargs)
    if look is None:
        async for message in ctx.history(limit=limit):
            if message.id == ctx.message.id: continue
            look = await lookup(message, **lookup_kwargs)
            if look is not None: break
    return look
示例#9
0
async def translate(context: commands.Context, *args: str) -> None:
    translate_arguments: TranslateArguments = await _parse_translate_arguments(
        context, args)

    if translate_arguments.target_member == bot.user:
        raise BadArgument(f"Can't translate messages sent by the bot.")

    target_message: discord.Message

    invocation_message: discord.Message = context.message
    if invocation_message.reference is not None:
        # If this message is a reply, the text to translate is the text replied to
        target_message = invocation_message.reference.resolved  # TODO: this has a chance at failing, handle possible exception?
        if not isinstance(target_message, discord.Message):
            raise commands.CommandError("Could not extract message from reply")
        if translate_arguments.target_member is not None:
            raise BadArgument(
                "Target member shouldn't be specified on replies")
    else:
        # If the invocation isn't a reply, fetch the first valid message from the channel to translate
        """
		There doesn't seem to be a good way to get a member's history within a channel.
		It would seem that a member's history() method should do it, but this history corresponds to DMs, not to the channel.
		"""
        message_in_history: discord.Message
        async for message_in_history in context.history(
                limit=_MESSAGE_HISTORY_LIMIT):
            if (message_in_history.author !=
                    bot.user  # Ignore messages sent by this bot
                    and context.message !=
                    message_in_history  # Ignore the message that triggered this invocation
                    and
                    # Just to be safe, ignore all commands that start with the command prefix
                    # TODO: could this check be less broad?
                (not message_in_history.content.startswith(_COMMAND_PREFIX))
                    and
                    # Ignore if message author does not match specified target member (if any were given)
                (translate_arguments.target_member is None or
                 translate_arguments.target_member == message_in_history.author
                 )):
                target_message = message_in_history
                break
        else:
            raise commands.BadArgument("Found no message to translate.")

    translated_text: str = translator.translate(
        target_message.content,
        target_language=translate_arguments.target_language,
        source_language=translate_arguments.source_language)

    await target_message.reply(translated_text)
示例#10
0
    async def _reactions(self, ctx: Context, search: int = 100) -> None:
        """Removes all reactions from messages that have them."""
        if search > 2000:
            await ctx.send(f"Too many messages to search for ({search} / 2000)"
                           )
            return

        total_reactions = 0
        async for message in ctx.history(limit=search, before=ctx.message):
            if len(message.reactions):
                total_reactions += sum(r.count for r in message.reactions)
                await message.clear_reactions()

        await ctx.send(f"Successfully removed {total_reactions} reactions.")
示例#11
0
 async def _find_message(self,
                         ctx: commands.Context,
                         member: discord.Member = None) -> discord.Message:
     """Finds specified member's last non-command message.
     If no member was specified, adds emojis to the last non-command message sent in the given channel.
     """
     if member is not None and member != ctx.author:
         async for message in ctx.history(limit=15):
             if message.author == member:
                 return message
     else:
         messages = await ctx.history(limit=2).flatten()
         if len(messages) > 1:
             return messages[1]
     return None
示例#12
0
 async def find_image(self,
                      channel: commands.Context,
                      *,
                      sent_by: Optional[discord.Member] = None,
                      limit: int = 15) -> ExtractedImage:
     attachment, input_image_bytes = None, None
     async with channel.typing():
         async for message in channel.history(limit=limit):
             if sent_by is not None and message.author != sent_by:
                 continue
             attachment, input_image_bytes = await self.extract_image(
                 message)
             if input_image_bytes is not None:
                 break
     return attachment, input_image_bytes
示例#13
0
    async def chat_stats(self, context: commands.Context, days: Optional[int]):
        '''Display stats for this context. Optionally given # days back. Will look only look at most 10k messages
        Example: !chatstats 10 -> chat stats for the last 10 days
        '''

        async with context.typing():  # at least give some level of feedback
            counts: Dict[str, int] = {}

            days_back = datetime.datetime.now() - datetime.timedelta(
                days=days) if days else None

            async for message in context.history(
                    limit=10_000, after=days_back, oldest_first=False).filter(
                        lambda m: not m.author.bot):  #type: discord.Message
                counts[message.author.display_name] = counts.get(
                    message.author.display_name, 0) + 1

            with plt.xkcd():
                fig, axs = plt.subplots()  # type: plt.Figure, plt.Axes

                names = sorted(counts.keys(), key=lambda x: len(x))
                values = [counts[n] for n in names]

                axs.bar(names, values)
                title = f'Number of messages in #{context.channel.name}'
                if days_back:
                    axs.set_title(title + f'\nsince {days_back:%Y-%m-%d}')
                else:
                    axs.set_title(title)

                # formatting
                for label in axs.get_xticklabels(
                ):  # type: matplotlib.text.Text
                    label.set_rotation(45)
                    label.set_horizontalalignment('right')
                fig.tight_layout()

                buffer = io.BytesIO()
                fig.savefig(buffer, format='png')
                buffer.seek(0)

            await context.send(
                file=discord.File(buffer, filename='chatstats.png'))
示例#14
0
    async def snakify_command(self,
                              ctx: Context,
                              *,
                              message: str = None) -> None:
        """
        How would I talk if I were a snake?

        If `message` is passed, the bot will snakify the message.
        Otherwise, a random message from the user's history is snakified.

        Written by Momo and kel.
        Modified by lemon.
        """
        with ctx.typing():
            embed = Embed()
            user = ctx.author

            if not message:

                # Get a random message from the users history
                messages = []
                async for message in ctx.history(
                        limit=500).filter(lambda msg: msg.author == ctx.
                                          author  # Message was sent by author.
                                          ):
                    messages.append(message.content)

                message = self._get_random_long_message(messages)

            # Set the avatar
            if user.avatar is not None:
                avatar = f"https://cdn.discordapp.com/avatars/{user.id}/{user.avatar}"
            else:
                avatar = ctx.author.default_avatar_url

            # Build and send the embed
            embed.set_author(
                name=f"{user.name}#{user.discriminator}",
                icon_url=avatar,
            )
            embed.description = f"*{self._snakify(message)}*"

            await ctx.send(embed=embed)
示例#15
0
    async def default(self, ctx: commands.Context, param: str) -> str:
        if ctx.message.attachments:
            return ctx.message.attachments[0].url

        async for message in ctx.history():
            if message.attachments:
                return message.attachments[0].url

            if message.embeds:
                embed = message.embeds[0]

                if embed.type == "image":
                    if embed.url:
                        return embed.url

                elif embed.image:
                    return embed.image.url

        raise commands.MissingRequiredArgument(param)
示例#16
0
 async def dontbullshit(self, ctx: commands.Context):
     """Inverts the last 8-Ball answer in the channel."""
     async for message in ctx.history(limit=10):
         if message.author != ctx.me:
             continue
         message_content = cast(
             str,
             cast(discord.Message, message).clean_content)
         if not message_content.startswith('🎱 '):
             continue
         previous_answer = message_content[2:]
         if previous_answer in self.ANSWERS['affirmative']:
             new_category = 'negative'
         elif previous_answer in self.ANSWERS['negative']:
             new_category = 'affirmative'
         else:
             new_category = random.choice(['affirmative', 'negative'])
         new_text = f'🎱 {random.choice(self.ANSWERS[new_category])}'
         await message.edit(content=new_text)
         break
示例#17
0
文件: mod.py 项目: SleepyyNet/dogbot
    async def purge_reactions(self, ctx: commands.Context, amount: int = 5):
        """ Purges reactions in the last <n> messages. """
        count = 0
        total_reactions_removed = 0

        async for message in ctx.history(limit=amount):
            # no reactions, skip
            if not message.reactions:
                continue

            # calculate total reaction count
            total_reactions_removed += sum(reaction.count
                                           for reaction in message.reactions)

            # remove all reactions
            await message.clear_reactions()
            count += 1

        await ctx.send(
            f'Purge complete. Removed {total_reactions_removed} reaction(s) from {count} message(s).',
            delete_after=2.5)
示例#18
0
    async def remoji(self, ctx: commands.Context, emoji: str):
        member = ctx.guild.get_member(bot.user.id)
        try:
            await ctx.message.remove_reaction(emoji, member)
        except BaseException:
            await ctx.send(f"<{EMOJIS['XMARK']}> Uh-oh, looks like that emoji doesn't work!")
            return

        channel_emoji = session.query(ChannelEmoji).filter(
            and_(
                ChannelEmoji.channel_id == ctx.channel.id,
                ChannelEmoji.emoji == emoji)).first()

        if channel_emoji:
            channel_emoji.delete()
            session.commit()
            await ctx.send(f"<{EMOJIS['CHECK']}> Successfully removed {emoji} from the list of emojis to add in this channel!", delete_after=3)
        else:
            await ctx.send(f"<{EMOJIS['XMARK']}> Couldn't find {emoji} in the list of emojis to add in this channel!")

        async for msg in ctx.history(limit=None):
            await msg.remove_reaction(emoji, member)
示例#19
0
文件: imaging.py 项目: Twixes/somsiad
 async def find_image(
     self,
     channel: commands.Context,
     *,
     sent_by: Optional[discord.Member] = None,
     message_id: Optional[int] = None,
     limit: int = 15,
 ) -> ExtractedImage:
     attachment, input_image_bytes = None, None
     if message_id is not None:
         reference_message = await channel.fetch_message(message_id)
         attachment, input_image_bytes = await self.extract_image(
             reference_message)
     else:
         async for message in channel.history(limit=limit):
             if sent_by is not None and message.author != sent_by:
                 continue
             attachment, input_image_bytes = await self.extract_image(
                 message)
             if input_image_bytes is not None:
                 break
     return attachment, input_image_bytes
示例#20
0
    async def by_amounts(
        self,
        ctx: commands.Context,
        amounts: int = 1,
        member: Optional[discord.Member] = None,
    ) -> None:
        # 辨認觸發指令,以及是否為管理員
        if ctx.invoked_parents[
                0] == "clean" or ctx.author.id not in self.moderators:
            member = ctx.guild.me

        # 依據是否有指定成員發送確認訊息
        if member is None:
            confirm_msg = await ctx.reply(f"你確定要清除 {amounts} 則 **無指定** 的訊息嗎?")
        else:
            confirm_msg = await ctx.reply(
                f"你確定要清除 {amounts} 則 **{member.display_name}** 的訊息嗎?")

        await confirm_msg.add_reaction(Reactions.check_mark)
        await confirm_msg.add_reaction(Reactions.cross_mark)

        def command_confirm(reaction: discord.Reaction, user: discord.User):
            if user != self.bot.user:
                if user == ctx.author and reaction.message.id == confirm_msg.id:
                    if str(reaction.emoji) == Reactions.check_mark:
                        raise ActiveCommand
                    elif str(reaction.emoji) == Reactions.cross_mark:
                        raise CancelCommand

        try:
            await self.bot.wait_for("reaction_add",
                                    timeout=10,
                                    check=command_confirm)
        # 點選確認
        except ActiveCommand:
            # 清除確認訊息
            await confirm_msg.delete()
            start_time = dt.now()

            def is_specific(m: discord.Message) -> bool:
                return member is None or m.author == member

            history_length = 0
            msg_delete_queue = []
            async with ctx.typing():
                async for m in ctx.history(limit=None,
                                           before=ctx.message.created_at,
                                           oldest_first=False):
                    history_length += 1
                    if is_specific(m):
                        msg_delete_queue.append(m)
                    if len(msg_delete_queue) == amounts:
                        break

                def in_queue(m: discord.Message) -> bool:
                    return m in msg_delete_queue

                deleted_msg_count = len(await ctx.channel.purge(
                    limit=history_length,
                    before=ctx.message.created_at,
                    oldest_first=False,
                    check=in_queue,
                ))

            # 計算花費時間
            time_taken = (dt.now() - start_time).total_seconds()
            h, r = divmod(time_taken, 3600)
            m, s = divmod(r, 60)
            # 依據是否有指定成員發送結果
            if member is None:
                await ctx.reply(
                    f"已清除 {deleted_msg_count} 則 **無指定** 的訊息|"
                    f"花費時長:{h:02.0f}:{m:02.0f}:{s:02.0f}",
                    delete_after=15,
                )
            else:
                await ctx.reply(
                    f"已清除 {deleted_msg_count} 則 **{member.display_name}** 的訊息|"
                    f"花費時長:{h:02.0f}:{m:02.0f}:{s:02.0f}",
                    delete_after=15,
                )
        # 點選取消
        except CancelCommand:
            await confirm_msg.delete()
            await ctx.reply("指令已取消", delete_after=15)
        # 未點選
        except TimeoutError:
            await confirm_msg.delete()
            await ctx.reply("超過等待時間,指令已取消", delete_after=15)
        finally:
            await ctx.message.delete(delay=15)