示例#1
0
    async def _confirm(ctx: commands.Context) -> bool:
        """Ask "Are you sure?" and get the response as a bool."""
        if ctx.guild is None or ctx.guild.me.permissions_in(ctx.channel).add_reactions:
            msg = await ctx.send(_("Are you sure?"))
            # noinspection PyAsyncCall
            task = start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS, ctx.bot.loop)
            pred = ReactionPredicate.yes_or_no(msg, ctx.author)
            try:
                await ctx.bot.wait_for("reaction_add", check=pred, timeout=30)
            except asyncio.TimeoutError:
                await ctx.send(_("Response timed out."))
                return False
            else:
                task.cancel()
                agreed = pred.result
            finally:
                await msg.delete()
        else:
            await ctx.send(_("Are you sure? (y/n)"))
            pred = MessagePredicate.yes_or_no(ctx)
            try:
                await ctx.bot.wait_for("message", check=pred, timeout=30)
            except asyncio.TimeoutError:
                await ctx.send(_("Response timed out."))
                return False
            else:
                agreed = pred.result

        if agreed is False:
            await ctx.send(_("Action cancelled."))
        return agreed
示例#2
0
    async def cast(self, ctx:commands.Context, bait_type:str):
        """Rolls for a fish
          Fish will periodically bite the pole, at which point the message can be reacted to to catch the fish
          After reeling in the rod, you will have the option to keep or release the fish

         - Must be used in a channel registered as a pool"""

        if not await self.IsSpecialized(ctx.guild, ctx.channel.id, POOL_CHANNEL):
            return
        profile = self.config.member(ctx.message.author)

        await profile.currently_fishing.set(True)
        modified_fish_weights = await self.startfishing(ctx, profile, bait_type)

        embed = Embed(title=f'{ctx.message.author.display_name} cast their rod into the shimmering waves at {ctx.channel}', color=0x7300ff)
        embed.set_footer(text='Not even a nibble yet...')
        msg = await ctx.send(embed=embed)
        start_adding_reactions(msg, ['🎣'])

        pred = ReactionPredicate.with_emojis(['🎣'], msg, ctx.author)
        time_left = await self.GetSetting(ctx.guild, 'max_fishing_length')
        min_pause = await self.GetSetting(ctx.guild, 'min_fishing_wait')
        max_pause = await self.GetSetting(ctx.guild, 'max_fishing_wait')
        curr_fish = None
        rarity = None
        while time_left >= 0:
            try:
                timer = time_left if time_left < max_pause else randint(min_pause, max_pause)
                time_left -= timer
                await ctx.bot.wait_for('reaction_add', check=pred, timeout=timer)
            except asyncio.TimeoutError:
                if curr_fish is None:
                    rarity = choices(FISH_RARITIES, modified_fish_weights)[0]
                    rarity_list = self.fishing_rarities.get(rarity)
                    curr_fish = rarity_list[randint(0, len(rarity_list) - 1)] if not await profile.bryan_mode() else self.SEA_BASS
                    embed.set_footer(text=RARITY_DESCRIPTIONS[rarity])
                else:
                    curr_fish = None
                    embed.set_footer(text='The rod drifts in the water')
                await msg.edit(embed=embed)

            if pred.result == 0:
                break

        if curr_fish is None or time_left <= 0:
            embed.set_footer(text='You feel a twist as the line snaps :(')
            await msg.edit(embed=embed)
            await msg.clear_reactions()
        else:
            new_fish = curr_fish.ToFishCatch(RARITY_VALUES[rarity])
            embed.set_footer(text=f'You pulled a {new_fish["name"]} ({new_fish["size"]} inches) out of the water!\nDo you want to keep or release?')
            embed.set_thumbnail(url=curr_fish.image)
            await msg.edit(embed=embed)
            await msg.clear_reactions()

            start_adding_reactions(msg, ['🥤', '🐟'])

            pred = ReactionPredicate.with_emojis(['🥤', '🐟'], msg, ctx.author)
            try:
                await ctx.bot.wait_for("reaction_add", check=pred, timeout=15)
            except asyncio.TimeoutError:
                if await self.AddFish(ctx.message.author, new_fish):
                    embed.set_footer(text=f'Timed out, {new_fish["name"]} was added to your bucket')
                else:
                    embed.set_footer(text=f'Timed out and your bucket was full, so {new_fish["name"]} was released :(')
                await msg.edit(embed=embed)
                await msg.clear_reactions()
            else:
                if pred.result == 0:
                    if await self.AddFish(ctx.message.author, new_fish):
                        embed.set_footer(text=f'{new_fish["name"]} was added to your bucket!')
                    else:
                        embed.set_footer(text=f'Your bucket was full, so you had to release {new_fish["name"]} :(')
                else:
                    embed.set_footer(text=f'You let {new_fish["name"]} swim away...')
                await msg.edit(embed=embed)
                await msg.clear_reactions()

        if randint(0, 100) < 100 * await self.GetSetting(ctx.guild, 'bait_recovery_chance'):
            await ctx.send(f'Your {bait_type} is still on the end of the rod! (+1 {bait_type})')
        else:
            user_bait = await profile.bait()
            user_bait[bait_type] -= 1
            await profile.bait.set(user_bait)

        await profile.currently_fishing.set(False)
        #if not await profile.mawiam_mode():
        #await profile.nextcast.set(time() + await self.GetSetting(ctx.guild, 'fishing_delay'))

        await self.CheckSchools(ctx)
示例#3
0
    async def test_radar_minigame(self, ctx):
        """Mini-jeu où on doit faire passer un satellite d'un côté à l'autre
        
        Intégration prévue dans le prochain jeu 'Astral'"""
        user, guild = ctx.author, ctx.guild
        arrows = ['➡️', '⬅️', '⬆️', '⬇️', '↗️', '↘️', '↙️', '↖️']
        goods = random.sample(arrows, k=3)
        affs = [
            f"🛰️ {goods[0]} · {goods[1]} · {goods[2]} ✅",
            f"· {goods[0]} 🛰️ {goods[1]} · {goods[2]} ✅",
            f"· {goods[0]} · {goods[1]} 🛰️ {goods[2]} ✅",
            f"· {goods[0]} · {goods[1]} · {goods[2]} 🛰️"
        ]

        affnb = 0
        random.shuffle(arrows)
        msg = None
        while affnb < 3:
            timeout = 5 if affnb > 0 else 7
            em = discord.Embed(description=box(affs[affnb]), color=user.color)
            em.set_footer(
                text=
                f"› Cliquez sur les bonnes réactions dans l'ordre ({timeout}s)"
            )
            if not msg:
                msg = await ctx.send(embed=em)
                start_adding_reactions(msg, arrows)
            else:
                await msg.edit(embed=em)

            try:
                react, _ = await self.bot.wait_for(
                    "reaction_add",
                    check=lambda m, u: u == ctx.author and m.message.id == msg.
                    id,
                    timeout=5)
            except asyncio.TimeoutError:
                em.description = box(affs[affnb].replace('🛰️', '💥'))
                txt = ["Loupé", "Manqué", "Echec"]
                nrg = random.randint(4, 8)
                em.set_footer(
                    text=f"{random.choice(txt)} › Vous perdez {nrg}x ⚡")
                try:
                    await msg.clear_reactions()
                except:
                    pass
                return await msg.edit(embed=em)

            if react.emoji == goods[affnb]:
                affnb += 1
                continue
            else:
                em.description = box(affs[affnb].replace('🛰️', '💥'))
                txt = ["Loupé", "Manqué", "Echec"]
                nrg = random.randint(2, 5)
                em.set_footer(
                    text=f"{random.choice(txt)} › Vous perdez {nrg}x ⚡")
                try:
                    await msg.clear_reactions()
                except:
                    pass
                return await msg.edit(embed=em)

        em = discord.Embed(description=box(affs[affnb]), color=user.color)
        em.set_footer(text="Vous avez réussi !")
        try:
            await msg.clear_reactions()
        except:
            pass
        await msg.edit(embed=em)
示例#4
0
    async def make_event(
        self,
        ctx: commands.Context,
        members: commands.Greedy[discord.Member],
        max_slots: Optional[int] = None,
        *,
        description: str,
    ) -> None:
        """
        Create an event

        `[members...]` Add members already in the event you want to host.
        `[max_slots=None]` Specify maximum number of Slots the event can have, default is no limit.
        `<description>` provide a description for the event you're hosting.
        With custom keyword links setup this will add an image to the events thumbnail
        after being approved by an admin.
        """
        approval_channel = ctx.guild.get_channel(await self.config.guild(
            ctx.guild).approval_channel())
        announcement_channel = ctx.guild.get_channel(await self.config.guild(
            ctx.guild).announcement_channel())
        if not approval_channel:
            return await ctx.send(
                "No admin channel has been setup on this server. Use `[p]eventset approvalchannel` to add one."
            )
        if not announcement_channel:
            return await ctx.send(
                "No announcement channel has been setup on this server. Use `[p]eventset channel` to add one."
            )
        if str(ctx.author.id) in await self.config.guild(ctx.guild).events():
            if not await self.check_clear_event(ctx):
                return
        if ctx.author not in members:
            members.insert(0, ctx.author)
        member_list = []
        for member in members:
            member_list.append((member, await
                                self.config.member(member).player_class()))

        if not max_slots:

            max_slots = await self.config.guild(ctx.guild).default_max()
            # log.debug(f"using default {max_slots}")
        event = Event(hoster=ctx.author,
                      members=list(member_list),
                      event=description,
                      max_slots=max_slots)
        em = await self.make_event_embed(ctx, event)
        admin_msg = await approval_channel.send(embed=em)
        start_adding_reactions(admin_msg, ReactionPredicate.YES_OR_NO_EMOJIS)
        pred = ReactionPredicate.yes_or_no(admin_msg)
        reaction, user = await ctx.bot.wait_for("reaction_add", check=pred)
        if pred.result:
            ping = await self.config.guild(ctx.guild).ping()
            publish = (await self.config.guild(ctx.guild).publish()
                       and announcement_channel.is_news())
            event.approver = user
            event.channel = announcement_channel
            em.set_footer(text=f"Approved by {user}", icon_url=user.avatar_url)
            posted_message = await announcement_channel.send(ping,
                                                             embed=em,
                                                             **self.sanitize)
            if publish:
                try:
                    await posted_message.publish()
                except (discord.errors.Forbidden,
                        discord.errors.HTTPException):
                    log.debug("Event Channel is not a news channel.")
                    pass
            event.message = posted_message
            async with self.config.guild(ctx.guild).events() as cur_events:
                cur_events[str(event.hoster.id)] = event.to_json()
            if ctx.guild.id not in self.event_cache:
                self.event_cache[ctx.guild.id] = {}
            self.event_cache[ctx.guild.id][posted_message.id] = event
            try:
                start_adding_reactions(posted_message, EVENT_EMOJIS)
            except discord.errors.Forbidden:
                pass
        else:
            await ctx.send(
                f"{ctx.author.mention}, your event request was denied by an admin."
            )
            await admin_msg.delete()
            return
示例#5
0
    async def convert(self, ctx: commands.Context,
                      argument: str) -> Union[List[str], List[int]]:
        result = []
        match = re.split(r"(;)", argument)
        valid_reactions = [
            "dm",
            "dmme",
            "remove_role",
            "add_role",
            "ban",
            "kick",
            "text",
            "filter",
            "delete",
            "publish",
            "react",
            "rename",
            "command",
            "mock",
        ]
        log.debug(match)
        my_perms = ctx.channel.permissions_for(ctx.me)
        if match[0] not in valid_reactions:
            raise BadArgument(
                _("`{response}` is not a valid reaction type.").format(
                    response=match[0]))
        for m in match:
            if m == ";":
                continue
            else:
                result.append(m)
        if result[0] == "filter":
            result[0] = "delete"
        if len(result) < 2 and result[0] not in ["delete", "ban", "kick"]:
            raise BadArgument(
                _("The provided multi response pattern is not valid."))
        if result[0] in ["add_role", "remove_role"
                         ] and not my_perms.manage_roles:
            raise BadArgument(
                _('I require "Manage Roles" permission to use that.'))
        if result[0] == "filter" and not my_perms.manage_messages:
            raise BadArgument(
                _('I require "Manage Messages" permission to use that.'))
        if result[0] == "publish" and not my_perms.manage_messages:
            raise BadArgument(
                _('I require "Manage Messages" permission to use that.'))
        if result[0] == "ban" and not my_perms.ban_members:
            raise BadArgument(
                _('I require "Ban Members" permission to use that.'))
        if result[0] == "kick" and not my_perms.kick_members:
            raise BadArgument(
                _('I require "Kick Members" permission to use that.'))
        if result[0] == "react" and not my_perms.add_reactions:
            raise BadArgument(
                _('I require "Add Reactions" permission to use that.'))
        if result[0] == "mock":
            msg = await ctx.send(
                _("Mock commands can allow any user to run a command "
                  "as if you did, are you sure you want to add this?"))
            start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
            pred = ReactionPredicate.yes_or_no(msg, ctx.author)
            try:
                await ctx.bot.wait_for("reaction_add", check=pred, timeout=15)
            except asyncio.TimeoutError:
                raise BadArgument(_("Not creating trigger."))
            if not pred.result:
                raise BadArgument(_("Not creating trigger."))

        def author_perms(ctx: commands.Context, role: discord.Role) -> bool:
            if (
                    ctx.author.id == ctx.guild.owner_id
            ):  # handles case where guild is not chunked and calls for the ID thru the endpoint instead
                return True
            return role < ctx.author.top_role

        if result[0] in ["add_role", "remove_role"]:
            good_roles = []
            for r in result[1:]:
                try:
                    role = await RoleConverter().convert(ctx, r)
                    if role < ctx.guild.me.top_role and author_perms(
                            ctx, role):
                        good_roles.append(role.id)
                except BadArgument:
                    log.error("Role `{}` not found.".format(r))
            result = [result[0]]
            for r_id in good_roles:
                result.append(r_id)
        if result[0] == "react":
            good_emojis: List[Union[discord.Emoji, str]] = []
            for r in result[1:]:
                try:
                    emoji = await ValidEmoji().convert(ctx, r)
                    good_emojis.append(emoji)
                except BadArgument:
                    log.error("Emoji `{}` not found.".format(r))
            log.debug(good_emojis)
            result = [result[0]] + good_emojis
        return result
