示例#1
0
    async def append_reminder(self, timestamp: datetime, ctx: Context,
                              content: str) -> None:
        """Add reminder to database and schedule it."""
        sql = (
            "INSERT INTO reminders(jump_url, user_id, channel_id, end_time, content) "
            "VALUES ($1, $2, $3, $4, $5)RETURNING reminder_id")
        async with self.bot.db_pool.acquire() as connection:
            reminder_id = await connection.fetchval(
                sql,
                ctx.message.jump_url,
                ctx.author.id,
                ctx.channel.id,
                timestamp,
                content,
            )

        embed = Embed(
            title=":white_check_mark:  Reminder set",
            color=Colours.green,
            description=REMINDER_DESCRIPTION.format(
                arrive_in=humanize.precisedelta(timestamp - datetime.utcnow(),
                                                format="%0.0f"), ),
        )
        embed.set_footer(text=f"ID: {reminder_id}")
        await ctx.send(embed=embed)
        self.reminders[reminder_id] = {
            "reminder_id": reminder_id,
            "jump_url": ctx.message.jump_url,
            "user_id": ctx.author.id,
            "channel_id": ctx.channel.id,
            "end_time": timestamp,
            "content": content,
        }

        await self.schedule_reminder(self.get_recent_reminder())
示例#2
0
    async def get_commit_messages(self, event_body, brief=False):
        embed_commits = []
        branch = event_body['ref'].split('/', 2)[2]
        project = event_body['repository']['full_name']
        commits = event_body['commits']

        if brief and len(commits) > self.config['commit_truncation_limit']:
            first_hash = commits[0]['id']
            last_hash = commits[-2]['id']
            compare_url = f'https://github.com/{project}/compare/{first_hash}^...{last_hash}'
            embed = Embed(
                title=
                f'Skipped {len(commits) - 1} commits... (click link for diff)',
                colour=Colour(self._skipped_commit_colour),
                url=compare_url)
            embed_commits.append((embed, None))
            commits = commits[-1:]

        for commit in commits:
            author_username = commit['author'].get('username', None)
            author_name = commit['author'].get('name', None)
            timestamp = dateutil.parser.parse(commit['timestamp'])
            commit_message = commit['message'].split('\n')
            embed = Embed(title=commit_message[0],
                          colour=Colour(self._commit_colour),
                          url=commit['url'],
                          timestamp=timestamp)

            if len(commit_message) > 2 and not brief:
                commit_body = '\n'.join(commit_message[2:])
                embed.description = commit_body[:4096]

            author = await self.get_author_info(author_username)

            if author:
                if author['name'] and author['name'] != author['login']:
                    author_name = f'{author["name"]} ({author["login"]})'
                else:
                    author_name = author['login']

                embed.set_author(name=author_name,
                                 url=author['html_url'],
                                 icon_url=author['avatar_url'])
            elif author_name:
                embed.set_author(name=author_name)
            else:
                embed.set_author(name='<No Name>')

            embed.set_footer(text='Commit')
            embed.add_field(name='Repository', value=project, inline=True)
            embed.add_field(name='Branch', value=branch, inline=True)
            embed_commits.append((embed, commit['id']))

        return embed_commits
示例#3
0
def add_author_footer(embed: discord.Embed,
                      author: Union[discord.User, discord.Member],
                      set_timestamp=True,
                      additional_text: Union[Iterable[str], None] = None):
    if set_timestamp:
        embed.timestamp = datetime.now(tz=timezone.utc)

    if additional_text is not None:
        embed.set_footer(icon_url=author.display_avatar.url,
                         text=' | '.join((str(author), *additional_text)))
    else:
        embed.set_footer(icon_url=author.display_avatar.url, text=str(author))

    return embed
示例#4
0
def add_author_footer(embed: disnake.Embed,
                      author: disnake.User,
                      set_timestamp=True,
                      additional_text: Iterable[str] = []):
    """
    Adds footer to the embed with author name and icon from ctx.

    :param author: author info
    :param embed: disnake.Embed object
    :param set_timestamp: bool, should the embed's timestamp be set
    :param additional_text: Iterable of strings that will be joined with author name by pipe symbol, eg.:
    "john#2121 | text1 | text2".
    """

    if set_timestamp:
        embed.timestamp = datetime.now(tz=timezone.utc)

    embed.set_footer(icon_url=author.display_avatar.url,
                     text=' | '.join((str(author), *additional_text)))
