Exemplo n.º 1
0
    async def rule(self, ctx, alias: Union[int, str]):
        """Shows rule based on number order or alias."""
        if isinstance(alias, int):
            rule_dict = self._get_rule_by_value(alias)
        else:
            rule_dict = self._get_rule_by_alias(alias)

        if rule_dict is None:
            await ctx.send(embed=failure("No such rule."), delete_after=5)
        else:
            await ctx.send(embed=info(rule_dict["statement"], ctx.guild.me, f"Rule: {rule_dict['name']}"))
Exemplo n.º 2
0
 async def cog_command_error(self, ctx, error):
     """A local error handler for all errors arising from commands in this cog."""
     if isinstance(error, commands.NoPrivateMessage):
         try:
             return await ctx.send(embed=failure(
                 "This command can not be used in private messages."))
         except discord.HTTPException:
             pass
     elif isinstance(error, InvalidVoiceChannel):
         await ctx.send(embed=failure(
             "Error connecting to Voice Channel. "
             "Please make sure you are in a valid channel or provide me with one"
         ))
     elif isinstance(error, TortoiseGuildCheckFailure):
         await ctx.send(embed=failure(f"{error}"))
     else:
         traceback_msg = traceback.format_exception(etype=type(error),
                                                    value=error,
                                                    tb=error.__traceback__)
         logger.error(traceback_msg)
         await self.bot.log_error(traceback_msg)
Exemplo n.º 3
0
    async def queue_info(self, ctx):
        """Retrieve a basic queue of upcoming songs."""
        vc = ctx.voice_client

        if not vc or not vc.is_connected():
            return await ctx.send(
                embed=failure("I am not currently connected to voice!"))

        player = self.get_player(ctx)
        if player.queue.empty():
            return await ctx.send(
                embed=failure("There are currently no more queued songs."))

        # Grab up to 5 entries from the queue...
        upcoming = list(itertools.islice(player.queue._queue, 0, 5))

        fmt = "\n".join(f"**`{_['title']}`**" for _ in upcoming)
        embed = discord.Embed(title=f"Upcoming - Next {len(upcoming)}",
                              description=fmt)

        await ctx.send(embed=embed)
Exemplo n.º 4
0
    async def stop_(self, ctx):
        """Stop the currently playing song and destroy the player.
        !Warning!
            This will destroy the player assigned to your guild, also deleting any queued songs and settings.
        """
        vc = ctx.voice_client

        if not vc or not vc.is_connected():
            return await ctx.send(
                embed=failure("I am not currently playing anything!"))

        await self.cleanup(ctx.guild)
Exemplo n.º 5
0
    async def reload(self, ctx, extension_name):
        """
        Reloads an extension.
        :param extension_name: cog name without suffix
        """
        if extension_name == Path(__file__).stem:
            await ctx.send(embed=failure(
                "This cog is protected, cannot execute operation."))
            return

        self.bot.reload_extension(f"bot.cogs.{extension_name}")
        await ctx.send(embed=success(f"{extension_name} reloaded.", ctx.me))
Exemplo n.º 6
0
    async def unmute(self, ctx, member: discord.Member):
        """Unmutes the member."""
        if self.muted_role not in member.roles:
            await ctx.send(embed=failure("Cannot unmute as member is not muted."))
            return

        reason = f"Unmuted by {ctx.author.id}"

        await member.remove_roles(self.muted_role, reason=reason)
        await member.add_roles(self.verified_role, reason=reason)

        await ctx.send(embed=success(f"{member} successfully unmuted."), delete_after=5)
Exemplo n.º 7
0
    async def pause_(self, ctx):
        """Pause the currently playing song."""
        vc = ctx.voice_client

        if not vc or not vc.is_playing():
            return await ctx.send(
                embed=failure("I am not currently playing anything!"))
        elif vc.is_paused():
            return

        vc.pause()
        await ctx.send(embed=info(f"**`{ctx.author}`** paused the song.",
                                  ctx.me,
                                  title="Song paused"))