示例#6
0
    async def bulkreact(
        self,
        ctx: Context,
        message: discord.Message,
        *role_emoji: RoleEmojiConverter,
    ):
        """
        Create multiple roles reactions for a single message

        `<message>` can be the channel_id-message_id pair
        from copying message ID while holding SHIFT or a message link
        `[role_emoji...]` Must be a role-emoji pair separated by either `;`, `,`, `|`, or `-`.

        Note: Any spaces will be considered a new set of role-emoji pairs so ensure
        there's no spaces between the role-emoji pair.

        e.g. `[p]roletools bulkreact 461417772115558410-821105109097644052 @member-:smile:`
        `[p]roletools bulkreact 461417772115558410-821105109097644052 role-:frown:`
        """
        if not message.guild or message.guild.id != ctx.guild.id:
            return await ctx.send(
                _("You cannot add a Reaction Role to a message not in this guild.")
            )
        added = []
        not_added = []
        send_to_react = False
        async with self.config.guild(ctx.guild).reaction_roles() as cur_setting:
            for role, emoji in role_emoji:
                log.debug(type(emoji))
                if isinstance(emoji, discord.PartialEmoji):
                    use_emoji = str(emoji.id)
                else:
                    use_emoji = str(emoji).strip("\N{VARIATION SELECTOR-16}")
                key = f"{message.channel.id}-{message.id}-{use_emoji}"
                if key not in cur_setting:
                    try:
                        await message.add_reaction(
                            str(emoji).strip().strip("\N{VARIATION SELECTOR-16}")
                        )
                    except discord.HTTPException:
                        send_to_react = True
                        log.exception("could not add reaction to message")
                        pass
                    if ctx.guild.id not in self.settings:
                        self.settings[ctx.guild.id] = await self.config.guild(ctx.guild).all()
                    self.settings[ctx.guild.id]["reaction_roles"][key] = role.id
                    cur_setting[key] = role.id
                    added.append((key, role))
                    async with self.config.role(role).reactions() as reactions:
                        reactions.append(key)

                else:
                    not_added.append((key, role))
        ask_to_modify = False
        if added:
            msg = _("__The following Reaction Roles were created__\n")

            if any(
                [
                    m is False
                    for m in [await self.config.role(r).selfassignable() for x, r in added]
                ]
            ):
                ask_to_modify = True
            for item, role in added:
                channel, message_id, emoji = item.split("-")
                if emoji.isdigit():
                    emoji = self.bot.get_emoji(int(emoji))
                msg += _("{role} - {emoji} on {message}\n").format(
                    role=role.name, emoji=emoji, message=message.jump_url
                )
            for page in pagify(msg):
                await ctx.send(page)
            if send_to_react:
                await ctx.send(
                    _(
                        "I couldn't add an emoji to the message. Please make "
                        "sure to add the missing emojis to the message for this to work."
                    )
                )
        if not_added:
            msg = _("__The following Reaction Roles could not be created__\n")
            for item, role in not_added:
                channel, message_id, emoji = item.split("-")
                if emoji.isdigit():
                    emoji = self.bot.get_emoji(int(emoji))
                msg += _("{role} - {emoji} on {message}\n").format(
                    role=role.name, emoji=emoji, message=message.jump_url
                )
            await ctx.send(msg)

        if ask_to_modify:
            msg_str = _(
                "Some roles are not self assignable. Would you liked to make "
                "them self assignable and self removeable?"
            ).format(role=role.name, prefix=ctx.clean_prefix)
            msg = await ctx.send(msg_str)
            start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
            pred = ReactionPredicate.yes_or_no(msg, ctx.author)
            try:
                await ctx.bot.wait_for("reaction_add", check=pred, timeout=60)
            except asyncio.TimeoutError:
                return await ctx.send(
                    _("Okay I won't automatically make {role} self assignable.").format(
                        role=role.name
                    )
                )
            if pred.result:
                for key, role in added:
                    await self.config.role(role).selfassignable.set(True)
                    await self.config.role(role).selfremovable.set(True)
                await ctx.send(
                    _("{roles} have been made self assignable and self removeable.").format(
                        roles=humanize_list([r for x, r in added])
                    )
                )
示例#7
0
    async def command_now(self, ctx: commands.Context):
        """Now playing."""
        if not self._player_check(ctx):
            return await self.send_embed_msg(ctx, title=_("Nothing playing."))
        emoji = {
            "prev":
            "\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}\N{VARIATION SELECTOR-16}",
            "stop": "\N{BLACK SQUARE FOR STOP}\N{VARIATION SELECTOR-16}",
            "pause":
            "\N{BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR}\N{VARIATION SELECTOR-16}",
            "next":
            "\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}\N{VARIATION SELECTOR-16}",
            "close": "\N{CROSS MARK}",
        }
        expected = tuple(emoji.values())
        player = lavalink.get_player(ctx.guild.id)
        if player.current:
            arrow = await self.draw_time(ctx)
            pos = self.format_time(player.position)
            if player.current.is_stream:
                dur = "LIVE"
            else:
                dur = self.format_time(player.current.length)
            song = (await self.get_track_description(
                player.current, self.local_folder_current_path) or "")
            song += _("\n Requested by: **{track.requester}**").format(
                track=player.current)
            song += "\n\n{arrow}`{pos}`/`{dur}`".format(arrow=arrow,
                                                        pos=pos,
                                                        dur=dur)
        else:
            song = _("Nothing.")

        if player.fetch("np_message") is not None:
            with contextlib.suppress(discord.HTTPException):
                await player.fetch("np_message").delete()
        embed = discord.Embed(title=_("Now Playing"), description=song)
        guild_data = await self.config.guild(ctx.guild).all()

        if guild_data[
                "thumbnail"] and player.current and player.current.thumbnail:
            embed.set_thumbnail(url=player.current.thumbnail)
        shuffle = guild_data["shuffle"]
        repeat = guild_data["repeat"]
        autoplay = guild_data["auto_play"]
        text = ""
        text += (
            _("Auto-Play") + ": " +
            ("\N{WHITE HEAVY CHECK MARK}" if autoplay else "\N{CROSS MARK}"))
        text += (
            (" | " if text else "") + _("Shuffle") + ": " +
            ("\N{WHITE HEAVY CHECK MARK}" if shuffle else "\N{CROSS MARK}"))
        text += (
            (" | " if text else "") + _("Repeat") + ": " +
            ("\N{WHITE HEAVY CHECK MARK}" if repeat else "\N{CROSS MARK}"))

        message = await self.send_embed_msg(ctx, embed=embed, footer=text)

        player.store("np_message", message)

        dj_enabled = self._dj_status_cache.setdefault(
            ctx.guild.id, await self.config.guild(ctx.guild).dj_enabled())
        vote_enabled = await self.config.guild(ctx.guild).vote_enabled()
        if ((dj_enabled or vote_enabled)
                and not await self._can_instaskip(ctx, ctx.author)
                and not await self.is_requester_alone(ctx)):
            return

        if not player.queue and not autoplay:
            expected = (emoji["stop"], emoji["pause"], emoji["close"])
        task: Optional[asyncio.Task]
        if player.current:
            task = start_adding_reactions(message, expected[:5])
        else:
            task = None

        try:
            (r, u) = await self.bot.wait_for(
                "reaction_add",
                check=ReactionPredicate.with_emojis(expected, message,
                                                    ctx.author),
                timeout=30.0,
            )
        except asyncio.TimeoutError:
            return await self._clear_react(message, emoji)
        else:
            if task is not None:
                task.cancel()
        reacts = {v: k for k, v in emoji.items()}
        react = reacts[r.emoji]
        if react == "prev":
            await self._clear_react(message, emoji)
            await ctx.invoke(self.command_prev)
        elif react == "stop":
            await self._clear_react(message, emoji)
            await ctx.invoke(self.command_stop)
        elif react == "pause":
            await self._clear_react(message, emoji)
            await ctx.invoke(self.command_pause)
        elif react == "next":
            await self._clear_react(message, emoji)
            await ctx.invoke(self.command_skip)
        elif react == "close":
            await message.delete()
示例#8
0
文件: inat.py 项目: synrg/dronefly
 async def inat_test(self, ctx):
     """Test command."""
     msg = await ctx.send(
         embed=make_embed(title="Test", description="Reactions test.")
     )
     start_adding_reactions(msg, ["\N{THREE BUTTON MOUSE}"])
示例#9
0
    async def setsuggest_setup(self, ctx: commands.Context):
        """ Go through the initial setup process. """
        await self.config.guild(ctx.guild).same.set(False)
        await self.config.guild(ctx.guild).suggest_id.set(None)
        await self.config.guild(ctx.guild).approve_id.set(None)
        await self.config.guild(ctx.guild).reject_id.set(None)
        predchan = MessagePredicate.valid_text_channel(ctx)
        overwrites = {
            ctx.guild.default_role:
            discord.PermissionOverwrite(send_messages=False),
            ctx.guild.me:
            discord.PermissionOverwrite(send_messages=True),
        }
        msg = await ctx.send("Do you already have your channel(s) done?")
        start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
        pred = ReactionPredicate.yes_or_no(msg, ctx.author)
        try:
            await self.bot.wait_for("reaction_add", timeout=30, check=pred)
        except asyncio.TimeoutError:
            await msg.delete()
            return await ctx.send("You took too long. Try again, please.")
        if not pred.result:
            await msg.delete()
            suggestions = get(ctx.guild.text_channels, name="suggestions")
            if not suggestions:
                suggestions = await ctx.guild.create_text_channel(
                    "suggestions",
                    overwrites=overwrites,
                    reason="Suggestion cog setup")
            await self.config.guild(ctx.guild).suggest_id.set(suggestions.id)

            msg = await ctx.send(
                "Do you want to use the same channel for approved and rejected suggestions? (If yes, they won't be reposted anywhere, only their title will change accordingly.)"
            )
            start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
            pred = ReactionPredicate.yes_or_no(msg, ctx.author)
            try:
                await self.bot.wait_for("reaction_add", timeout=30, check=pred)
            except asyncio.TimeoutError:
                await msg.delete()
                return await ctx.send("You took too long. Try again, please.")
            if pred.result:
                await msg.delete()
                await self.config.guild(ctx.guild).same.set(True)
            else:
                await msg.delete()
                approved = get(ctx.guild.text_channels,
                               name="approved-suggestions")
                if not approved:
                    msg = await ctx.send(
                        "Do you want to have an approved suggestions channel?")
                    start_adding_reactions(msg,
                                           ReactionPredicate.YES_OR_NO_EMOJIS)
                    pred = ReactionPredicate.yes_or_no(msg, ctx.author)
                    try:
                        await self.bot.wait_for("reaction_add",
                                                timeout=30,
                                                check=pred)
                    except asyncio.TimeoutError:
                        await msg.delete()
                        return await ctx.send(
                            "You took too long. Try again, please.")
                    if pred.result:
                        approved = await ctx.guild.create_text_channel(
                            "approved-suggestions",
                            overwrites=overwrites,
                            reason="Suggestion cog setup",
                        )
                        await self.config.guild(ctx.guild
                                                ).approve_id.set(approved.id)
                    await msg.delete()
                else:
                    await self.config.guild(ctx.guild
                                            ).approve_id.set(approved.id)
                rejected = get(ctx.guild.text_channels,
                               name="rejected-suggestions")
                if not rejected:
                    msg = await ctx.send(
                        "Do you want to have a rejected suggestions channel?")
                    start_adding_reactions(msg,
                                           ReactionPredicate.YES_OR_NO_EMOJIS)
                    pred = ReactionPredicate.yes_or_no(msg, ctx.author)
                    try:
                        await self.bot.wait_for("reaction_add",
                                                timeout=30,
                                                check=pred)
                    except asyncio.TimeoutError:
                        await msg.delete()
                        return await ctx.send(
                            "You took too long. Try again, please.")
                    if pred.result:
                        rejected = await ctx.guild.create_text_channel(
                            "rejected-suggestions",
                            overwrites=overwrites,
                            reason="Suggestion cog setup",
                        )
                        await self.config.guild(ctx.guild
                                                ).reject_id.set(rejected.id)
                    await msg.delete()
                else:
                    await self.config.guild(ctx.guild
                                            ).reject_id.set(rejected.id)
        else:
            await msg.delete()
            msg = await ctx.send(
                "Mention the channel where you want me to post new suggestions."
            )
            try:
                await self.bot.wait_for("message", timeout=30, check=predchan)
            except asyncio.TimeoutError:
                await msg.delete()
                return await ctx.send("You took too long. Try again, please.")
            suggestion = predchan.result
            await self.config.guild(ctx.guild).suggest_id.set(suggestion.id)
            await msg.delete()

            msg = await ctx.send(
                "Do you want to use the same channel for approved and rejected suggestions? (If yes, they won't be reposted anywhere, only their title will change accordingly.)"
            )
            start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
            pred = ReactionPredicate.yes_or_no(msg, ctx.author)
            try:
                await self.bot.wait_for("reaction_add", timeout=30, check=pred)
            except asyncio.TimeoutError:
                await msg.delete()
                return await ctx.send("You took too long. Try again, please.")
            if pred.result:
                await msg.delete()
                await self.config.guild(ctx.guild).same.set(True)
            else:
                await msg.delete()
                msg = await ctx.send(
                    "Do you want to have an approved suggestions channel?")
                start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
                pred = ReactionPredicate.yes_or_no(msg, ctx.author)
                try:
                    await self.bot.wait_for("reaction_add",
                                            timeout=30,
                                            check=pred)
                except asyncio.TimeoutError:
                    await msg.delete()
                    return await ctx.send(
                        "You took too long. Try again, please.")
                if pred.result:
                    await msg.delete()
                    msg = await ctx.send(
                        "Mention the channel where you want me to post approved suggestions."
                    )
                    try:
                        await self.bot.wait_for("message",
                                                timeout=30,
                                                check=predchan)
                    except asyncio.TimeoutError:
                        await msg.delete()
                        return await ctx.send(
                            "You took too long. Try again, please.")
                    approved = predchan.result
                    await self.config.guild(ctx.guild
                                            ).approve_id.set(approved.id)
                await msg.delete()

                msg = await ctx.send(
                    "Do you want to have a rejected suggestions channel?")
                start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
                pred = ReactionPredicate.yes_or_no(msg, ctx.author)
                try:
                    await self.bot.wait_for("reaction_add",
                                            timeout=30,
                                            check=pred)
                except asyncio.TimeoutError:
                    await msg.delete()
                    return await ctx.send(
                        "You took too long. Try again, please.")
                if pred.result:
                    await msg.delete()
                    msg = await ctx.send(
                        "Mention the channel where you want me to post rejected suggestions."
                    )
                    try:
                        await self.bot.wait_for("message",
                                                timeout=30,
                                                check=predchan)
                    except asyncio.TimeoutError:
                        await msg.delete()
                        return await ctx.send(
                            "You took too long. Try again, please.")
                    rejected = predchan.result
                    await self.config.guild(ctx.guild
                                            ).reject_id.set(rejected.id)
                await msg.delete()
        await ctx.send(
            "You have finished the setup! Please, move your channels to the category you want them in."
        )
示例#10
0
 async def check_clear_event(self, ctx: commands.Context) -> bool:
     msg = await ctx.send("You already have an event running, would you like to cancel it?")
     start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
     pred = ReactionPredicate.yes_or_no(msg, ctx.author)
     await ctx.bot.wait_for("reaction_add", check=pred)
     return pred.result