示例#5
0
    async def fider(self):
        items = []
        async with self.bot.session.get(
                'https://ideas.obsproject.com/api/v1/posts?view=recent') as r:
            if r.status == 200:
                feed = await r.json()
                for entry in feed:
                    if entry['id'] <= self.bot.state['fider_last_id']:
                        continue
                    items.append(entry)
            else:
                logger.warning(
                    f'Fetching fider posts failed with status code {r.status}')
                return

        for item in reversed(items):
            if item['id'] > self.bot.state['fider_last_id']:
                self.bot.state['fider_last_id'] = item['id']

            url = f'https://ideas.obsproject.com/posts/{item["id"]}/'
            logger.info(f'Got new Fider post: {url}')

            description = item['description']
            if len(description) > 180:
                description = description[:180] + ' [...]'

            embed = Embed(title=f'{item["id"]}: {item["title"]}',
                          colour=Colour(self._fider_colour),
                          url=url,
                          description=description,
                          timestamp=dateutil.parser.parse(item['createdAt']))

            embed.set_author(
                name='Fider',
                url='https://ideas.obsproject.com/',
                icon_url='https://cdn.rodney.io/stuff/obsbot/fider.png')
            embed.set_footer(text='New Idea on Fider')
            name = 'Anonymous' if not item['user']['name'] else item['user'][
                'name']
            embed.add_field(name='Created By', value=name, inline=True)
            await self.fider_channel.send(embed=embed)
示例#6
0
    async def get_pr_messages(self, event_body):
        pr_number = event_body['number']
        title = event_body['pull_request']['title']
        timestamp = dateutil.parser.parse(
            event_body['pull_request']['created_at'])
        embed = Embed(title=f'#{pr_number}: {title}',
                      colour=Colour(self._pull_request_colour),
                      url=event_body['pull_request']['html_url'],
                      timestamp=timestamp)

        author_name = event_body['pull_request']['user']['login']
        author = await self.get_author_info(author_name)
        if author and author['name'] and author['name'] != author['login']:
            author_name = f'{author["name"]} ({author["login"]})'

        embed.set_author(
            name=author_name,
            url=event_body['pull_request']['user']['html_url'],
            icon_url=event_body['pull_request']['user']['avatar_url'])

        embed.set_footer(text='Pull Request')
        embed.add_field(name='Repository',
                        value=event_body['repository']['full_name'],
                        inline=True)
        # create copy without description text for brief channel
        brief_embed = embed.copy()
        # filter out comments in template
        event_body['pull_request']['body'] = '\n'.join(
            l.strip() for l in event_body['pull_request']['body'].splitlines()
            if not l.startswith('<!-'))

        # trim message to discord limits
        if len(event_body['pull_request']['body']) >= 2048:
            embed.description = event_body['pull_request'][
                'body'][:2000] + ' [... message trimmed]'
        else:
            embed.description = event_body['pull_request']['body']

        return brief_embed, embed
示例#7
0
    async def get_issue_messages(self, event_body):
        issue_number = event_body['issue']['number']
        title = event_body['issue']['title']
        timestamp = dateutil.parser.parse(event_body['issue']['created_at'])
        embed = Embed(title=f'#{issue_number}: {title}',
                      colour=Colour(self._issue_colour),
                      url=event_body['issue']['html_url'],
                      timestamp=timestamp)

        author_name = event_body['issue']['user']['login']
        author = await self.get_author_info(author_name)
        if author and author['name'] and author['name'] != author['login']:
            author_name = f'{author["name"]} ({author["login"]})'

        embed.set_author(name=author_name,
                         url=event_body['issue']['user']['html_url'],
                         icon_url=event_body['issue']['user']['avatar_url'])

        embed.set_footer(text='Issue')
        embed.add_field(name='Repository',
                        value=event_body['repository']['full_name'],
                        inline=True)
        # create copy without description text for brief channel
        brief_embed = embed.copy()
        event_body['issue']['body'] = '\n'.join(
            l.strip() for l in event_body['issue']['body'].splitlines()
            if not l.startswith('<!-'))

        issue_text = event_body['issue']['body']
        # strip double-newlines from issue forms
        if '\n\n' in issue_text:
            issue_text = issue_text.replace('\n\n', '\n')

        if len(issue_text) >= 2048:
            embed.description = issue_text[:2000] + ' [... message trimmed]'
        else:
            embed.description = issue_text

        return brief_embed, embed