Exemplo n.º 8
0
    async def resume_(self, ctx):
        """Resume the currently paused song."""
        vc = ctx.voice_client

        if not vc or not vc.is_connected():
            return await ctx.send(
                embed=failure("I am not currently playing anything!"))
        elif not vc.is_paused():
            return

        vc.resume()
        await ctx.send(embed=info(f"**`{ctx.author}`** resumed the song.",
                                  ctx.me,
                                  title="Song resumed"))
Exemplo n.º 9
0
    async def _mass_ban_timestamp_helper(self, ctx, timestamp_start: datetime, timestamp_end: datetime, reason: str):
        members_to_ban = []

        for member in self.tortoise_guild.members:
            if member.joined_at is None:
                continue

            if timestamp_start < member.joined_at < timestamp_end:
                members_to_ban.append(member)

        if not members_to_ban:
            return await ctx.send(embed=failure("Could not find any members, aborting.."))

        members_to_ban.sort(key=lambda m: m.joined_at)

        reaction_msg = await ctx.send(
            embed=warning(
                f"This will ban {len(members_to_ban)} members, "
                f"first one being {members_to_ban[0]} and last one being {members_to_ban[-1]}.\n"
                f"Are you sure you want to continue?"
            )
        )

        confirmation = await ConfirmationMessage.create_instance(self.bot, reaction_msg, ctx.author)
        if confirmation:

            one_tenth = len(members_to_ban) // 10
            notify_interval = one_tenth if one_tenth > 50 else 50

            await ctx.send(
                embed=info(
                    f"Starting the ban process, please be patient.\n"
                    f"You will be notified for each {notify_interval} banned members.",
                    ctx.author
                )
            )
            logger.info(f"{ctx.author} is timestamp banning: {', '.join(str(member.id) for member in members_to_ban)}")

            for count, member in enumerate(members_to_ban):
                if count != 0 and count % notify_interval == 0:
                    await ctx.send(embed=info(f"Banned {count} members..", ctx.author))

                await ctx.guild.ban(member, reason=reason)

            message = f"Successfully mass banned {len(members_to_ban)} members!"
            await ctx.send(embed=success(message))
            await self.deterrence_log_channel.send(embed=authored(message, author=ctx.author))
        else:
            await ctx.send(embed=info("Aborting mass ban.", ctx.me))
Exemplo n.º 10
0
    async def ban_timestamp(self,
                            ctx,
                            timestamp_start: DatetimeConverter,
                            timestamp_end: DatetimeConverter,
                            *,
                            reason="Mass ban with timestamp."):
        """Bans  member from the guild if he joined at specific time.

        Both arguments need to be in this specific format:
        %Y-%m-%d %H:%M

        Example:
        t.ban_timestamp "2020-09-15 13:00" "2020-10-15 13:00"

        All values need to be padded with 0.
        Timezones are not accounted for.
        """
        members_to_ban = []

        for member in self.tortoise_guild.members:
            if member.joined_at is None:
                continue

            if timestamp_start < member.joined_at < timestamp_end:
                members_to_ban.append(member)

        if not members_to_ban:
            return await ctx.send(
                embed=failure("Could not find any members, aborting.."))

        reaction_msg = await ctx.send(embed=warning(
            f"This will ban {len(members_to_ban)} members, "
            f"first one being {members_to_ban[0]} and last one being {members_to_ban[-1]}.\n"
            f"Are you sure you want to continue?"))

        confirmation = await ConfirmationMessage.create_instance(
            self.bot, reaction_msg, ctx.author)
        if confirmation:
            logger.info(
                f"{ctx.author} is timestamp banning: {', '.join(member.id for member in members_to_ban)}"
            )

            for member in members_to_ban:
                await self._ban_helper(ctx, member, reason)
            await ctx.send(embed=success(
                f"Successfully mass banned {len(members_to_ban)} members!"))
        else:
            await ctx.send(embed=info("Aborting mass ban.", ctx.me))