示例#11
0
    async def yamlscan(self, ctx: commands.Context):
        """Scan yaml to see if its correct.

        Your next message will be used to as the yaml to scan.
        You can also upload a YAML file.
        """
        def cleanup_code(content) -> str:
            # From redbot.core.dev_commands, thanks will :P
            if content.startswith("```") and content.endswith("```"):
                return "\n".join(content.split("\n")[1:-1])
            return content.strip("` \n")

        def tick(text) -> str:
            return "{} {}".format(
                "\N{BALLOT BOX WITH CHECK}\N{VARIATION SELECTOR-16}", text)

        def cross(text) -> str:
            return "{} {}".format("\N{CROSS MARK}", text)

        if ctx.message.attachments:
            # attachments will take priority
            file = ctx.message.attachments[0]
            if not file.filename.split(".")[-1] in ("yaml", "yml", "mir"):
                return await ctx.send("Please upload a valid YAML file.")
            try:
                file = await file.read()
                content = file.decode(encoding="utf-8")
            except UnicodeDecodeError:
                return await ctx.send(
                    "Something went wrong whilst trying to decode the provided file."
                )

        else:

            message = await ctx.send(
                "Your next message will be your YAML content:")

            check = lambda x: x.channel == ctx.channel and x.author == ctx.author

            try:
                content = await self.bot.wait_for("message",
                                                  check=check,
                                                  timeout=100)
                content = content.content
            except asyncio.TimeoutError:
                with contextlib.suppress(discord.NotFound):
                    await message.edit("You took too long to respond.")
                    return
                await ctx.send("You took too long to respond.")
                return

        try:
            yaml.full_load(cleanup_code(content))
        except yaml.parser.MarkedYAMLError as e:
            message = cross(
                "This was **not** valid YAML. Would you like to see the exception details?"
            )
            can_react = ctx.channel.permissions_for(ctx.me).add_reactions

            if not can_react:
                message += " (yes/no)"

            message = await ctx.send(message)

            if can_react:
                start_adding_reactions(message,
                                       ReactionPredicate.YES_OR_NO_EMOJIS)
                predicate = ReactionPredicate.yes_or_no(message, ctx.author)
                event_type = "reaction_add"

            else:
                predicate = MessagePredicate.yes_or_no(ctx)
                event_type = "message"

            try:
                await self.bot.wait_for(event_type,
                                        check=predicate,
                                        timeout=30)
            except asyncio.TimeoutError:
                with contextlib.suppress(discord.NotFound):
                    await message.edit(
                        content=cross("This was **not** valid YAML."))

            if predicate.result:
                description = box("".join(
                    traceback.format_exception(type(e), e, e.__traceback__)),
                                  lang="py")
                await message.clear_reactions()
                await message.edit(content=description)
            else:
                with contextlib.suppress(discord.NotFound):
                    await message.edit(
                        content=cross("This was **not** valid YAML."))
            return

        await ctx.send(tick("This YAML looks good!"))
示例#12
0
    async def awbadge(self, ctx, tier: str = None, group: int = None):
        """Get alliance war badges."""
        if group is not None and group >= 1 and group < 4:
            group_num = group - 1  # This is to make sure that it will work with the urls
        tiers = {
            "master": [
                "https://media.discordapp.net/attachments/401476363707744257/738083791654092940/47EFB6D4D1380ABD2C40D2C7B0533A29245F7955.png",
                "https://media.discordapp.net/attachments/401476363707744257/738083791113027654/650E29ADB8C5C382FF5A358113B2C02B8EADA415.png",
                "https://media.discordapp.net/attachments/401476363707744257/738083791440052294/08BA0A081A9D56E35E60E3FD61FAB7ED9A10CD00.png"
            ],
            "platinum": [
                "https://media.discordapp.net/attachments/401476363707744257/738083790718631937/E78E2BAF9B0C9BA6C7FE45BE726FFB0B0B0CACFD.png",
                "https://media.discordapp.net/attachments/401476363707744257/738083790362116116/487EA26A1BA0F2C2848E7C87F10430BD218C2178.png",
                "https://media.discordapp.net/attachments/401476363707744257/738083790559117352/0ED8BD10441C6D086AEB7BBA5271269F46E009D1.png"
            ],
            "gold": [
                "https://media.discordapp.net/attachments/401476363707744257/738083790131298375/76BC21BF523A415866D19814BD8AF4BE16EF30A9.png",
                "https://media.discordapp.net/attachments/401476363707744257/738083998462509096/8CD52FEB7540016B6ABA1EC67B9F1777E3C29878.png",
                "https://media.discordapp.net/attachments/401476363707744257/738084001926873098/3A9A8FDA006D0BE225242AAA5909021CD52BCFB3.png"
            ],
            "silver": [
                "https://media.discordapp.net/attachments/401476363707744257/738084001465499789/4B389D377A94EDA747B38DF640C0B33A3A3F61AE.png",
                "https://media.discordapp.net/attachments/401476363707744257/738084001465499789/4B389D377A94EDA747B38DF640C0B33A3A3F61AE.png",
                "https://media.discordapp.net/attachments/401476363707744257/738083994612006914/5302FA8FA04735224847C8BBF82D1D54C8567B9C.png"
            ],
            "bronze": [
                "https://media.discordapp.net/attachments/401476363707744257/738083995211792404/719AC2C2AB5833D815C899DAF9ADB7CF11819CBA.png",
                "https://media.discordapp.net/attachments/401476363707744257/738083993043337276/E636A90C3F0DFFDAED0176D972AA0C73F3E40FF8.png",
                "https://media.discordapp.net/attachments/401476363707744257/738083997866786876/5B06D509847E0FA1405A50021486C1A5D8C6F9B2.png"
            ],
            "stone": [
                "https://media.discordapp.net/attachments/401476363707744257/738083996054978730/9AC92A2FDC2996C346125296356C664373147F2F.png",
                "https://media.discordapp.net/attachments/401476363707744257/738083993681002586/BF3D13EACC9F44216E754884AA183185761C84CF.png",
                "https://media.discordapp.net/attachments/401476363707744257/738084098857238670/EA938C0B0C2AE3E6DB91514F5F8768C4F033D373.png"
            ]
        }
        tier = tier.lower() if tier is not None else None
        if tier is None or tier not in tiers:
            embed = Embed.create(
                self, ctx, title="Alliance War Badge Tiers",
                description="Please choose one of the tiers below :arrow_down:\nSyntax: `,awbadge <tier>`"
            )
            normal = "\n".join([t.capitalize() for t in tiers.keys()])
            embed.add_field(
                # Unfortunatly I have to do this to make sure that participation gets in the list :/
                name="Badges", value="{}\nParticipation".format(normal)
            )

            normal = "\n".join(tiers)
            return await ctx.send(embed=embed)
        if tier == "participation":
            embed = Embed.create(
                self, ctx, title="Participation",
                image="https://media.discordapp.net/attachments/401476363707744257/738083790886535228/DA7D39277836A9CF1B39A68D37EAF99999B366C7.png"
            )
            return await ctx.send(embed=embed)
        if group is None:
            embeds = []
            for i in range(3):
                embed = Embed.create(
                    self, ctx,
                    title="{} Badges".format(tier.capitalize()), image=tiers[tier][i]
                )
                embeds.append(embed)
            msg = await ctx.send(embed=embeds[0])
            control = menus.DEFAULT_CONTROLS if len(embeds) > 1 else {
                "\N{CROSS MARK}": menus.close_menu
            }
            asyncio.create_task(menus.menu(ctx, embeds, control, message=msg))
            menus.start_adding_reactions(msg, control.keys())
        else:
            embed = Embed.create(
                self, ctx,
                title="{} Badge".format(tier.capitalize()), description="{} {}".format(tier.capitalize(), group),
                image=tiers[tier][group_num]
            )
            await ctx.send(embed=embed)
示例#13
0
    async def watch2gether(self, ctx, link=None):
        '''
        Create a watch2gether room. If a link is provided then the room will be opened for that resource
        '''
        api_keys = await self.bot.get_shared_api_tokens("watch2gether")
        if api_keys.get("api_key") is None:
            return await ctx.send(
                "The Watch2Gether API key has not been set. Set it with "
                f"`{ctx.prefix}set api watch2gether api_key,<api_key>` command"
            )
        api_key = api_keys["api_key"]

        if link is None:
            running_rooms = await self.db.guild(ctx.guild).rooms()
            if running_rooms:
                now = datetime.utcnow()
                room_strs = []
                i = 1
                for room in running_rooms:
                    created_at = discord.utils.snowflake_time(
                        room["message_id"])
                    expires = await self.db.guild(ctx.guild).expires()
                    if expires:
                        if (now - created_at).total_seconds() > expires:
                            async with self.db.guild(
                                    ctx.guild).rooms() as rooms:
                                rooms.remove(room)
                                running_rooms.remove(room)
                                continue
                    time_delta = human_timedelta(created_at, accuracy=1)
                    string = f"[Room {i}]({room['room_url']}) (Created By - <@{room['author_id']}>, {time_delta})"
                    i = i + 1
                    room_strs.append(string)
                if room_strs:
                    embed_color = await ctx.embed_color()
                    embed = discord.Embed(color=embed_color,
                                          title="Currently running rooms:")
                    embed.description = "\n".join(room_strs)
                    embed.set_footer(
                        text=
                        "Click on any of the URLs above to enter the room.\n"
                        'If you want to create a new room, react with "+" on this message'
                    )
                    message = await ctx.send(embed=embed)

                    emojis = [
                        "\N{HEAVY PLUS SIGN}",
                        "\N{HEAVY MULTIPLICATION X}\N{VARIATION SELECTOR-16}"
                    ]
                    start_adding_reactions(message, emojis)
                    pred = ReactionPredicate.with_emojis(emojis,
                                                         message=message,
                                                         user=ctx.author)
                    try:
                        await self.bot.wait_for("reaction_add",
                                                check=pred,
                                                timeout=60.0)
                    except asyncio.exceptions.TimeoutError:
                        return await message.clear_reactions()
                    else:
                        if pred.result == 1:
                            return await message.delete()

        url = "https://www.watch2gether.com/rooms/create.json"
        data = {"api_key": api_key, "share": link}
        async with self.session.post(url, data=data) as resp:
            jsondata = await resp.json()

        room_key = jsondata["streamkey"]
        room_url = f"https://www.watch2gether.com/rooms/{room_key}"
        async with self.db.guild(ctx.guild).rooms() as rooms:
            rooms.append({
                "room_key": room_key,
                "room_url": room_url,
                "message_id": ctx.message.id,
                "author_id": ctx.author.id
            })

        await ctx.send(f"New Watch2Gether room created: {room_url}")
示例#14
0
    async def _cog_update(self, ctx, cog_name: InstalledCog = None):
        """Update all cogs, or one of your choosing."""
        installed_cogs = set(await self.installed_cogs())

        async with ctx.typing():
            if cog_name is None:
                updated = await self._repo_manager.update_all_repos()

            else:
                try:
                    updated = await self._repo_manager.update_repo(
                        cog_name.repo_name)
                except KeyError:
                    # Thrown if the repo no longer exists
                    updated = {}

            updated_cogs = set(cog for repo in updated
                               for cog in repo.available_cogs)
            installed_and_updated = updated_cogs & installed_cogs

            if installed_and_updated:
                await self._reinstall_requirements(installed_and_updated)
                await self._reinstall_cogs(installed_and_updated)
                await self._reinstall_libraries(installed_and_updated)
                message = _("Cog update completed successfully.")

                cognames = {c.name for c in installed_and_updated}
                message += _("\nUpdated: ") + humanize_list(
                    tuple(map(inline, cognames)))
            else:
                await ctx.send(_("All installed cogs are already up to date."))
                return
        await ctx.send(message)

        cognames &= set(ctx.bot.extensions.keys())  # only reload loaded cogs
        if not cognames:
            return await ctx.send(
                _("None of the updated cogs were previously loaded. Update complete."
                  ))
        message = _("Would you like to reload the updated cogs?")
        can_react = ctx.channel.permissions_for(ctx.me).add_reactions
        if not can_react:
            message += " (y/n)"
        query: discord.Message = await ctx.send(message)
        if can_react:
            # noinspection PyAsyncCall
            start_adding_reactions(query, ReactionPredicate.YES_OR_NO_EMOJIS,
                                   ctx.bot.loop)
            pred = ReactionPredicate.yes_or_no(query, ctx.author)
            event = "reaction_add"
        else:
            pred = MessagePredicate.yes_or_no(ctx)
            event = "message"
        try:
            await ctx.bot.wait_for(event, check=pred, timeout=30)
        except asyncio.TimeoutError:
            await query.delete()
            return

        if pred.result is True:
            if can_react:
                with contextlib.suppress(discord.Forbidden):
                    await query.clear_reactions()
            await ctx.invoke(ctx.bot.get_cog("Core").reload, *cognames)
        else:
            if can_react:
                await query.delete()
            else:
                await ctx.send(_("OK then."))
示例#15
0
 async def open_chest(ctx, user, type):
     if hasattr(user, "display_name"):
         await ctx.send("{} is opening a treasure chest. What riches lay inside?".format(user.display_name))
     else: #this is when a pet is foraging.
         await ctx.send("{} is foraging for treasure. What will it find?".format(user[:1].upper() + user[1:]))
         await asyncio.sleep(2)
     if hasattr(user, "display_name"):
         luckbonus = Userdata.users[str(user.id)]['buffs'].get('luck', {'bonus':0})['bonus']
         roll = random.randint(1,100)-luckbonus
     else:
         luckbonus = 0
         roll = random.randint(1,100)
     if type == "pet":
         if roll <= 1:
             await ctx.send("{} found something precious!".format(user[:1].upper() + user[1:]))
             chance = Treasure.quest
         elif roll <= 11:
             chance = Treasure.unique
         elif roll > 11 and roll <= 50:
             chance = Treasure.rare
         elif roll > 50 and roll <= 75:
             chance = Treasure.common
         else:
             await ctx.send("{} found nothing of value.".format(user[:1].upper() + user[1:]))
             return None
     elif type == "normal":
         if roll <= 5:
             chance = Treasure.unique
         elif roll > 5 and roll <= 25:
             chance = Treasure.rare
         else:
             chance = Treasure.common
     elif type == "rare":
         if roll <= 15:
             chance = Treasure.unique
         elif roll > 15 and roll <= 45:
             chance = Treasure.rare
         else:
             chance = Treasure.common
     elif type == "epic":
         if roll <= 1:
             await ctx.send("This was no ordinary epic chest!")
             chance = Treasure.quest
         elif roll <= 25:
             chance = Treasure.unique
         else:
             chance = Treasure.rare
     elif type == "quest":
         if roll <= 10:
             chance = Treasure.quest
         else:
             chance = Treasure.unique
     itemname = random.choice(list(chance.keys()))
     item = chance[itemname]
     if item['slot'] == ['consumable']:
         item['uses'] = random.randint(1,item['uses'])
         if hasattr(user, "display_name"):
             await ctx.send("```css\n{} found {} ({}x).```".format(user.display_name,itemname,item['uses']))
         else:
             await ctx.send("```css\nYour {} found {} ({}x).```".format(user,itemname,item['uses']))
         msg = await ctx.send("Do you want to use, put in backpack or sell this item?")
         start_adding_reactions(msg, Treasure.controls.keys())
         if hasattr(user, "id"):
             pred = ReactionPredicate.with_emojis(tuple(Treasure.controls.keys()), msg, user)
         else:
             pred = ReactionPredicate.with_emojis(tuple(Treasure.controls.keys()), msg, ctx.author)
         react = None
         try:
             react, user = await ctx.bot.wait_for("reaction_add", check=pred, timeout=60)
         except asyncio.TimeoutError:
             await ctx.send("Item claim timed out after one minute. Selling...")
             react_emoji = "💰"
         try:
             await msg.clear_reactions()
         except discord.Forbidden:  # cannot remove all reactions
             for key in Treasure.controls.keys():
                 await msg.remove_reaction(key, ctx.bot.user)
         if luckbonus != 0:
             if Userdata.users[str(user.id)]['buffs']['luck']['duration'] <= 1:
                 Userdata.users[str(user.id)]['buffs'].pop('luck')
                 luckbonus = 0
             else:
                 Userdata.users[str(user.id)]['buffs']['luck']['duration'] = Userdata.users[str(user.id)]['buffs']['luck']['duration'] - 1
         await Userdata.save()
         if react != None:
             react_emoji = react.emoji
         return {"itemname": itemname,"item":item,"equip":Treasure.controls[react_emoji]}
     else:
         if len(item["slot"]) == 2: # two handed weapons add their bonuses twice
             hand = "two handed"
             att = item["att"]*2
             cha = item["cha"]*2
         else:
             if item["slot"][0] == "right" or item["slot"][0] == "left":
                 hand = item["slot"][0] + " handed"
             else:
                 hand = item["slot"][0] + " slot"
             att = item["att"]
             cha = item["cha"]
         if hasattr(user, "display_name"):
             await ctx.send("```css\n{} found a {}. (Attack: {}, Charisma: {} [{}])```".format(user.display_name,itemname,str(att),str(cha),hand))
         else:
             await ctx.send("```css\nYour {} found a {}. (Attack: {}, Charisma: {} [{}])```".format(user,itemname,str(att),str(cha),hand))
         msg = await ctx.send("Do you want to equip, put in backpack or sell this item?")
         start_adding_reactions(msg, Treasure.controls.keys())
         if hasattr(user, "id"):
             pred = ReactionPredicate.with_emojis(tuple(Treasure.controls.keys()), msg, user)
         else:
             pred = ReactionPredicate.with_emojis(tuple(Treasure.controls.keys()), msg, ctx.author)
         react = None
         try:
             react, user = await ctx.bot.wait_for("reaction_add", check=pred, timeout=60)
         except asyncio.TimeoutError:
             await ctx.send("Item claim timed out after one minute. Selling...")
             react_emoji = "💰"
         try:
             await msg.clear_reactions()
         except discord.Forbidden:  # cannot remove all reactions
             for key in Treasure.controls.keys():
                 await msg.remove_reaction(key, ctx.bot.user)
         if luckbonus != 0:
             if Userdata.users[str(user.id)]['buffs']['luck']['duration'] <= 1:
                 Userdata.users[str(user.id)]['buffs'].pop('luck')
                 luckbonus = 0
             else:
                 Userdata.users[str(user.id)]['buffs']['luck']['duration'] = Userdata.users[str(user.id)]['buffs']['luck']['duration'] - 1
         await Userdata.save()
         if react != None:
             react_emoji = react.emoji
         return {"itemname": itemname,"item":item,"equip":Treasure.controls[react_emoji]}