示例#8
0
    async def get_discussion_messages(self, event_body):
        discussion_number = event_body['discussion']['number']
        title = event_body['discussion']['title']
        category = event_body['discussion']['category']['name']
        timestamp = dateutil.parser.parse(
            event_body['discussion']['created_at'])
        embed = Embed(title=f'#{discussion_number}: {category} - {title}',
                      colour=Colour(self._discussion_colour),
                      timestamp=timestamp,
                      url=event_body['discussion']['html_url'])

        author_name = event_body['discussion']['user']['login']
        author = await self.get_author_info(author_name)
        if author and author['name'] and author['name'] != author['login']:
            author_name = f'{author["name"]} ({author["login"]})'

        embed.set_author(
            name=author_name,
            url=event_body['discussion']['user']['html_url'],
            icon_url=event_body['discussion']['user']['avatar_url'])

        embed.set_footer(text='Discussion')
        embed.add_field(name='Repository',
                        value=event_body['repository']['full_name'],
                        inline=True)
        # create copy without description text for brief channel
        brief_embed = embed.copy()
        event_body['discussion']['body'] = '\n'.join(
            l.strip() for l in event_body['discussion']['body'].splitlines()
            if not l.startswith('<!-'))

        if len(event_body['discussion']['body']) >= 1024:
            embed.description = event_body['discussion'][
                'body'][:1024] + ' [... message trimmed]'
        else:
            embed.description = event_body['discussion']['body']

        return brief_embed, embed
示例#9
0
    async def get_wiki_message(self, event_body):
        embed = Embed(colour=Colour(self._wiki_colour))
        embed.set_footer(text='GitHub Wiki Changes')
        # All edits in the response are from a single author
        author_name = event_body['sender']['login']
        author = await self.get_author_info(author_name)
        if author and author['name'] and author['name'] != author['login']:
            author_name = f'{author["name"]} ({author["login"]})'
        embed.set_author(name=author_name,
                         url=event_body['sender']['html_url'],
                         icon_url=event_body['sender']['avatar_url'])
        embed.add_field(name='Repository',
                        value=event_body['repository']['full_name'])

        body = []
        for page in event_body['pages']:
            diff_url = f'{page["html_url"]}/_compare/{page["sha"]}^...{page["sha"]}'
            page_url = f'{page["html_url"]}/{page["sha"]}'
            body.append(
                f'**{page["action"]}:** [{page["title"]}]({page_url}) [[diff]({diff_url})]'
            )
        embed.description = '\n'.join(body)
        return embed