Exemplo n.º 11
0
    async def create_mod_mail(self, user: discord.User):
        if user.id in self.pending_mod_mails:
            await user.send(embed=failure(
                "You already have a pending mod mail, please be patient."))
            return

        submission_embed = authored(f"`{user.id}` submitted for mod mail.",
                                    author=user)
        # Ping roles so they get notified sooner
        await self.mod_mail_report_channel.send("@here", delete_after=30)
        await self.mod_mail_report_channel.send(embed=submission_embed)

        self.pending_mod_mails.add(user.id)
        await user.send(embed=success(
            "Mod mail was sent to admins, please wait for one of the admins to accept."
        ))
Exemplo n.º 12
0
    async def unload(self, ctx, extension_name):
        """
        Unloads an extension.
        :param extension_name: cog name without suffix
        """
        if extension_name == Path(__file__).stem:
            await ctx.send(
                embed=failure("This cog is protected, cannot unload."))
            return

        self.bot.unload_extension(f"bot.cogs.{extension_name}")

        msg = f"{extension_name} unloaded."
        logger.info(f"{msg} by {ctx.author.id}")

        await ctx.send(embed=success(f"{extension_name} unloaded.", ctx.me))
Exemplo n.º 13
0
    async def skip_(self, ctx):
        """Skip the song."""
        vc = ctx.voice_client

        if not vc or not vc.is_connected():
            return await ctx.send(
                embed=failure("I am not currently playing anything!"))

        if vc.is_paused():
            pass
        elif not vc.is_playing():
            return

        vc.stop()
        await ctx.send(embed=info(f"**`{ctx.author}`** skipped the song.",
                                  ctx.me,
                                  title="Skipping song"))
Exemplo n.º 14
0
    async def aoc_countdown(self, ctx):
        """Time until next challenge starts."""
        utc_minus_5 = datetime.timezone(offset=datetime.timedelta(hours=-5))
        now = datetime.datetime.now(tz=utc_minus_5)
        if now.month != 12:
            return await ctx.send(embed=failure("AoC is over!"))

        current_day = now.day
        end_date = datetime.datetime(year=2020,
                                     month=12,
                                     day=current_day + 1,
                                     tzinfo=utc_minus_5)

        difference = end_date - now
        ends_in = format_timedelta(difference)
        await ctx.send(embed=info(f"Day {current_day} ends in {ends_in}",
                                  title="Countdown",
                                  member=ctx.guild.me))
Exemplo n.º 15
0
    async def mute(self,
                   ctx,
                   member: discord.Member,
                   *,
                   reason="No reason stated."):
        """Mutes the member."""
        if self.muted_role in member.roles:
            await ctx.send(
                embed=failure("Cannot mute as member is already muted."))
            return

        reason = f"Muting member. {reason}"
        await member.add_roles(self.muted_role, reason=reason)
        await member.remove_roles(self.verified_role, reason=reason)
        await ctx.send(embed=success(f"{member} successfully muted."),
                       delete_after=5)
        await self.bot.api_client.add_member_warning(ctx.author.id, member.id,
                                                     reason)
Exemplo n.º 16
0
    async def player_loop(self):
        """Our main player loop."""
        await self.bot.wait_until_ready()

        while not self.bot.is_closed():
            self.next.clear()

            try:
                # Wait for the next song. If we timeout cancel the player and disconnect...
                async with timeout(300):
                    source = await self.queue.get()
            except asyncio.TimeoutError:
                return self.destroy(self._guild)

            if not isinstance(source, YTDLSource):
                # Source was probably a stream (not downloaded)
                # So we should regather to prevent stream expiration
                try:
                    source = await YTDLSource.regather_stream(
                        source, loop=self.bot.loop)
                except Exception as e:
                    await self._channel.send(embed=failure(
                        f"There was an error processing your song.\n"
                        f"```css\n[{e}]```"))
                    continue

            source.volume = self.volume
            self.current = source

            self._guild.voice_client.play(source,
                                          after=lambda _: self.bot.loop.
                                          call_soon_threadsafe(self.next.set))
            self.now_playing = f"`{source.title}` requested by `{source.requester}`"
            await self._channel.send(embed=info(
                self.now_playing, self._guild.me, title="Now playing"))
            await self.next.wait()

            # Make sure the FFmpeg process is cleaned up.
            source.cleanup()
            self.current = None
            self.now_playing = "Nothing."