示例#16
0
    async def dehoist(self,
                      ctx: commands.Context,
                      *,
                      role: discord.Role = None):
        """Decancer all members of the targeted role.

        Role defaults to all members of the server."""
        if not await self.config.guild(ctx.guild).modlogchannel():
            await ctx.send(
                f"Set up a modlog for this server using `{ctx.prefix}decancerset modlog #channel`"
            )
            ctx.command.reset_cooldown(ctx)
            return

        role = role or ctx.guild.default_role
        guild = ctx.guild
        cancerous_list = [
            member for member in role.members
            if not member.bot and self.is_cancerous(member.display_name)
            and ctx.me.top_role > member.top_role
        ]
        if not cancerous_list:
            await ctx.send(f"There's no one I can decancer in **`{role}`**.")
            ctx.command.reset_cooldown(ctx)
            return
        if len(cancerous_list) > 5000:
            await ctx.send(
                "There are too many members to decancer in the targeted role. "
                "Please select a role with less than 5000 members.")
            ctx.command.reset_cooldown(ctx)
            return
        member_preview = "\n".join(
            f"{member} - {member.id}"
            for index, member in enumerate(cancerous_list, 1) if
            index <= 10) + (f"\nand {len(cancerous_list) - 10} other members.."
                            if len(cancerous_list) > 10 else "")

        case = "" if len(cancerous_list) == 1 else "s"
        msg = await ctx.send(
            f"Are you sure you want me to decancer the following {len(cancerous_list)} member{case}?\n"
            + box(member_preview, "py"))
        start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
        pred = ReactionPredicate.yes_or_no(msg, ctx.author)
        try:
            await self.bot.wait_for("reaction_add", check=pred, timeout=60)
        except asyncio.TimeoutError:
            await ctx.send("Action cancelled.")
            ctx.command.reset_cooldown(ctx)
            return

        if pred.result is True:
            await ctx.send(
                f"Ok. This will take around **{humanize_timedelta(timedelta=timedelta(seconds=len(cancerous_list) * 1.5))}**."
            )
            async with ctx.typing():
                for member in cancerous_list:
                    await asyncio.sleep(1)
                    old_nick = member.display_name
                    new_cool_nick = await self.nick_maker(
                        guild, member.display_name)
                    if old_nick.lower() != new_cool_nick.lower():
                        try:
                            await member.edit(
                                reason=
                                f"Dehoist | Old name ({old_nick}): contained special characters",
                                nick=new_cool_nick,
                            )
                        except discord.Forbidden:
                            await ctx.send(
                                "Dehoist failed due to invalid permissions.")
                            return
                        except discord.NotFound:
                            continue
                    # else:
                    #     await self.decancer_log(
                    #         guild, member, guild.me, old_nick, new_cool_nick, "dehoist"
                    #     )
            try:
                await ctx.send("Dehoist completed.")
            except (discord.NotFound, discord.Forbidden):
                pass
        else:
            await ctx.send("Action cancelled.")
            ctx.command.reset_cooldown(ctx)
            return
示例#17
0
    async def _trader(self, ctx: commands.Context, bypass=False):
        em_list = ReactionPredicate.NUMBER_EMOJIS

        cart = await self.config.cart_name()
        if await self.config.guild(ctx.guild).cart_name():
            cart = await self.config.guild(ctx.guild).cart_name()
        text = box(_("[{} is bringing the cart around!]").format(cart), lang="css")
        timeout = await self.config.guild(ctx.guild).cart_timeout()
        if ctx.guild.id not in self._last_trade:
            self._last_trade[ctx.guild.id] = 0

        if not bypass:
            if self._last_trade[ctx.guild.id] == 0:
                self._last_trade[ctx.guild.id] = time.time()
            elif self._last_trade[ctx.guild.id] >= time.time() - timeout:
                # trader can return after 3 hours have passed since last visit.
                return  # silent return.
        self._last_trade[ctx.guild.id] = time.time()

        room = await self.config.guild(ctx.guild).cartroom()
        if room:
            room = ctx.guild.get_channel(room)
        if room is None or bypass:
            room = ctx
        self.bot.dispatch("adventure_cart", ctx)  # dispatch after silent return
        stockcount = random.randint(3, 9)
        controls = {em_list[i + 1]: i for i in range(stockcount)}
        self._curent_trader_stock[ctx.guild.id] = (stockcount, controls)

        stock = await self._trader_get_items(ctx, stockcount)
        currency_name = await bank.get_currency_name(
            ctx.guild,
        )
        if str(currency_name).startswith("<"):
            currency_name = "credits"
        for (index, item) in enumerate(stock):
            item = stock[index]
            if len(item["item"].slot) == 2:  # two handed weapons add their bonuses twice
                hand = "two handed"
                att = item["item"].att * 2
                cha = item["item"].cha * 2
                intel = item["item"].int * 2
                luck = item["item"].luck * 2
                dex = item["item"].dex * 2
            else:
                if item["item"].slot[0] == "right" or item["item"].slot[0] == "left":
                    hand = item["item"].slot[0] + _(" handed")
                else:
                    hand = item["item"].slot[0] + _(" slot")
                att = item["item"].att
                cha = item["item"].cha
                intel = item["item"].int
                luck = item["item"].luck
                dex = item["item"].dex
            text += box(
                _(
                    "\n[{i}] Lvl req {lvl} | {item_name} ("
                    "Attack: {str_att}, "
                    "Charisma: {str_cha}, "
                    "Intelligence: {str_int}, "
                    "Dexterity: {str_dex}, "
                    "Luck: {str_luck} "
                    "[{hand}]) for {item_price} {currency_name}."
                ).format(
                    i=str(index + 1),
                    item_name=item["item"].formatted_name,
                    lvl=item["item"].lvl,
                    str_att=str(att),
                    str_int=str(intel),
                    str_cha=str(cha),
                    str_luck=str(luck),
                    str_dex=str(dex),
                    hand=hand,
                    item_price=humanize_number(item["price"]),
                    currency_name=currency_name,
                ),
                lang="css",
            )
        text += _("Do you want to buy any of these fine items? Tell me which one below:")
        msg = await room.send(text)
        start_adding_reactions(msg, controls.keys())
        self._current_traders[ctx.guild.id] = {"msg": msg.id, "stock": stock, "users": []}
        timeout = self._last_trade[ctx.guild.id] + 180 - time.time()
        if timeout <= 0:
            timeout = 0
        timer = await self._cart_countdown(ctx, timeout, _("The cart will leave in: "), room=room)
        self.tasks[msg.id] = timer
        try:
            await asyncio.wait_for(timer, timeout + 5)
        except asyncio.TimeoutError:
            await self._clear_react(msg)
            return
        with contextlib.suppress(discord.HTTPException):
            await msg.delete()
示例#18
0
    async def daily(self, ctx):
        """View the daily deals.

        These will come at a lower price than the store, but can only be bought once per day.

        Status guide:
            A: Available to be bought and put in backyard
            B: Already purchased
            S: Available to be bought, but will be put in stash because you either do not have the space for the, or above your level threshold"""
        async with self.lock:
            data = await self.conf.user(ctx.author).all()
        animals = data["animals"]
        animal = data["animal"]

        if animal in ["", "P"]:
            return await ctx.send("Finish starting your evolution first")

        multiplier = data["multiplier"]
        highest = max(list(map(int, animals.keys())))
        e = 6 + math.ceil((multiplier - 1) * 5)

        display = []
        deals = await self.conf.daily()
        for did, deal in deals.items():
            status = ""
            amount = deal["details"]["amount"]
            level = deal["details"]["level"]
            if ctx.author.id in deal["bought"]:
                status = "[B]"
            elif (level > int(highest) - 3
                  and level != 1) or (amount + animals.get(str(level), 0) > e):
                status = "#S "
            else:
                status = " A "

            price = self.utils.get_total_price(level, 0, amount, False) * 0.75

            display.append([
                did,
                status,
                humanize_number(price),
                f"{amount} Level {level} {animal}{'s' if amount != 1 else ''}",
            ])

        message = await ctx.send(
            f"{box(tabulate(display, tablefmt='psql'), lang='css')}Would you like to buy any of these fine animals?  Click the corresponding reaction below."
        )
        emojis = ReactionPredicate.NUMBER_EMOJIS[1:7]
        start_adding_reactions(message, emojis)

        pred = ReactionPredicate.with_emojis(emojis, message, ctx.author)
        try:
            await self.bot.wait_for("reaction_add", check=pred, timeout=60.0)
        except asyncio.TimeoutError:
            return await ctx.send(
                "The vendor grew uncomfortable with you there, and told you to leave and come back later."
            )

        if ctx.author.id in self.inmarket:
            return await ctx.send(
                "Complete your current transaction or evolution first.")
        self.inmarket.append(ctx.author.id)
        buying = pred.result + 1

        deal = deals[str(buying)]
        if ctx.author.id in deal["bought"]:  # ;no
            self.inmarket.remove(ctx.author.id)
            return await ctx.send(
                "You already bought this deal.  You cannot buy daily deals multiple times."
            )

        level = deal["details"]["level"]
        amount = deal["details"]["amount"]

        price = self.utils.get_total_price(level, 0, amount, False) * 0.75
        balance = await bank.get_balance(ctx.author)

        if balance < price:
            self.inmarket.remove(ctx.author.id)
            return await ctx.send(
                f"You need {humanize_number(price - balance)} more credits to buy that deal."
            )

        stashing = 0
        delivering = amount
        if level > int(highest) - 3 and level != 1:
            stashing = amount
            delivering = 0
        elif amount + animals.get(str(level), 0) > e:
            delivering = e - animals[str(level)]
            stashing = amount - delivering

        async with self.lock:
            async with self.conf.user(ctx.author).all() as data:
                data["animals"][str(level)] = animals.get(str(level),
                                                          0) + delivering

                if stashing:
                    current_stash = data["stash"]["animals"].get(str(level), 0)
                    data["stash"]["animals"][str(
                        level)] = current_stash + stashing

                self.cache[ctx.author.id] = data
            async with self.conf.daily(
            ) as data:  # In case someone buys at the same time, we need to re-read the data
                data[str(buying)]["bought"].append(ctx.author.id)

        await bank.withdraw_credits(ctx.author, int(price))
        await ctx.send(
            box(
                (f"[Transaction Complete]\nYou spent {humanize_number(price)} credits to buy {amount} Level {str(level)} {animal}{'s' if amount != 1 else ''}."
                 f"\n\n{delivering} have been added to your backyard, {stashing} have been sent to your stash."
                 ),
                "css",
            ))
        self.inmarket.remove(ctx.author.id)