示例#10
0
    async def on_message(self, msg: Message):
        if not self.filtering_enabled:
            return
        # check if channel is in private (these are ignored)
        if self.bot.is_private(msg.channel):
            return
        if self.bot.is_supporter(msg.author):
            return

        # go through bannable rules first, then kickable, then just delete
        for name, regex in self.sorted_filters:
            m = regex.search(msg.content)
            if m:
                break
        else:  # no filter match
            return

        try:
            await msg.delete()
            deleted = 'Yes'
            self.bot.state['mod_faster'] += 1
        except Exception as e:
            deleted = f'No, failed with error: {e!r}'
        finally:
            self.bot.state['mod_deletes'] += 1
            if not self.bot.state['mod_first_delete']:
                self.bot.state['mod_first_delete'] = time.time()

        embed = Embed(
            colour=0xC90000,  # title='Message Filter Match',
            description=f'**Message by** {msg.author.mention} **in** '
            f'{msg.channel.mention} **matched filter:**\n'
            f'```\n{msg.content}\n```')
        embed.set_footer(text=f'Message ID: {msg.id}')
        embed.add_field(name='Filter name', value=f'`{name}`', inline=True)
        embed.add_field(name='Filter regex',
                        value=f'`{regex.pattern}`',
                        inline=True)
        embed.add_field(name='Regex match',
                        value=f'`{m.group()}`',
                        inline=True)
        embed.add_field(name='Message deleted?', value=deleted)

        if name in self.bannable:
            try:
                await msg.author.ban(delete_message_days=1,
                                     reason=f'Filter rule "{name}" matched.')
                embed.add_field(name='User banned?', value='Yes')
                self.bot.state['mod_bans'] += 1
                if not self.bot.state['mod_first_ban']:
                    self.bot.state['mod_first_ban'] = time.time()
            except Exception as e:
                logger.warning(f'Banning user {msg.author} failed: {e!r}')
                embed.add_field(name='User banned?',
                                value=f'No, failed with error: {e!r}')
            else:
                logger.info(
                    f'Banned user {msg.author.id}; Message {msg.id} matched filter "{name}"'
                )
        elif name in self.kickable:
            try:
                await msg.author.kick(reason=f'Filter rule "{name}" matched.')
                embed.add_field(name='User kicked?', value='Yes')
                self.bot.state['mod_kicks'] += 1
                if not self.bot.state['mod_first_kick']:
                    self.bot.state['mod_first_kick'] = time.time()
            except Exception as e:
                logger.warning(f'Banning user {msg.author} failed: {e!r}')
                embed.add_field(name='User kicked?',
                                value=f'No, failed with error: {e!r}')
            else:
                logger.info(
                    f'Kicked user {msg.author.id}; Message {msg.id} matched filter "{name}"'
                )
        else:
            logger.info(
                f'Deleted message by {msg.author.id}; Message {msg.id} matched filter "{name}"'
            )

        return await self.log_channel.send(embed=embed)
示例#11
0
    async def on_message(self, message: disnake.Message):
        if message.author.bot or message.webhook_id is not None:
            return
        if not hasattr(message.channel,
                       "guild") or message.channel.guild is None:
            return

        me = message.guild.me
        if me is None:
            me = Utils.get_member(self.bot, message.guild, self.bot.user.id)
        permissions = message.channel.permissions_for(me)
        if me is None:
            return
        if not (permissions.read_messages and permissions.send_messages
                and permissions.embed_links):
            return

        role_list = Configuration.get_var(message.guild.id, "CUSTOM_COMMANDS",
                                          "ROLES")
        role_required = Configuration.get_var(message.guild.id,
                                              "CUSTOM_COMMANDS",
                                              "ROLE_REQUIRED")
        channel_list = Configuration.get_var(message.guild.id,
                                             "CUSTOM_COMMANDS", "CHANNELS")
        channels_ignored = Configuration.get_var(message.guild.id,
                                                 "CUSTOM_COMMANDS",
                                                 "CHANNELS_IGNORED")
        mod_bypass = Configuration.get_var(message.guild.id, "CUSTOM_COMMANDS",
                                           "MOD_BYPASS")

        is_mod = message.author is not None and Permissioncheckers.is_mod(
            message.author)

        if (message.channel.id in channel_list) is channels_ignored and not (
                is_mod and mod_bypass):
            return

        has_role = False
        if message.author is not None and hasattr(message.author, "roles"):
            for role in message.author.roles:
                if role.id in role_list:
                    has_role = True
                    break

        if has_role is not role_required and not (is_mod and mod_bypass):
            return

        prefix = Configuration.get_var(message.guild.id, "GENERAL", "PREFIX")
        if message.content.startswith(prefix,
                                      0) and message.guild.id in self.commands:
            for trigger in self.commands[message.guild.id]:
                if message.content.lower() == prefix + trigger or (
                        message.content.lower().startswith(
                            trigger, len(prefix)) and
                        message.content.lower()[len(prefix + trigger)] == " "):
                    info = self.commands[message.guild.id][trigger]
                    images = IMAGE_MATCHER.findall(info.content)
                    image = None
                    if len(images) == 1:
                        image = images[0]
                        description = info.content.replace(image, "")
                    else:
                        description = info.content
                    embed = Embed(description=description)
                    if info.created_by is not None:
                        creator = await Utils.get_user(info.created_by)
                        embed.set_footer(
                            text=
                            f"Created by {str(creator)} ({info.created_by})",
                            icon_url=creator.avatar.url)
                    if image is not None:
                        embed.set_image(url=image)
                    await message.channel.send(embed=embed)
                    self.bot.custom_command_count += 1