Exemplo n.º 17
0
    async def hata(self, ctx, *, search_for: str):
        """Shows hata docs based on search query."""
        hata_docs_website = "https://huyanematsu.pythonanywhere.com/docs/"

        results = await self.hata_api.search(search_for)
        if not results:
            return await ctx.send(
                embed=failure(f"Could not find anything for {search_for}."))

        body = []
        for result in results[:8]:
            name_with_link = f"[{result['name']}]({hata_docs_website}{result['url']})"
            body.append(
                f"{name_with_link} *{result['type']}*\n{result.get('preview', '')}"
            )

        description = "\n\n".join(body)
        title = f"Showing {len(results)}" if len(
            results) < 8 else f"Showing 8/{len(results)}"
        docs_embed = discord.Embed(title=title, description=description)
        await ctx.send(embed=docs_embed)
Exemplo n.º 18
0
    async def fetch_doc_links(self, ctx, key, obj):
        page_types = {
            'latest': 'https://discordpy.readthedocs.io/en/latest',
            'python': 'https://docs.python.org/3',
        }

        if obj is None:
            await ctx.send(page_types[key])
            return

        if not self._doc_cache:
            await ctx.trigger_typing()
            await self.build_documentation_lookup_table(page_types)

        obj = re.sub(r'^(?:discord\.(?:ext\.)?)?(?:commands\.)?(.+)', r'\1',
                     obj)

        if key.startswith('latest'):
            # point the abc.Messageable types properly:
            q = obj.lower()
            for name in dir(discord.abc.Messageable):
                if name[0] == '_':
                    continue
                if q == name:
                    obj = f'abc.Messageable.{name}'
                    break

        cache = list(self._doc_cache[key].items())

        matches = Fuzzy.finder(obj, cache, key=lambda t: t[0], lazy=False)[:8]

        if len(matches) == 0:
            await ctx.send(embed=failure("Query didn't match any entity"))
            return

        embed_msg = "\n".join(f"[`{key}`]({url})" for key, url in matches)
        embed_msg = info(embed_msg, ctx.me, title="Links")

        await ctx.send(embed=embed_msg)
Exemplo n.º 19
0
    async def leaderboard(self, ctx):
        """
        Shows Tortoise leaderboard.

        Leaderboard is updated each 30 minutes.
        """
        if self._leaderboard_cache is None:
            return await ctx.send(embed=failure(
                "Please try again in few seconds as cache is not yet loaded."))

        sorted_members = {
            k: v
            for k, v in sorted(self._leaderboard_cache["members"].items(),
                               key=lambda item: item[1]["local_score"],
                               reverse=True)
        }

        leaderboard = ["```py"]
        num_of_members = 10
        position_counter = 0

        for member_id, member_data in sorted_members.items():
            position_counter += 1
            if position_counter > num_of_members:
                break

            stars_pretty = f"{'★' + str(member_data['stars']):4}"
            leaderboard.append(
                f"{position_counter}. {member_data['local_score']:4}p {stars_pretty} {member_data['name']}"
            )

        leaderboard.append("```")
        leaderboard_text = "\n".join(leaderboard)
        embed = info(
            f"{leaderboard_text}\n\nThe leaderboard is refreshed each 30 minutes.",
            member=ctx.guild.me,
            title="Tortoise AoC leaderboard")
        await ctx.send(embed=embed)