示例#19
0
    async def ao3(self, ctx, ficlink, *, notes=""):
        """Returns details of a fic from a link

        If the fic you inputted is wrong, just click the ❎ emoji to delete the message (Needs Manage Messages permissions)."""

        # SET NOTES
        if notes == "":
            notes = "None."
        else:
            nlimit = await self.config.guild(ctx.guild).noteslimit()
            notes = notes[:nlimit]

        # GET URL
        if "chapter" in ficlink:
            newlink = ficlink.split("chapters")[0]
            ficlink = str(newlink)
        if "collections" in ficlink:
            newlink = ficlink.split("/works/")[1]
            ficlink = str(f"https://archiveofourown.org/works/{newlink}")
        if "?view_full_work=true" in ficlink:
            newlink = ficlink.split("?")[0]
            ficlink = str(newlink)

        firstchap = f"{ficlink}/navigate"
        async with self.session.get(firstchap) as ao3navigation:
            navigate = BeautifulSoup(await ao3navigation.text(),
                                     'html.parser',
                                     parse_only=SoupStrainer("ol"))
        try:
            firstchap = navigate.find("li").a['href']
            url = f"https://archiveofourown.org{firstchap}?view_adult=true"
        except AttributeError:
            return await ctx.send(
                "Error loading work info. Please ensure that the work is not locked."
            )

        # START SCRAPING
        async with self.session.get(url) as ao3session:
            result = BeautifulSoup(await ao3session.text(), 'html.parser')

        # GET AUTHORS
        try:
            a = result.find_all("a", {'rel': 'author'})
            author_list = []
            for author in a:
                author_list.append(author.string.strip())
            try:
                authors = humanize_list(deduplicate_iterables(author_list))
            except Exception:
                authors = "Anonymous"
        except Exception:
            return await ctx.send("Error loading author list.")

        # GET TITLE
        try:
            preface = result.find("div", {'class': 'preface group'}).h2.string
            title = str(preface.strip())
        except Exception:
            title = "No title found."

        # GET FANDOM
        try:
            fan = result.find("dd", {'class': 'fandom tags'})
            fan_list = []
            fandomlimit = await self.config.guild(ctx.guild).fandomlimit()
            for fandom in fan.find_all("li", limit=fandomlimit):
                fan_list.append(fandom.a.string)
            fandom = humanize_list(fan_list)
        except Exception:
            fandom = "No fandom found."

        # GET PAIRING
        try:
            reltags = result.find("dd", {'class': 'relationship tags'})
            pair_list = []
            pairlimit = await self.config.guild(ctx.guild).pairlimit()
            for rel in reltags.find_all("li", limit=pairlimit):
                pair_list.append(rel.a.string)
            pairing = humanize_list(pair_list)
        except Exception:
            pairing = "No Pairing."

        # GET CHAPTERS
        chapters = result.find("dd", {'class': 'chapters'})
        totalchapters = str(BeautifulSoup.getText(chapters))

        # GET STATUS
        chap_list = totalchapters.split("/")
        if "?" in chap_list[1]:
            status = "Work in Progress"
        elif chap_list[0] != chap_list[1]:
            status = "Work in Progress"
        else:
            status = "Complete"

        # GET RATING
        try:
            rate = result.find("dd", {'class': 'rating tags'})
            rating = rate.a.string
        except Exception:
            rating = "Not Rated"

        # GET SUMMARY
        try:
            div = result.find("div", {'class': 'preface group'})
            userstuff = div.find("blockquote", {'class': 'userstuff'})
            stuff = str(BeautifulSoup.getText(userstuff))
            summarytest = f"{stuff}".replace('. ', '**').replace('.', '. ')
            summ = f"{summarytest}".replace('**', '. \n\n')
            slimit = await self.config.guild(ctx.guild).sumlimit()
            summary = summ[:slimit]
        except Exception:
            summary = "No work summary found."

        # GET TAGS
        try:
            use_censor = await self.config.guild(ctx.guild).censor()
            freeform = result.find("dd", {'class': 'freeform tags'})
            tag_list = []
            taglimit = await self.config.guild(ctx.guild).taglimit()
            for tag in freeform.find_all("li", limit=taglimit):
                tag_list.append(tag.a.string)
            if "Explicit" in rating and use_censor:
                tags = f"||{(humanize_list(tag_list))}||"
            else:
                tags = humanize_list(tag_list)

        except Exception:
            tags = "No tags found."

        # GET DATE PUBLISHED AND UPDATED
        published = result.find("dd", {'class': 'published'}).string.strip()
        try:
            updated = result.find("dd", {'class': 'status'}).string.strip()
        except Exception:
            updated = published

        # GET LANGUAGE
        language = result.find("dd", {'class': 'language'}).string.strip()

        # GET WORDS
        words = int(
            result.find("dd", {
                'class': 'words'
            }).string.replace(",", ""))

        # GET KUDOS
        try:
            kudos = int(
                result.find("dd", {
                    'class': 'kudos'
                }).string.replace(",", ""))
        except AttributeError:
            kudos = 0

        # GET HITS
        try:
            hits = int(
                result.find("dd", {
                    'class': 'hits'
                }).string.replace(",", ""))
        except AttributeError:
            hits = 0

        # GET WARNINGS
        warntags = result.find("dd", {'class': 'warning tags'})
        warn_list = []
        try:
            for warning in warntags.find_all("li"):
                warn_list.append(warning.a.string)
            warnings = humanize_list(warn_list)
        except Exception:
            warnings = "No warnings found."

        # CHECK INFO FORMAT
        use_embed = await self.config.guild(ctx.guild).embed()
        data = await self.config.guild(ctx.guild).formatting()

        if use_embed:
            data = discord.Embed(description=summary,
                                 title=title,
                                 url=ficlink,
                                 colour=3553599)
            data.add_field(name="Author:", value=authors, inline=False)
            data.add_field(name="Fandom:", value=fandom, inline=False)
            data.add_field(name="Rating:", value=rating, inline=False)
            data.add_field(name="Pairings:", value=pairing, inline=False)
            data.add_field(name="Tags:", value=tags, inline=False)
            data.add_field(name=f"Rec Notes by {ctx.author}: ",
                           value=notes,
                           inline=False)
            data.set_footer(
                text=
                f"Language: {language}     |       Words: {words}       |       Date Updated: {updated}        |       Status: {status}        "
            )
            ao3msg = await ctx.send(embed=data)

        else:
            params = {
                "title": title,
                "authors": authors,
                "rating": rating,
                "warnings": warnings,
                "language": language,
                "fandom": fandom,
                "pairing": pairing,
                "tags": tags,
                "summary": summary,
                "totalchapters": totalchapters,
                "status": status,
                "words": words,
                "kudos": kudos,
                "hits": hits,
                "reccer": ctx.author.mention,
                "notes": notes,
                "url": f"<{ficlink}>",
                "published": published,
                "updated": updated
            }
            ao3msg = await ctx.send(data.format(**params))

        start_adding_reactions(ao3msg, ReactionPredicate.YES_OR_NO_EMOJIS)

        pred = ReactionPredicate.yes_or_no(ao3msg, ctx.author)
        try:
            await ctx.bot.wait_for("reaction_add", check=pred, timeout=30)
            await self._clear_react(ao3msg)

            if pred.result is False:
                await ao3msg.delete()
                return

        except asyncio.TimeoutError:
            await self._clear_react(ao3msg)

        autodel = await self.config.guild(ctx.guild).autodelete()

        try:
            if autodel is True:
                await ctx.message.delete()
            return
        except Exception:
            return