示例#12
0
        image_url = urL_or_file(image)
        thumbnail_url = urL_or_file(thumbnail)
        files = []

        if isinstance(image_url, str):
            embed.set_image(url=image_url)
        else:
            embed.set_image(file=image_url)
            files.append(image_url)

        if isinstance(thumbnail_url, str):
            embed.set_thumbnail(url=thumbnail_url)
        else:
            embed.set_thumbnail(file=thumbnail_url)
            files.append(thumbnail_url)

        embed.set_footer(text=split(footer))

        wb.send(
            content=split(text),
            embed=embed,
            files=files,
            username=data.username,
            avatar_url=data.avatar_url,
        )

        print(f"\nText and embed sent successfully.")
        time.sleep(1)
        delete_lines(11)
示例#13
0
    async def paginate(
        cls,
        lines: t.List[str],
        ctx: Context,
        embed: disnake.Embed,
        prefix: str = "",
        suffix: str = "",
        max_lines: t.Optional[int] = None,
        max_size: int = 500,
        scale_to_size: int = 2000,
        empty: bool = False,
        restrict_to_user: User = None,
        timeout: int = 300,
        footer_text: str = None,
        url: str = None,
        allow_empty_lines: bool = False,
    ) -> t.Optional[disnake.Message]:
        """
        Use a paginator and set of reactions to provide pagination over a set of lines.

        When used, this will send a message using `ctx.send()` and apply the pagination reactions,
        to control the embed.

        Pagination will also be removed automatically if no reaction is added for `timeout` seconds.

        The interaction will be limited to `restrict_to_user` (ctx.author by default) or
        to any user with a moderation role.

        Example:
        >>> people = ["Guido van Rossum", "Linus Torvalds", "Gurkbot", "Bjarne Stroustrup"]
        >>> e = disnake.Embed()
        >>> e.set_author(name="Ain't these people just awesome?")
        >>> await LinePaginator.paginate(people, ctx, e)
        """
        def event_check(reaction_: disnake.Reaction,
                        user_: disnake.Member) -> bool:
            """Make sure that this reaction is what we want to operate on."""
            return (
                # Conditions for a successful pagination:
                all((
                    # Reaction is on this message
                    reaction_.message.id == message.id,
                    # Reaction is one of the pagination emotes
                    str(reaction_.emoji) in PAGINATION_EMOJI,
                    # Reaction was not made by the Bot
                    user_.id != ctx.bot.user.id,
                    # The reaction was by a whitelisted user
                    user_.id == restrict_to_user.id,
                )))

        paginator = cls(
            prefix=prefix,
            suffix=suffix,
            max_size=max_size,
            max_lines=max_lines,
            scale_to_size=scale_to_size,
        )
        current_page = 0

        # If the `restrict_to_user` is empty then set it to the original message author.
        restrict_to_user = restrict_to_user or ctx.author

        if not lines:
            if not allow_empty_lines:
                logger.exception(
                    "`Empty lines found, raising error as `allow_empty_lines` is `False`."
                )
                raise EmptyPaginatorEmbed("No lines to paginate.")

            logger.debug(
                "Empty lines found, `allow_empty_lines` is `True`, adding 'nothing to display' as content."
            )
            lines.append("(nothing to display)")

        for line in lines:
            try:
                paginator.add_line(line, empty=empty)
            except Exception:
                logger.exception(f"Failed to add line to paginator: '{line}'.")
                raise

        logger.debug(f"Paginator created with {len(paginator.pages)} pages.")

        # Set embed description to content of current page.
        embed.description = paginator.pages[current_page]

        if len(paginator.pages) <= 1:
            if footer_text:
                embed.set_footer(text=footer_text)

            if url:
                embed.url = url

            logger.debug("Less than two pages, skipping pagination.")
            await ctx.send(embed=embed)
            return
        else:
            if footer_text:
                embed.set_footer(
                    text=
                    f"{footer_text} (Page {current_page + 1}/{len(paginator.pages)})"
                )
            else:
                embed.set_footer(
                    text=f"Page {current_page + 1}/{len(paginator.pages)}")

            if url:
                embed.url = url

            message = await ctx.send(embed=embed)

        logger.debug("Adding emoji reactions to message...")

        for emoji in PAGINATION_EMOJI:
            # Add all the applicable emoji to the message
            await message.add_reaction(emoji)

        logger.debug("Successfully added all pagination emojis to message.")

        while True:
            try:
                reaction, user = await ctx.bot.wait_for("reaction_add",
                                                        timeout=timeout,
                                                        check=event_check)
                logger.trace(f"Got reaction: {reaction}.")
            except asyncio.TimeoutError:
                logger.debug("Timed out waiting for a reaction.")
                break  # We're done, no reactions for the last 5 minutes

            if str(reaction.emoji) == DELETE_EMOJI:
                logger.debug("Got delete reaction.")
                await message.delete()
                return

            if reaction.emoji == FIRST_EMOJI:
                await message.remove_reaction(reaction.emoji, user)
                current_page = 0

                logger.debug(
                    f"Got first page reaction - changing to page 1/{len(paginator.pages)}."
                )

                embed.description = paginator.pages[current_page]
                if footer_text:
                    # Current page is zero index based.
                    embed.set_footer(
                        text=
                        f"{footer_text} (Page {current_page + 1}/{len(paginator.pages)})"
                    )
                else:
                    embed.set_footer(
                        text=f"Page {current_page + 1}/{len(paginator.pages)}")
                await message.edit(embed=embed)

            if reaction.emoji == LAST_EMOJI:
                await message.remove_reaction(reaction.emoji, user)
                current_page = len(paginator.pages) - 1

                logger.debug(
                    f"Got last page reaction - changing to page {current_page + 1}/{len(paginator.pages)}"
                )

                embed.description = paginator.pages[current_page]
                if footer_text:
                    embed.set_footer(
                        text=
                        f"{footer_text} (Page {current_page + 1}/{len(paginator.pages)})"
                    )
                else:
                    embed.set_footer(
                        text=f"Page {current_page + 1}/{len(paginator.pages)}")
                await message.edit(embed=embed)

            if reaction.emoji == LEFT_EMOJI:
                await message.remove_reaction(reaction.emoji, user)

                if current_page <= 0:
                    logger.debug(
                        "Got previous page reaction while they are on the first page, ignoring."
                    )
                    continue

                current_page -= 1
                logger.debug(
                    f"Got previous page reaction - changing to page {current_page + 1}/{len(paginator.pages)}"
                )

                embed.description = paginator.pages[current_page]

                if footer_text:
                    embed.set_footer(
                        text=
                        f"{footer_text} (Page {current_page + 1}/{len(paginator.pages)})"
                    )
                else:
                    embed.set_footer(
                        text=f"Page {current_page + 1}/{len(paginator.pages)}")
                await message.edit(embed=embed)

            if reaction.emoji == RIGHT_EMOJI:
                await message.remove_reaction(reaction.emoji, user)

                if current_page >= len(paginator.pages) - 1:
                    logger.debug(
                        "Got next page reaction while they are on the last page, ignoring."
                    )
                    continue

                current_page += 1
                logger.debug(
                    f"Got next page reaction - changing to page {current_page + 1}/{len(paginator.pages)}"
                )

                embed.description = paginator.pages[current_page]

                if footer_text:
                    embed.set_footer(
                        text=
                        f"{footer_text} (Page {current_page + 1}/{len(paginator.pages)})"
                    )
                else:
                    embed.set_footer(
                        text=f"Page {current_page + 1}/{len(paginator.pages)}")
                await message.edit(embed=embed)

        logger.debug("Ending pagination and clearing reactions.")
        with suppress(disnake.NotFound):
            await message.clear_reactions()