Exemplo n.º 20
0
    async def attend(self, ctx, user_id: int):
        if not any(role in ctx.author.roles
                   for role in (self.admin_role, self.moderator_role)):
            await ctx.send(embed=failure(
                "You do not have permission to use this command."))
            return

        # Time to wait for FIRST USER reply. Useful if mod attends but user is away.
        first_timeout = 21_600  # 6 hours
        # Flag for above variable. False means there has been no messages from the user.
        first_timeout_flag = False
        # After the user sends first reply this is the timeout we use.
        regular_timeout = 1800  # 30 min

        user = self.bot.get_user(user_id)
        mod = ctx.author

        if user is None:
            await ctx.send(embed=failure(
                "That user cannot be found or you entered incorrect ID."))
            return
        elif user_id not in self.pending_mod_mails:
            await ctx.send(
                embed=failure("That user is not registered for mod mail."))
            return
        elif self.is_any_session_active(mod.id):
            await ctx.send(embed=failure(
                "You already have one of active sessions (reports/mod mail etc)."
            ))
            return

        try:
            await mod.send(
                embed=success(f"You have accepted `{user}` mod mail request.\n"
                              "Reply here in DMs to chat with them.\n"
                              "This mod mail will be logged.\n"
                              "Type `close` to close this mod mail."))
        except discord.HTTPException:
            await ctx.send(embed=failure(
                "Mod mail failed to initialize due to mod having closed DMs."))
            return

        # Unlike failing for mods due to closed DMs this cannot fail for user since user already did interact
        # with bot in DMs as he needs to in order to even open mod-mail.
        await user.send(embed=authored((
            "has accepted your mod mail request.\n"
            "Reply here in DMs to chat with them.\n"
            "This mod mail will be logged, by continuing you agree to that."),
                                       author=mod))

        await ctx.send(embed=success("Mod mail initialized, check your DMs."))
        self.pending_mod_mails.remove(user_id)
        self.active_mod_mails[user_id] = mod.id
        _timeout = first_timeout
        # Keep a log of all messages in mod-mail
        log = MessageLogger(mod.id, user.id)

        def mod_mail_check(msg):
            return msg.guild is None and msg.author.id in (user_id, mod.id)

        while True:
            try:
                mail_msg = await self.bot.wait_for("message",
                                                   check=mod_mail_check,
                                                   timeout=_timeout)
                log.add_message(mail_msg)
            except TimeoutError:
                timeout_embed = failure("Mod mail closed due to inactivity.")
                log.add_embed(timeout_embed)
                await mod.send(embed=timeout_embed)
                await user.send(embed=timeout_embed)
                del self.active_mod_mails[user_id]
                await self.mod_mail_report_channel.send(file=discord.File(
                    StringIO(str(log)), filename=log.filename))
                break

            # Deal with attachments. We don't re-upload we just copy paste attachment url.
            attachments = self._get_attachments_as_urls(mail_msg)
            mail_msg.content += attachments

            if len(mail_msg.content) > 1900:
                mail_msg.content = f"{mail_msg.content[:1900]} ...truncated because it was too long."

            # Deal with dynamic timeout.
            if mail_msg.author == user and not first_timeout_flag:
                first_timeout_flag = True
                _timeout = regular_timeout

            # Deal with canceling mod mail
            if mail_msg.content.lower(
            ) == "close" and mail_msg.author.id == mod.id:
                close_embed = success(
                    f"Mod mail successfully closed by {mail_msg.author}.")
                log.add_embed(close_embed)
                await mod.send(embed=close_embed)
                await user.send(embed=close_embed)
                del self.active_mod_mails[user_id]
                await self.mod_mail_report_channel.send(file=discord.File(
                    StringIO(str(log)), filename=log.filename))
                break

            # Deal with user-mod communication
            if mail_msg.author == user:
                await mod.send(mail_msg.content)
            elif mail_msg.author == mod:
                await user.send(mail_msg.content)