示例#20
0
class Warnings_Custom(commands.Cog):
    """Warn misbehaving users and take automated actions."""

    default_guild = {
        "actions": [],
        "reasons": {},
        "allow_custom_reasons": False,
        "allow_context": False,
        "toggle_dm": True,
        "show_mod": False,
        "warn_channel": None,
        "toggle_channel": False,
    }

    default_member = {"total_points": 0, "status": "", "warnings": {}}

    def __init__(self, bot: Red):
        super().__init__()
        self.config = Config.get_conf(self, identifier=5757575755)
        self.config.register_guild(**self.default_guild)
        self.config.register_member(**self.default_member)
        self.bot = bot
        self.registration_task = self.bot.loop.create_task(
            self.register_warningtype())

    async def red_delete_data_for_user(
        self,
        *,
        requester: Literal["discord_deleted_user", "owner", "user",
                           "user_strict"],
        user_id: int,
    ):
        if requester != "discord_deleted_user":
            return

        all_members = await self.config.all_members()

        c = 0

        for guild_id, guild_data in all_members.items():
            c += 1
            if not c % 100:
                await asyncio.sleep(0)

            if user_id in guild_data:
                await self.config.member_from_ids(guild_id, user_id).clear()

            for remaining_user, user_warns in guild_data.items():
                c += 1
                if not c % 100:
                    await asyncio.sleep(0)

                for warn_id, warning in user_warns.get("warnings", {}).items():
                    c += 1
                    if not c % 100:
                        await asyncio.sleep(0)

                    if warning.get("mod", 0) == user_id:
                        grp = self.config.member_from_ids(
                            guild_id, remaining_user)
                        await grp.set_raw("warnings",
                                          warn_id,
                                          "mod",
                                          value=0xDE1)

    # We're not utilising modlog yet - no need to register a casetype
    @staticmethod
    async def register_warningtype():
        casetypes_to_register = [
            {
                "name": "warning",
                "default_setting": True,
                "image": "\N{WARNING SIGN}\N{VARIATION SELECTOR-16}",
                "case_str": "Warning",
            },
            {
                "name": "unwarned",
                "default_setting": True,
                "image": "\N{WARNING SIGN}\N{VARIATION SELECTOR-16}",
                "case_str": "Unwarned",
            },
        ]
        try:
            await modlog.register_casetypes(casetypes_to_register)
        except RuntimeError:
            pass

    @commands.group()
    @commands.guild_only()
    @checks.guildowner_or_permissions(administrator=True)
    async def warningset(self, ctx: commands.Context):
        """Manage settings for Warnings."""
        pass

    @warningset.command()
    @commands.guild_only()
    async def allowcustomreasons(self, ctx: commands.Context, allowed: bool):
        """Enable or disable custom reasons for a warning."""
        guild = ctx.guild
        await self.config.guild(guild).allow_custom_reasons.set(allowed)
        if allowed:
            await ctx.send(_("Custom reasons have been enabled."))
        else:
            await ctx.send(_("Custom reasons have been disabled."))

    @warningset.command()
    @commands.guild_only()
    async def allowcontext(self, ctx: commands.Context, allowed: bool):
        """Enable or disable adding context to warnings."""
        guild = ctx.guild
        await self.config.guild(guild).allow_context.set(allowed)
        if allowed:
            await ctx.send(_("Context has been enabled."))
        else:
            await ctx.send(_("Context has been disabled."))

    @warningset.command()
    @commands.guild_only()
    async def senddm(self, ctx: commands.Context, true_or_false: bool):
        """Set whether warnings should be sent to users in DMs."""
        await self.config.guild(ctx.guild).toggle_dm.set(true_or_false)
        if true_or_false:
            await ctx.send(_("I will now try to send warnings to users DMs."))
        else:
            await ctx.send(_("Warnings will no longer be sent to users DMs."))

    @warningset.command()
    @commands.guild_only()
    async def showmoderator(self, ctx, true_or_false: bool):
        """Decide whether the name of the moderator warning a user should be included in the DM to that user."""
        await self.config.guild(ctx.guild).show_mod.set(true_or_false)
        if true_or_false:
            await ctx.send(
                _("I will include the name of the moderator who issued the warning when sending a DM to a user."
                  ))
        else:
            await ctx.send(
                _("I will not include the name of the moderator who issued the warning when sending a DM to a user."
                  ))

    @warningset.command()
    @commands.guild_only()
    async def warnchannel(self,
                          ctx: commands.Context,
                          channel: discord.TextChannel = None):
        """Set the channel where warnings should be sent to.

        Leave empty to use the channel `[p]warn` command was called in.
        """
        guild = ctx.guild
        if channel:
            await self.config.guild(guild).warn_channel.set(channel.id)
            await ctx.send(
                _("The warn channel has been set to {channel}.").format(
                    channel=channel.mention))
        else:
            await self.config.guild(guild).warn_channel.set(channel)
            await ctx.send(
                _("Warnings will now be sent in the channel command was used in."
                  ))

    @warningset.command()
    @commands.guild_only()
    async def usewarnchannel(self, ctx: commands.Context, true_or_false: bool):
        """
        Set if warnings should be sent to a channel set with `[p]warningset warnchannel`.
        """
        await self.config.guild(ctx.guild).toggle_channel.set(true_or_false)
        channel = self.bot.get_channel(await
                                       self.config.guild(ctx.guild
                                                         ).warn_channel())
        if true_or_false:
            if channel:
                await ctx.send(
                    _("Warnings will now be sent to {channel}.").format(
                        channel=channel.mention))
            else:
                await ctx.send(
                    _("Warnings will now be sent in the channel command was used in."
                      ))
        else:
            await ctx.send(_("Toggle channel has been disabled."))

    @commands.group()
    @commands.guild_only()
    @checks.guildowner_or_permissions(administrator=True)
    async def warnaction(self, ctx: commands.Context):
        """Manage automated actions for Warnings.

        Actions are essentially command macros. Any command can be run
        when the action is initially triggered, and/or when the action
        is lifted.

        Actions must be given a name and a points threshold. When a
        user is warned enough so that their points go over this
        threshold, the action will be executed.
        """
        pass

    @warnaction.command(name="add")
    @commands.guild_only()
    async def action_add(self, ctx: commands.Context, name: str, points: int):
        """Create an automated action.

        Duplicate action names are not allowed.
        """
        guild = ctx.guild

        exceed_command = await get_command_for_exceeded_points(ctx)
        drop_command = await get_command_for_dropping_points(ctx)

        to_add = {
            "action_name": name,
            "points": points,
            "exceed_command": exceed_command,
            "drop_command": drop_command,
        }

        # Have all details for the action, now save the action
        guild_settings = self.config.guild(guild)
        async with guild_settings.actions() as registered_actions:
            for act in registered_actions:
                if act["action_name"] == to_add["action_name"]:
                    await ctx.send(_("Duplicate action name found!"))
                    break
            else:
                registered_actions.append(to_add)
                # Sort in descending order by point count for ease in
                # finding the highest possible action to take
                registered_actions.sort(key=lambda a: a["points"],
                                        reverse=True)
                await ctx.send(
                    _("Action {name} has been added.").format(name=name))

    @warnaction.command(name="delete", aliases=["del", "remove"])
    @commands.guild_only()
    async def action_del(self, ctx: commands.Context, action_name: str):
        """Delete the action with the specified name."""
        guild = ctx.guild
        guild_settings = self.config.guild(guild)
        async with guild_settings.actions() as registered_actions:
            to_remove = None
            for act in registered_actions:
                if act["action_name"] == action_name:
                    to_remove = act
                    break
            if to_remove:
                registered_actions.remove(to_remove)
                await ctx.tick()
            else:
                await ctx.send(
                    _("No action named {name} exists!").format(
                        name=action_name))

    @commands.group()
    @commands.guild_only()
    @checks.guildowner_or_permissions(administrator=True)
    async def warnreason(self, ctx: commands.Context):
        """Manage warning reasons.

        Reasons must be given a name, description and points value. The
        name of the reason must be given when a user is warned.
        """
        pass

    @warnreason.command(name="create", aliases=["add"])
    @commands.guild_only()
    async def reason_create(self, ctx: commands.Context, name: str,
                            points: int, *, description: str):
        """Create a warning reason."""
        guild = ctx.guild

        if name.lower() == "custom":
            await ctx.send(_("*Custom* cannot be used as a reason name!"))
            return
        to_add = {"points": points, "description": description}
        completed = {name.lower(): to_add}

        guild_settings = self.config.guild(guild)

        async with guild_settings.reasons() as registered_reasons:
            registered_reasons.update(completed)

        await ctx.send(_("The new reason has been registered."))

    @warnreason.command(name="delete", aliases=["remove", "del"])
    @commands.guild_only()
    async def reason_del(self, ctx: commands.Context, reason_name: str):
        """Delete a warning reason."""
        guild = ctx.guild
        guild_settings = self.config.guild(guild)
        async with guild_settings.reasons() as registered_reasons:
            if registered_reasons.pop(reason_name.lower(), None):
                await ctx.tick()
            else:
                await ctx.send(_("That is not a registered reason name."))

    @commands.command()
    @commands.guild_only()
    @checks.admin_or_permissions(ban_members=True)
    async def reasonlist(self, ctx: commands.Context):
        """List all configured reasons for Warnings."""
        guild = ctx.guild
        guild_settings = self.config.guild(guild)
        msg_list = []
        async with guild_settings.reasons() as registered_reasons:
            for r, v in registered_reasons.items():
                if await ctx.embed_requested():
                    em = discord.Embed(
                        title=_("Reason: {name}").format(name=r),
                        description=v["description"],
                    )
                    em.add_field(name=_("Points"), value=str(v["points"]))
                    msg_list.append(em)
                else:
                    msg_list.append(
                        _("Name: {reason_name}\nPoints: {points}\nDescription: {description}"
                          ).format(reason_name=r, **v))
        if msg_list:
            await menu(ctx, msg_list, DEFAULT_CONTROLS)
        else:
            await ctx.send(_("There are no reasons configured!"))

    @commands.command()
    @commands.guild_only()
    @checks.admin_or_permissions(ban_members=True)
    async def actionlist(self, ctx: commands.Context):
        """List all configured automated actions for Warnings."""
        guild = ctx.guild
        guild_settings = self.config.guild(guild)
        msg_list = []
        async with guild_settings.actions() as registered_actions:
            for r in registered_actions:
                if await ctx.embed_requested():
                    em = discord.Embed(title=_("Action: {name}").format(
                        name=r["action_name"]))
                    em.add_field(name=_("Points"),
                                 value="{}".format(r["points"]),
                                 inline=False)
                    em.add_field(
                        name=_("Exceed command"),
                        value=r["exceed_command"],
                        inline=False,
                    )
                    em.add_field(name=_("Drop command"),
                                 value=r["drop_command"],
                                 inline=False)
                    msg_list.append(em)
                else:
                    msg_list.append(
                        _("Name: {action_name}\nPoints: {points}\n"
                          "Exceed command: {exceed_command}\nDrop command: {drop_command}"
                          ).format(**r))
        if msg_list:
            await menu(ctx, msg_list, DEFAULT_CONTROLS)
        else:
            await ctx.send(_("There are no actions configured!"))

    @commands.command()
    @commands.guild_only()
    @checks.admin_or_permissions(ban_members=True)
    async def warn(
        self,
        ctx: commands.Context,
        user: discord.Member,
        points: UserInputOptional[int] = 1,
        *,
        reason: str,
    ):
        """Warn the user for the specified reason. Context can be provided after running command

        `<points>` number of points the warning should be for. If no number is supplied
        1 point will be given. Pre-set warnings disregard this.
        `<reason>` can be a registered reason if it exists or a custom one
        is created by default.
        """
        guild = ctx.guild
        if user == ctx.author:
            return await ctx.send(_("You cannot warn yourself."))
        if user.bot:
            return await ctx.send(_("You cannot warn other bots."))
        if user == ctx.guild.owner:
            return await ctx.send(_("You cannot warn the server owner."))
        if user.top_role >= ctx.author.top_role and ctx.author != ctx.guild.owner:
            return await ctx.send(
                _("The person you're trying to warn is equal or higher than you in the discord hierarchy, you cannot warn them."
                  ))
        guild_settings = await self.config.guild(ctx.guild).all()
        custom_allowed = guild_settings["allow_custom_reasons"]

        reason_type = None
        async with self.config.guild(
                ctx.guild).reasons() as registered_reasons:
            if (reason_type := registered_reasons.get(reason.lower())) is None:
                msg = _("That is not a registered reason!")
                if custom_allowed:
                    reason_type = {"description": reason, "points": points}
                else:
                    # logic taken from `[p]permissions canrun`
                    fake_message = copy(ctx.message)
                    fake_message.content = f"{ctx.prefix}warningset allowcustomreasons"
                    fake_context = await ctx.bot.get_context(fake_message)
                    try:
                        can = await self.allowcustomreasons.can_run(
                            fake_context,
                            check_all_parents=True,
                            change_permission_state=False)
                    except commands.CommandError:
                        can = False
                    if can:
                        msg += " " + _(
                            "Do `{prefix}warningset allowcustomreasons true` to enable custom "
                            "reasons.").format(prefix=ctx.clean_prefix)
                    return await ctx.send(msg)
        if reason_type is None:
            return

        # get context of reason, if provided
        context = ""
        if await self.config.guild(guild).allow_context():
            msg = await ctx.send(
                "Would you like to provide more context to the warning? (react with yes or no)",
                delete_after=31)
            start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
            pred = ReactionPredicate.yes_or_no(msg, ctx.author)

            try:
                await self.bot.wait_for("reaction_add", check=pred, timeout=30)
            except asyncio.TimeoutError:
                await ctx.send(error("Took too long, cancelling warning!"),
                               delete_after=30)
                return

            if pred.result:
                done = False
                while not done:
                    await ctx.send(
                        "Please provide context as text and/or an attachment.",
                        delete_after=240)
                    pred = MessagePredicate.same_context(ctx)
                    try:
                        msg = await self.bot.wait_for("message",
                                                      check=pred,
                                                      timeout=240)
                    except asyncio.TimeoutError:
                        await ctx.send(
                            error("Took too long, cancelling warning!"),
                            delete_after=30)
                        return

                    yes_or_no = await ctx.send(
                        "Continue with provided context? React no to redo.",
                        delete_after=31)
                    start_adding_reactions(yes_or_no,
                                           ReactionPredicate.YES_OR_NO_EMOJIS)
                    pred = ReactionPredicate.yes_or_no(yes_or_no, ctx.author)
                    try:
                        await self.bot.wait_for("reaction_add",
                                                check=pred,
                                                timeout=30)
                    except asyncio.TimeoutError:
                        await ctx.send(
                            error("Took too long, cancelling warning!"))
                        return

                    done = pred.result

                if len(msg.attachments):
                    urls = "\n".join([a.url for a in msg.attachments])
                    context = f"{msg.content}\n**urls**: {urls}"
                else:
                    context = msg.content

        member_settings = self.config.member(user)
        current_point_count = await member_settings.total_points()
        current_point_count += reason_type["points"]
        await member_settings.total_points.set(current_point_count)

        await warning_points_add_check(self.config, ctx, user,
                                       current_point_count)
        dm = guild_settings["toggle_dm"]
        showmod = guild_settings["show_mod"]
        dm_failed = False
        if dm:
            if showmod:
                title = _("Warning from {user}").format(user=ctx.author)
            else:
                title = _("Warning")
            em = discord.Embed(
                title=title,
                description=reason_type["description"],
            )
            em.add_field(name=_("Points"), value=str(reason_type["points"]))
            try:
                await user.send(
                    _("You have received a warning in {guild_name}.").format(
                        guild_name=ctx.guild.name),
                    embed=em,
                )
            except discord.HTTPException:
                dm_failed = True

        if dm_failed:
            await ctx.send(
                _("A warning for {user} has been issued,"
                  " but I wasn't able to send them a warn message.").format(
                      user=user.mention))

        toggle_channel = guild_settings["toggle_channel"]
        if toggle_channel:
            if showmod:
                title = _("Warning from {user}").format(user=ctx.author)
            else:
                title = _("Warning")
            em = discord.Embed(
                title=title,
                description=reason_type["description"],
            )
            em.add_field(name=_("Points"), value=str(reason_type["points"]))
            warn_channel = self.bot.get_channel(guild_settings["warn_channel"])
            if warn_channel:
                if warn_channel.permissions_for(guild.me).send_messages:
                    with contextlib.suppress(discord.HTTPException):
                        await warn_channel.send(
                            _("{user} has been warned.").format(
                                user=user.mention),
                            embed=em,
                        )

            if not dm_failed:
                if warn_channel:
                    await ctx.tick()
                else:
                    await ctx.send(
                        _("{user} has been warned.").format(user=user.mention),
                        embed=em)
        else:
            if not dm_failed:
                await ctx.tick()
        reason_msg = _(
            "{reason}\n\nUse `{prefix}unwarn {user} {message}` to remove this warning.{context}"
        ).format(
            reason=_("{description}\nPoints: {points}").format(
                description=reason_type["description"],
                points=reason_type["points"]),
            prefix=ctx.clean_prefix,
            user=user.id,
            message=ctx.message.id,
            context=f"\n\n**Context**:\n{context}" if context else "",
        )
        case = await modlog.create_case(
            self.bot,
            ctx.guild,
            ctx.message.created_at.replace(tzinfo=timezone.utc),
            "warning",
            user,
            ctx.message.author,
            reason_msg,
            until=None,
            channel=None,
        )

        warning_to_add = {
            str(ctx.message.id): {
                "points":
                reason_type["points"],
                "description":
                reason_type["description"],
                "mod":
                ctx.author.id,
                "date":
                ctx.message.created_at.replace(
                    tzinfo=timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC"),
                "caseno":
                case.case_number,
            }
        }
        async with member_settings.warnings() as user_warnings:
            user_warnings.update(warning_to_add)
示例#21
0
    async def react(
        self,
        ctx: Context,
        message: discord.Message,
        emoji: Union[discord.Emoji, str],
        *,
        role: RoleHierarchyConverter,
    ):
        """
        Create a reaction role

        `<message>` can be the channel_id-message_id pair
        from copying message ID while holding SHIFT or a message link
        `<emoji>` The emoji you want people to react with to get the role.
        `<role>` The role you want people to receive for reacting.
        """
        if not message.guild or message.guild.id != ctx.guild.id:
            return await ctx.send(
                _("You cannot add a Reaction Role to a message not in this guild.")
            )
        async with self.config.guild(ctx.guild).reaction_roles() as cur_setting:
            if isinstance(emoji, discord.Emoji):
                use_emoji = str(emoji.id)
            else:
                use_emoji = str(emoji).strip("\N{VARIATION SELECTOR-16}")
            key = f"{message.channel.id}-{message.id}-{use_emoji}"
            send_to_react = False
            try:
                await message.add_reaction(str(emoji).strip("\N{VARIATION SELECTOR-16}"))
            except discord.HTTPException:
                send_to_react = True
            if ctx.guild.id not in self.settings:
                self.settings[ctx.guild.id] = await self.config.guild(ctx.guild).all()
            self.settings[ctx.guild.id]["reaction_roles"][key] = role.id
            cur_setting[key] = role.id
        async with self.config.role(role).reactions() as reactions:
            reactions.append(key)
        await ctx.send(
            _("Created the reaction role {role} to {emoji} on {message}").format(
                role=role.name, emoji=emoji, message=message.jump_url
            )
        )
        if send_to_react:
            await ctx.send(
                _(
                    "I couldn't add the emoji to the message. Please make "
                    "sure to add the emoji to the message for this to work."
                )
            )
        if not await self.config.role(role).selfassignable():
            msg_str = _(
                "{role} is not self assignable. Would you liked to make "
                "it self assignable and self removeable?"
            ).format(role=role.name, prefix=ctx.clean_prefix)
            msg = await ctx.send(msg_str)
            start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
            pred = ReactionPredicate.yes_or_no(msg, ctx.author)
            try:
                await ctx.bot.wait_for("reaction_add", check=pred, timeout=60)
            except asyncio.TimeoutError:
                return await ctx.send(
                    _("Okay I won't automatically make {role} self assignable.").format(
                        role=role.name
                    )
                )
            if pred.result:
                await self.config.role(role).selfassignable.set(True)
                await self.config.role(role).selfremovable.set(True)
                await ctx.send(
                    _("{role} has been made self assignable and self removeable.").format(
                        role=role.name
                    )
                )
示例#22
0
    async def _cog_update(self, ctx, cog_name: InstalledCog = None):
        """Update all cogs, or one of your choosing."""
        installed_cogs = set(await self.installed_cogs())

        async with ctx.typing():
            if cog_name is None:
                updated = await self._repo_manager.update_all_repos()

            else:
                try:
                    updated = await self._repo_manager.update_repo(cog_name.repo_name)
                except KeyError:
                    # Thrown if the repo no longer exists
                    updated = {}

            updated_cogs = set(cog for repo in updated for cog in repo.available_cogs)
            installed_and_updated = updated_cogs & installed_cogs

            if installed_and_updated:
                await self._reinstall_requirements(installed_and_updated)
                await self._reinstall_cogs(installed_and_updated)
                await self._reinstall_libraries(installed_and_updated)
                message = _("Cog update completed successfully.")

                cognames = {c.name for c in installed_and_updated}
                message += _("\nUpdated: ") + humanize_list(tuple(map(inline, cognames)))
            else:
                await ctx.send(_("All installed cogs are already up to date."))
                return
        await ctx.send(message)

        cognames &= set(ctx.bot.extensions.keys())  # only reload loaded cogs
        if not cognames:
            return await ctx.send(
                _("None of the updated cogs were previously loaded. Update complete.")
            )
        message = _("Would you like to reload the updated cogs?")
        can_react = ctx.channel.permissions_for(ctx.me).add_reactions
        if not can_react:
            message += " (y/n)"
        query: discord.Message = await ctx.send(message)
        if can_react:
            # noinspection PyAsyncCall
            start_adding_reactions(query, ReactionPredicate.YES_OR_NO_EMOJIS, ctx.bot.loop)
            pred = ReactionPredicate.yes_or_no(query, ctx.author)
            event = "reaction_add"
        else:
            pred = MessagePredicate.yes_or_no(ctx)
            event = "message"
        try:
            await ctx.bot.wait_for(event, check=pred, timeout=30)
        except asyncio.TimeoutError:
            await query.delete()
            return

        if pred.result is True:
            if can_react:
                with contextlib.suppress(discord.Forbidden):
                    await query.clear_reactions()
            await ctx.invoke(ctx.bot.get_cog("Core").reload, *cognames)
        else:
            if can_react:
                await query.delete()
            else:
                await ctx.send(_("OK then."))
示例#23
0
    async def command_equalizer_save(self, ctx: commands.Context, eq_preset: str = None):
        """Save the current eq settings to a preset."""
        if not self._player_check(ctx):
            return await self.send_embed_msg(ctx, title=_("Nothing playing."))
        dj_enabled = self._dj_status_cache.setdefault(
            ctx.guild.id, await self.config.guild(ctx.guild).dj_enabled()
        )
        if dj_enabled and not await self._can_instaskip(ctx, ctx.author):
            ctx.command.reset_cooldown(ctx)
            return await self.send_embed_msg(
                ctx,
                title=_("Unable To Save Preset"),
                description=_("You need the DJ role to save equalizer presets."),
            )
        if not eq_preset:
            await self.send_embed_msg(
                ctx, title=_("Please enter a name for this equalizer preset.")
            )
            try:
                eq_name_msg = await self.bot.wait_for(
                    "message",
                    timeout=15.0,
                    check=MessagePredicate.regex(fr"^(?!{re.escape(ctx.prefix)})", ctx),
                )
                eq_preset = eq_name_msg.content.split(" ")[0].strip('"').lower()
            except asyncio.TimeoutError:
                ctx.command.reset_cooldown(ctx)
                return await self.send_embed_msg(
                    ctx,
                    title=_("Unable To Save Preset"),
                    description=_(
                        "No equalizer preset name entered, try the command again later."
                    ),
                )
        eq_preset = eq_preset or ""
        eq_exists_msg = None
        eq_preset = eq_preset.lower().lstrip(ctx.prefix)
        eq_presets = await self.config.custom("EQUALIZER", ctx.guild.id).eq_presets()
        eq_list = list(eq_presets.keys())

        if len(eq_preset) > 20:
            ctx.command.reset_cooldown(ctx)
            return await self.send_embed_msg(
                ctx,
                title=_("Unable To Save Preset"),
                description=_("Try the command again with a shorter name."),
            )
        if eq_preset in eq_list:
            eq_exists_msg = await self.send_embed_msg(
                ctx, title=_("Preset name already exists, do you want to replace it?")
            )
            start_adding_reactions(eq_exists_msg, ReactionPredicate.YES_OR_NO_EMOJIS)
            pred = ReactionPredicate.yes_or_no(eq_exists_msg, ctx.author)
            await self.bot.wait_for("reaction_add", check=pred)
            if not pred.result:
                await self._clear_react(eq_exists_msg)
                embed2 = discord.Embed(
                    colour=await ctx.embed_colour(), title=_("Not saving preset.")
                )
                ctx.command.reset_cooldown(ctx)
                return await eq_exists_msg.edit(embed=embed2)

        player = lavalink.get_player(ctx.guild.id)
        eq = player.fetch("eq", Equalizer())
        to_append = {eq_preset: {"author": ctx.author.id, "bands": eq.bands}}
        new_eq_presets = {**eq_presets, **to_append}
        await self.config.custom("EQUALIZER", ctx.guild.id).eq_presets.set(new_eq_presets)
        embed3 = discord.Embed(
            colour=await ctx.embed_colour(),
            title=_("Current equalizer saved to the {preset_name} preset.").format(
                preset_name=eq_preset
            ),
        )
        if eq_exists_msg:
            await self._clear_react(eq_exists_msg)
            await eq_exists_msg.edit(embed=embed3)
        else:
            await self.send_embed_msg(ctx, embed=embed3)
示例#24
0
    async def makeDE(self, ctx, *userList):
        """Adds the Draft Eligible and League roles, removes Spectator role, and adds the DE prefix to every member that can be found from the userList"""
        empty = True
        added = 0
        had = 0
        notFound = 0
        deRole = None
        leagueRole = None
        spectatorRole = None
        formerPlayerRole = None
        message = ""
        for role in ctx.guild.roles:
            if role.name == "Draft Eligible":
                deRole = role
            elif role.name == "League":
                leagueRole = role
            elif role.name == "Spectator":
                spectatorRole = role
            elif role.name == "Former Player":
                formerPlayerRole = role
            if leagueRole and deRole and spectatorRole and formerPlayerRole:
                break

        if deRole is None or leagueRole is None or spectatorRole is None or formerPlayerRole is None:
            await ctx.send(
                ":x: Couldn't find either the Draft Eligible, League, Spectator, or Former Player role in the server. Use `{0}addRequiredServerRoles` to add these roles."
                .format(ctx.prefix))
            return

        for user in userList:
            try:
                member = await commands.MemberConverter().convert(ctx, user)
            except:
                message += "Couldn't find: {0}\n".format(user)
                notFound += 1
                continue
            if member in ctx.guild.members:
                if leagueRole in member.roles:
                    msg = await ctx.send(
                        "{0} already has the league role, are you sure you want to make him a DE?"
                        .format(member.mention))
                    start_adding_reactions(msg,
                                           ReactionPredicate.YES_OR_NO_EMOJIS)

                    pred = ReactionPredicate.yes_or_no(msg, ctx.author)
                    await ctx.bot.wait_for("reaction_add", check=pred)
                    if pred.result is False:
                        await ctx.send("{0} not made DE.".format(member.name))
                        had += 1
                        continue
                    else:
                        await ctx.send(
                            "You will need to manually remove any team or free agent roles if {0} has any."
                            .format(member.mention))

                await member.add_roles(deRole, leagueRole)
                added += 1
                await member.edit(nick="{0} | {1}".format(
                    "DE", self.get_player_nickname(member)))
                await member.remove_roles(spectatorRole, formerPlayerRole)
                deMessage = await self._draft_eligible_message(ctx)
                if deMessage:
                    # await member.send(deMessage)
                    await self._send_member_message(ctx, member, deMessage)

                empty = False

        if empty:
            message += ":x: Nobody was given the Draft Eligible role"
        else:
            message += ":white_check_mark: Draft Eligible role given to everyone that was found from list"
        if notFound > 0:
            message += ". {0} user(s) were not found".format(notFound)
        if had > 0:
            message += ". {0} user(s) already had the role or were already in the league".format(
                had)
        if added > 0:
            message += ". {0} user(s) had the role added to them".format(added)
        await ctx.send(message)
示例#25
0
    async def convert(
            self, ctx: Context,
            argument: str) -> Optional[Union[List[Dict[str, dict]], str]]:
        result: Optional[Union[List[Dict[str, dict]], str]] = []
        team_list = await check_valid_team(argument)
        my_perms = ctx.channel.permissions_for(ctx.guild.me)
        if team_list == []:
            raise BadArgument('Team "{}" not found'.format(argument))
        if len(team_list) == 1:
            result = team_list[0]
        else:
            # This is just some extra stuff to correct the team picker
            msg = _(
                "There's multiple teams with that name, pick one of these:\n")
            if my_perms.add_reactions and my_perms.use_external_emojis:
                new_msg = await ctx.send(msg)
                team_emojis = [
                    await
                    EmojiConverter().convert(ctx,
                                             "<:" + TEAMS[team]["emoji"] + ">")
                    for team in team_list
                ]
                log.debug(team_emojis)
                log.debug(team_list)
                pred = ReactionPredicate.with_emojis(team_emojis,
                                                     message=new_msg)
                start_adding_reactions(new_msg, team_emojis)
                try:
                    reaction, user = await ctx.bot.wait_for("reaction_add",
                                                            check=pred,
                                                            timeout=60)
                except asyncio.TimeoutError:
                    await new_msg.edit(content=_("I guess not."))
                    return None
                else:
                    result = team_list[pred.result]
                    log.debug(result)
            else:
                for i, team_name in enumerate(team_list):
                    msg += "{}: {}\n".format(i + 1, team_name)

                def msg_check(m):
                    return m.author == ctx.message.author

                try:
                    msg = await ctx.bot.wait_for("message",
                                                 check=msg_check,
                                                 timeout=60)
                except asyncio.TimeoutError:
                    await new_msg.edit(content=_("I guess not."))
                    return None

                if msg.content.isdigit():
                    msg = int(msg.content) - 1
                    try:
                        result = team_list[msg]
                    except (IndexError, ValueError, AttributeError):
                        pass
                else:
                    return_team = None
                    for team in team_list:
                        if msg.content.lower() in team.lower():
                            return_team = team
                    result = return_team
            if new_msg:
                await new_msg.delete()
        return result
示例#26
0
    async def command_queue(self, ctx: commands.Context, *, page: int = 1):
        """List the songs in the queue."""
        async def _queue_menu(
            ctx: commands.Context,
            pages: list,
            controls: MutableMapping,
            message: discord.Message,
            page: int,
            timeout: float,
            emoji: str,
        ):
            if message:
                await ctx.send_help(self.command_queue)
                with contextlib.suppress(discord.HTTPException):
                    await message.delete()
                return None

        queue_controls = {
            "\N{LEFTWARDS BLACK ARROW}\N{VARIATION SELECTOR-16}": prev_page,
            "\N{CROSS MARK}": close_menu,
            "\N{BLACK RIGHTWARDS ARROW}\N{VARIATION SELECTOR-16}": next_page,
            "\N{INFORMATION SOURCE}\N{VARIATION SELECTOR-16}": _queue_menu,
        }

        if not self._player_check(ctx):
            return await self.send_embed_msg(
                ctx, title=_("There's nothing in the queue."))
        player = lavalink.get_player(ctx.guild.id)

        if player.current and not player.queue:
            arrow = await self.draw_time(ctx)
            pos = self.format_time(player.position)
            if player.current.is_stream:
                dur = "LIVE"
            else:
                dur = self.format_time(player.current.length)
            song = (await self.get_track_description(
                player.current, self.local_folder_current_path) or "")
            song += _("\n Requested by: **{track.requester}**").format(
                track=player.current)
            song += f"\n\n{arrow}`{pos}`/`{dur}`"
            embed = discord.Embed(title=_("Now Playing"), description=song)
            guild_data = await self.config.guild(ctx.guild).all()
            if guild_data[
                    "thumbnail"] and player.current and player.current.thumbnail:
                embed.set_thumbnail(url=player.current.thumbnail)

            shuffle = guild_data["shuffle"]
            repeat = guild_data["repeat"]
            autoplay = guild_data["auto_play"]
            text = ""
            text += (_("Auto-Play") + ": " + ("\N{WHITE HEAVY CHECK MARK}" if
                                              autoplay else "\N{CROSS MARK}"))
            text += ((" | " if text else "") + _("Shuffle") + ": " +
                     ("\N{WHITE HEAVY CHECK MARK}"
                      if shuffle else "\N{CROSS MARK}"))
            text += (
                (" | " if text else "") + _("Repeat") + ": " +
                ("\N{WHITE HEAVY CHECK MARK}" if repeat else "\N{CROSS MARK}"))
            embed.set_footer(text=text)
            message = await self.send_embed_msg(ctx, embed=embed)
            dj_enabled = self._dj_status_cache.setdefault(
                ctx.guild.id, guild_data["dj_enabled"])
            vote_enabled = guild_data["vote_enabled"]
            if ((dj_enabled or vote_enabled)
                    and not await self._can_instaskip(ctx, ctx.author)
                    and not await self.is_requester_alone(ctx)):
                return

            emoji = {
                "prev":
                "\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}\N{VARIATION SELECTOR-16}",
                "stop": "\N{BLACK SQUARE FOR STOP}\N{VARIATION SELECTOR-16}",
                "pause":
                "\N{BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR}\N{VARIATION SELECTOR-16}",
                "next":
                "\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}\N{VARIATION SELECTOR-16}",
                "close": "\N{CROSS MARK}",
            }
            expected = tuple(emoji.values())
            if not player.queue and not autoplay:
                expected = (emoji["stop"], emoji["pause"], emoji["close"])
            if player.current:
                task: Optional[asyncio.Task] = start_adding_reactions(
                    message, expected[:5])
            else:
                task: Optional[asyncio.Task] = None

            try:
                (r, u) = await self.bot.wait_for(
                    "reaction_add",
                    check=ReactionPredicate.with_emojis(
                        expected, message, ctx.author),
                    timeout=30.0,
                )
            except asyncio.TimeoutError:
                return await self._clear_react(message, emoji)
            else:
                if task is not None:
                    task.cancel()
            reacts = {v: k for k, v in emoji.items()}
            react = reacts[r.emoji]
            if react == "prev":
                await self._clear_react(message, emoji)
                await ctx.invoke(self.command_prev)
            elif react == "stop":
                await self._clear_react(message, emoji)
                await ctx.invoke(self.command_stop)
            elif react == "pause":
                await self._clear_react(message, emoji)
                await ctx.invoke(self.command_pause)
            elif react == "next":
                await self._clear_react(message, emoji)
                await ctx.invoke(self.command_skip)
            elif react == "close":
                await message.delete()
            return
        elif not player.current and not player.queue:
            return await self.send_embed_msg(
                ctx, title=_("There's nothing in the queue."))

        async with ctx.typing():
            limited_queue = player.queue[:
                                         500]  # TODO: Improve when Toby menu's are merged
            len_queue_pages = math.ceil(len(limited_queue) / 10)
            queue_page_list = []
            async for page_num in AsyncIter(range(1, len_queue_pages + 1)):
                embed = await self._build_queue_page(ctx, limited_queue,
                                                     player, page_num)
                queue_page_list.append(embed)
            if page > len_queue_pages:
                page = len_queue_pages
        return await menu(ctx,
                          queue_page_list,
                          queue_controls,
                          page=(page - 1))
示例#27
0
文件: trivia.py 项目: rtunney/eliza
    async def _save_trivia_list(self, ctx: commands.Context,
                                attachment: discord.Attachment) -> None:
        """Checks and saves a trivia list to data folder.

        Parameters
        ----------
        file : discord.Attachment
            A discord message attachment.

        Returns
        -------
        None
        """
        filename = attachment.filename.rsplit(".", 1)[0].casefold()

        # Check if trivia filename exists in core files or if it is a command
        if filename in self.trivia.all_commands or any(
                filename == item.stem for item in get_core_lists()):
            await ctx.send(
                _("{filename} is a reserved trivia name and cannot be replaced.\n"
                  "Choose another name.").format(filename=filename))
            return

        file = cog_data_path(self) / f"{filename}.yaml"
        if file.exists():
            overwrite_message = _(
                "{filename} already exists. Do you wish to overwrite?").format(
                    filename=filename)

            can_react = ctx.channel.permissions_for(ctx.me).add_reactions
            if not can_react:
                overwrite_message += " (y/n)"

            overwrite_message_object: discord.Message = await ctx.send(
                overwrite_message)
            if can_react:
                # noinspection PyAsyncCall
                start_adding_reactions(overwrite_message_object,
                                       ReactionPredicate.YES_OR_NO_EMOJIS)
                pred = ReactionPredicate.yes_or_no(overwrite_message_object,
                                                   ctx.author)
                event = "reaction_add"
            else:
                pred = MessagePredicate.yes_or_no(ctx=ctx)
                event = "message"
            try:
                await ctx.bot.wait_for(event, check=pred, timeout=30)
            except asyncio.TimeoutError:
                await ctx.send(_("You took too long answering."))
                return

            if pred.result is False:
                await ctx.send(_("I am not replacing the existing file."))
                return

        buffer = io.BytesIO(await attachment.read())
        yaml.safe_load(buffer)
        buffer.seek(0)

        with file.open("wb") as fp:
            fp.write(buffer.read())
        await ctx.send(
            _("Saved Trivia list as {filename}.").format(filename=filename))
示例#28
0
 async def open_chest(ctx, user, type):
     if hasattr(user, "display_name"):
         await ctx.send(
             "{} is opening a treasure chest. What riches lay inside?".
             format(user.display_name))
     else:
         await ctx.send(
             "{} is foraging for treasure. What will it find?".format(
                 user[:1].upper() + user[1:]))
         await asyncio.sleep(2)
     roll = random.randint(1, 100)
     if type == "pet":
         if roll <= 5:
             chance = Treasure.unique
         elif roll > 5 and roll <= 25:
             chance = Treasure.rare
         elif roll > 25 and roll <= 75:
             chance = Treasure.common
         else:
             await ctx.send(
                 "{} found nothing of value.".format(user[:1].upper() +
                                                     user[1:]))
             return None
     if type == "normal":
         if roll <= 5:
             chance = Treasure.unique
         elif roll > 5 and roll <= 25:
             chance = Treasure.rare
         else:
             chance = Treasure.common
     if type == "rare":
         if roll <= 15:
             chance = Treasure.unique
         elif roll > 15 and roll <= 45:
             chance = Treasure.rare
         else:
             chance = Treasure.common
     if type == "epic":
         if roll <= 25:
             chance = Treasure.unique
         else:
             chance = Treasure.rare
     itemname = random.choice(list(chance.keys()))
     item = chance[itemname]
     if len(item["slot"]
            ) == 2:  # two handed weapons add their bonuses twice
         hand = "two handed"
         att = item["att"] * 2
         cha = item["cha"] * 2
     else:
         if item["slot"][0] == "right" or item["slot"][0] == "left":
             hand = item["slot"][0] + " handed"
         else:
             hand = item["slot"][0] + " slot"
         att = item["att"]
         cha = item["cha"]
     if hasattr(user, "display_name"):
         await ctx.send(
             "{} found a {}. (Attack: {}, Charisma: {} [{}])".format(
                 user.display_name, itemname, str(att), str(cha), hand))
     else:
         await ctx.send(
             "Your {} found a {}. (Attack: {}, Charisma: {} [{}])".format(
                 user, itemname, str(att), str(cha), hand))
     msg = await ctx.send(
         "Do you want to equip, put in backpack or sell this item?")
     start_adding_reactions(msg, Treasure.controls.keys())
     if hasattr(user, "id"):
         pred = ReactionPredicate.with_emojis(
             tuple(Treasure.controls.keys()), msg, user)
     else:
         pred = ReactionPredicate.with_emojis(
             tuple(Treasure.controls.keys()), msg, ctx.author)
     react, user = await ctx.bot.wait_for("reaction_add", check=pred)
     try:
         await msg.clear_reactions()
     except discord.Forbidden:  # cannot remove all reactions
         for key in Treasure.controls.keys():
             await msg.remove_reaction(key, ctx.bot.user)
     return {
         "itemname": itemname,
         "item": item,
         "equip": Treasure.controls[react.emoji]
     }
示例#29
0
    async def get_playlist_match(
        self,
        context: commands.Context,
        matches: MutableMapping,
        scope: str,
        author: discord.User,
        guild: discord.Guild,
        specified_user: bool = False,
    ) -> Tuple[Optional[Playlist], str, str]:
        """
        Parameters
        ----------
        context: commands.Context
            The context in which this is being called.
        matches: dict
            A dict of the matches found where key is scope and value is matches.
        scope:str
            The custom config scope. A value from :code:`PlaylistScope`.
        author: discord.User
            The user.
        guild: discord.Guild
            The guild.
        specified_user: bool
            Whether or not a user ID was specified via argparse.
        Returns
        -------
        Tuple[Optional[Playlist], str, str]
            Tuple of Playlist or None if none found, original user input and scope.
        Raises
        ------
        `TooManyMatches`
            When more than 10 matches are found or
            When multiple matches are found but none is selected.

        """
        correct_scope_matches: List[Playlist]
        original_input = matches.get("arg")
        lazy_match = False
        if scope is None:
            correct_scope_matches_temp: MutableMapping = matches.get("all")
            lazy_match = True
        else:
            correct_scope_matches_temp: MutableMapping = matches.get(scope)
        guild_to_query = guild.id
        user_to_query = author.id
        correct_scope_matches_user = []
        correct_scope_matches_guild = []
        correct_scope_matches_global = []
        if not correct_scope_matches_temp:
            return None, original_input, scope or PlaylistScope.GUILD.value
        if lazy_match or (scope == PlaylistScope.USER.value):
            correct_scope_matches_user = [
                p for p in matches.get(PlaylistScope.USER.value)
                if user_to_query == p.scope_id
            ]
        if lazy_match or (scope == PlaylistScope.GUILD.value
                          and not correct_scope_matches_user):
            if specified_user:
                correct_scope_matches_guild = [
                    p for p in matches.get(PlaylistScope.GUILD.value) if
                    guild_to_query == p.scope_id and p.author == user_to_query
                ]
            else:
                correct_scope_matches_guild = [
                    p for p in matches.get(PlaylistScope.GUILD.value)
                    if guild_to_query == p.scope_id
                ]
        if lazy_match or (scope == PlaylistScope.GLOBAL.value
                          and not correct_scope_matches_user
                          and not correct_scope_matches_guild):
            if specified_user:
                correct_scope_matches_global = [
                    p for p in matches.get(PlaylistScope.GLOBAL.value)
                    if p.author == user_to_query
                ]
            else:
                correct_scope_matches_global = [
                    p for p in matches.get(PlaylistScope.GLOBAL.value)
                ]

        correct_scope_matches = [
            *correct_scope_matches_global,
            *correct_scope_matches_guild,
            *correct_scope_matches_user,
        ]
        match_count = len(correct_scope_matches)
        if match_count > 1:
            correct_scope_matches2 = [
                p for p in correct_scope_matches
                if p.name == str(original_input).strip()
            ]
            if correct_scope_matches2:
                correct_scope_matches = correct_scope_matches2
            elif original_input.isnumeric():
                arg = int(original_input)
                correct_scope_matches3 = [
                    p for p in correct_scope_matches if p.id == arg
                ]
                if correct_scope_matches3:
                    correct_scope_matches = correct_scope_matches3
        match_count = len(correct_scope_matches)
        # We done all the trimming we can with the info available time to ask the user
        if match_count > 10:
            if original_input.isnumeric():
                arg = int(original_input)
                correct_scope_matches = [
                    p for p in correct_scope_matches if p.id == arg
                ]
            if match_count > 10:
                raise TooManyMatches(
                    _("{match_count} playlists match {original_input}: "
                      "Please try to be more specific, or use the playlist ID."
                      ).format(match_count=match_count,
                               original_input=original_input))
        elif match_count == 1:
            return correct_scope_matches[
                0], original_input, correct_scope_matches[0].scope
        elif match_count == 0:
            return None, original_input, scope or PlaylistScope.GUILD.value

        # TODO : Convert this section to a new paged reaction menu when Toby Menus are Merged
        pos_len = 3
        playlists = f"{'#':{pos_len}}\n"
        number = 0
        correct_scope_matches = sorted(correct_scope_matches,
                                       key=lambda x: x.name.lower())
        async for number, playlist in AsyncIter(
                correct_scope_matches).enumerate(start=1):
            author = self.bot.get_user(
                playlist.author) or playlist.author or _("Unknown")
            line = _("{number}."
                     "    <{playlist.name}>\n"
                     " - Scope:  < {scope} >\n"
                     " - ID:     < {playlist.id} >\n"
                     " - Tracks: < {tracks} >\n"
                     " - Author: < {author} >\n\n").format(
                         number=number,
                         playlist=playlist,
                         scope=self.humanize_scope(playlist.scope),
                         tracks=len(playlist.tracks),
                         author=author,
                     )
            playlists += line

        embed = discord.Embed(
            title=_("{playlists} playlists found, which one would you like?").
            format(playlists=number),
            description=box(playlists, lang="md"),
            colour=await context.embed_colour(),
        )
        msg = await context.send(embed=embed)
        avaliable_emojis = ReactionPredicate.NUMBER_EMOJIS[1:]
        avaliable_emojis.append("🔟")
        emojis = avaliable_emojis[:len(correct_scope_matches)]
        emojis.append("\N{CROSS MARK}")
        start_adding_reactions(msg, emojis)
        pred = ReactionPredicate.with_emojis(emojis, msg, user=context.author)
        try:
            await context.bot.wait_for("reaction_add", check=pred, timeout=60)
        except asyncio.TimeoutError:
            with contextlib.suppress(discord.HTTPException):
                await msg.delete()
            raise TooManyMatches(
                _("Too many matches found and you did not select which one you wanted."
                  ))
        if emojis[pred.result] == "\N{CROSS MARK}":
            with contextlib.suppress(discord.HTTPException):
                await msg.delete()
            raise TooManyMatches(
                _("Too many matches found and you did not select which one you wanted."
                  ))
        with contextlib.suppress(discord.HTTPException):
            await msg.delete()
        return (
            correct_scope_matches[pred.result],
            original_input,
            correct_scope_matches[pred.result].scope,
        )
示例#30
0
    async def message(self, ctx: commands.Context, *, message: str):
        """
        Set your custom ping message.

        Optional Regex:
        `{author}`: Replaces with the authors display name.
        `{latency}`: Replaces with the bots latency.

        Example Usage:
        `[p]pingset message Hello {author}! My latency is {latency} ms.`

        Random Responses:
        When you specify `<message>`, you will be asked if you want to add
        more responses. These responses will be chosen at random when you run the
        ping command.

        To exit out of the random selection session, type `stop()` or `exit()`.
        """

        msg = await ctx.send(
            "Would you like to add any other responses, to be chosen at random?"
        )
        pred = ReactionPredicate.yes_or_no(msg, ctx.author)
        start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)

        try:
            await self.bot.wait_for("reaction_add", check=pred, timeout=30)
        except asyncio.TimeoutError:
            await self.config.response.clear()
            response = await self.config.response()
            response.append(message)
            await self.config.response.set(response)
            return await ctx.send(
                "You took too long to answer, I'll stick to this one response!"
            )

        if pred.result:
            await ctx.send(
                "Okay, let's add some random responses. Type `stop()` or `exit()` once you're done!"
            )
            await asyncio.sleep(1)
            await self.config.response.clear()

            message_list = [message]
            response = await self.config.response()

            while True:

                if len(message_list) > 9:
                    await ctx.send(
                        "You've reached the maximum number of responses!")
                    return await self.enum(ctx, message_list)

                await ctx.send("Add a random response:")

                def check(x):
                    return x.author == ctx.author and x.channel == ctx.channel

                try:
                    add_response = await self.bot.wait_for("message",
                                                           timeout=50,
                                                           check=check)
                except asyncio.TimeoutError:
                    return await ctx.send(
                        "Timed out. No changes have been made.")

                if add_response.content.lower().startswith(
                    ("exit()", "stop()")):
                    await ctx.send("Ended!")
                    return await self.enum(ctx, message_list)
                else:
                    message_list.append(add_response.content)

        else:
            await self.config.response.clear()
            response = await self.config.response()
            response.append(message)
            await self.config.response.set(response)
            return await ctx.send("Ok, I'll stick to this one response!")
示例#31
0
    async def quarall(self, ctx, quarType: int = 1, *, userSearchText: str):
        """Search for all usernames (not nicknames) that match a string and quarantine them
        
        Types:
        1 - Normal quarantine
        2 - Kick the users
        3 - Ban the users
        """
        def quarTypeText(quarTypeInt: int):
            if quarTypeInt == 1:
                return "muterole"
            elif quarTypeInt == 2:
                return "kick"
            elif quartypeInt == 3:
                return "ban"
            else:
                return None

        try:
            userSearchRegex = re.compile(userSearchText, re.I)
        except re.error:
            return await ctx.send(
                "Invalid search string. Format your search using Regex here and try again: https://pythex.org/"
            )
        else:
            # Find the role in server
            muteroledata = await self.config.guild(ctx.guild).muterole()
            if muteroledata == "":
                return await ctx.send(
                    "Be sure to set the muterole first using `setquar` :')")
            muterole = ctx.guild.get_role(muteroledata)

            # Regex search display names and usernames
            # Only add users who aren't muted already
            memberObj = ctx.guild.members
            matches = []
            alreadyHave = 0
            for memObj in memberObj:
                searchText = str(memObj.display_name) + " " + str(memObj.name)
                if re.search(userSearchRegex, searchText):
                    if muterole not in memObj.roles:
                        matches.append(memObj)
                    else:
                        alreadyHave += 1

            # Return results in an embed asking if confirm
            desc = "Are you sure you want to {} the following users?\n\n".format(
                quarTypeText(quarType))
            userlist = ""
            for user in matches:
                userlist += user.mention + " " + str(user.id) + "\n"
            desc += userlist
            e = discord.Embed(color=(await ctx.embed_colour()),
                              title="Quarantine Search Results",
                              description=desc)
            if alreadyHave > 0:
                e.set_footer(text=str(alreadyHave) +
                             " user(s) were already quarantined and skipped.")
            confirmEmbed = await ctx.send(embed=e)

            # Wait for confirm
            start_adding_reactions(confirmEmbed,
                                   ReactionPredicate.YES_OR_NO_EMOJIS)
            pred = ReactionPredicate.yes_or_no(confirmEmbed, ctx.author)
            try:
                await ctx.bot.wait_for("reaction_add", check=pred, timeout=60)
            except asyncio.TimeoutError:
                return await ctx.send("Selection timed out.")

            if pred.result is True:
                # User responded with tick
                await confirmEmbed.add_reaction("⏳")
                print("muterole: " + str(muterole))
                try:
                    if quarType == 1:
                        for quarUser in matches:
                            print("Quarantine type 1/muterole against " +
                                  str(quarUser.display_name))
                            await quarUser.edit(roles=[muterole])
                    elif quarType == 2:
                        for quarUser in matches:
                            print("Quarantine type 2/kick against " +
                                  str(quarUser.display_name))
                            await ctx.guild.kick(quarUser)
                    elif quarType == 3:
                        for quarUser in matches:
                            print("Quarantine type 3/ban against " +
                                  str(quarUser.display_name))
                            await ctx.guild.ban(quarUser)
                except:
                    return await ctx.send(
                        "Please confirm that I have permissions to manage members + manage roles...."
                    )
                else:
                    await confirmEmbed.add_reaction("💯")

                # Send report to channel
                destinationdata = await self.config.guild(ctx.guild).report()
                if destinationdata == "":
                    return
                else:
                    e2 = discord.Embed(color=(await ctx.embed_colour()),
                                       title="Quarantined: " +
                                       quarTypeText(quarType),
                                       description=userlist)
                    e2.set_footer(text="Sent in #{}".format(ctx.channel))
                    destination = ctx.guild.get_channel(destinationdata)
                    await destination.send(embed=e2)
            else:
                # User responded with cross
                return await ctx.send("Exited quarantine")
示例#32
0
    async def create_poll(self, ctx, *, args: str):
        """Créer un sondage dynamique avec réactions
        
        **Format :** `poll Question ?;Réponse 1;Réponse 2;Réponse N...`
        
        __Options__
        `-exp X` = Modifier la durée (en minutes) après lequel le sondage expire (par def. 10m)
        `-image URL` = Ajouter une image au sondage
        `-pin` = Epingler/désépingler auto. le sondage
        `-nostats` = Désactiver les statistiques en direct (elles s'afficheront quand même à la fin)
        `-anonymous` = Ne pas afficher le créateur du sondage"""
        author, channel = ctx.author, ctx.channel
        letters = [u for u in '🇦🇧🇨🇩🇪🇫🇬🇭🇮']

        exp = 10
        anonyme = False
        pin = False
        dispstats = True
        sus = False

        emcolor = discord.Color.random()
        em = discord.Embed(color=emcolor)

        opts = re.compile(r'-(\w*)(?:\s?([\w:\/\.?=&\-]*))?',
                          re.DOTALL | re.IGNORECASE).findall(args)
        if opts:
            args = args.split('-')[0]
            for opt, val in opts:
                if opt.lower() == 'image':
                    em.set_image(url=val)
                elif opt.lower() == 'exp':
                    try:
                        exp = int(val)
                    except Exception:
                        pass

                    if exp < 1 or exp > 720:
                        return ctx.reply(
                            "**Temps invalide** › Le sondage ne peut durer qu'entre 1 et 720m (12h)",
                            mention_author=False)
                    else:
                        pass

                elif opt.lower() in ('anonymous', 'anonyme'):
                    anonyme = True
                elif opt.lower() == 'pin':
                    pin = True
                elif opt.lower() in ('nostats', 'nostat'):
                    dispstats = False
                elif opt.lower() == 'sus':
                    sus = True

        q, *r = [i.strip() for i in re.split(';|-', args)]
        if not r:
            r = ('Pour/Oui', 'Contre/Non')
            emojis = ['👍', '👎']
        elif len(r) <= 9:
            emojis = letters[:len(r)]
        else:
            return await ctx.reply(
                "**Trop de réponses possibles** › Vous ne pouvez mettre que 9 réponses possibles au maximum.",
                mention_author=False)

        polls = await self.config.channel(channel).Polls()
        if polls:
            poll_id = max([polls[n]['id'] for n in polls]) + 1
        else:
            poll_id = 1

        reps = {i: emojis[r.index(i)] for i in r}
        stats = {i: [] for i in r}
        em.timestamp = datetime.utcnow() + timedelta(minutes=exp)
        em.title = f'`#{poll_id}` · ***{q}***'
        em.description = "\n".join([
            f'{reps[p]} › **{p}** (0%)' for p in reps
        ]) if dispstats else "\n".join([f'{reps[p]} › **{p}**' for p in reps])

        if not anonyme:
            em.set_footer(text=author.name, icon_url=author.avatar_url)

        if sus:  # Easter-egg AMONGUSSSSS
            em.set_footer(
                text='Imposteur',
                icon_url='https://cdn2.clc2l.fr/t/A/m/Among-Us-oAEaxX.png')

        poll_data = {
            'embed': em.to_dict(),
            'reps': reps,
            'stats': stats,
            'id': poll_id,
            'exp': time.time() + (60 * exp),
            'disp_stats': dispstats,
            'pin': pin
        }

        msg = await ctx.send(embed=em)

        await self.config.channel(channel).Polls.set_raw(msg.id,
                                                         value=poll_data)
        start_adding_reactions(msg, emojis)

        if pin:
            try:
                await msg.pin()
            except Exception:
                await ctx.send(
                    "Impossible d'épingler auto. › Je n'ai pas les permissions nécessaires (`Gestion des messages`)"
                )