コード例 #1
0
ファイル: playlists.py プロジェクト: plofts/Red-DiscordBot
 async def _get_bundled_playlist_tracks(self):
     async with aiohttp.ClientSession(json_serialize=json.dumps) as session:
         async with session.get(
                 CURRATED_DATA + f"?timestamp={int(time.time())}",
                 headers={"content-type": "application/json"},
         ) as response:
             if response.status != 200:
                 return 0, []
             try:
                 data = json.loads(await response.read())
             except Exception:
                 log.exception(
                     "Curated playlist couldn't be parsed, report this error."
                 )
                 data = {}
             web_version = data.get("version", 0)
             entries = data.get("entries", [])
             if entries:
                 random.shuffle(entries)
     tracks = []
     async for entry in AsyncIter(entries, steps=25):
         with contextlib.suppress(Exception):
             tracks.append(self.decode_track(entry))
     return web_version, tracks
コード例 #2
0
ファイル: queue.py プロジェクト: xBlynd/drapercogs
    async def command_queue_search(self, ctx: commands.Context, *,
                                   search_words: str):
        """Search the queue."""
        try:
            player = lavalink.get_player(ctx.guild.id)
        except KeyError:
            return await self.send_embed_msg(
                ctx, title=_("There's nothing in the queue."))
        if not self._player_check(ctx) or not player.queue:
            return await self.send_embed_msg(
                ctx, title=_("There's nothing in the queue."))

        search_list = await self._build_queue_search_list(
            player.queue, search_words)
        if not search_list:
            return await self.send_embed_msg(ctx, title=_("No matches."))

        len_search_pages = math.ceil(len(search_list) / 10)
        search_page_list = []
        async for page_num in AsyncIter(range(1, len_search_pages + 1)):
            embed = await self._build_queue_search_page(
                ctx, page_num, search_list)
            search_page_list.append(embed)
        await menu(ctx, search_page_list, DEFAULT_CONTROLS)
コード例 #3
0
    async def guildChannelsDenyList(self, ctx: Context):
        """List the channels in the denylist."""
        dlChannels = await self.config.guild(ctx.guild
                                             ).get_attr(KEY_CHANNEL_DENYLIST)()
        channelMentions: List[str] = []

        if dlChannels:
            channelMentions = [
                channelObject.mention for channelObject in list(
                    map(
                        lambda chId: discord.utils.get(ctx.guild.text_channels,
                                                       id=chId),
                        dlChannels,
                    )) if channelObject
            ]

        if channelMentions:
            pageList = []
            msg = "\n".join(channelMentions)
            pages = list(chat_formatting.pagify(msg, page_length=300))
            totalPages = len(pages)
            totalEntries = len(dlChannels)
            async for pageNumber, page in AsyncIter(pages).enumerate(start=1):
                embed = discord.Embed(
                    title=f"Denylist channels for **{ctx.guild.name}**",
                    description=page)
                embed.set_footer(
                    text=
                    f"Page {pageNumber}/{totalPages} ({totalEntries} entries)")
                embed.colour = discord.Colour.red()
                pageList.append(embed)
            await menu(ctx, pageList, DEFAULT_CONTROLS)
        else:
            await ctx.send(
                f"There are no channels on the denylist for **{ctx.guild.name}**!"
            )
コード例 #4
0
ファイル: local_tracks.py プロジェクト: PredaaA/drapercogs
    async def get_localtrack_folder_tracks(
            self, ctx, player: lavalink.player_manager.Player,
            query: Query) -> List[lavalink.rest_api.Track]:
        """Return a list of tracks per the provided query."""
        if not await self.localtracks_folder_exists(
                ctx) or self.api_interface is None:
            return []

        audio_data = LocalPath(None, self.local_folder_current_path)
        try:
            if query.local_track_path is not None:
                query.local_track_path.path.relative_to(audio_data.to_string())
            else:
                return []
        except ValueError:
            return []
        local_tracks = []
        async for local_file in AsyncIter(
                await self.get_all_localtrack_folder_tracks(ctx, query)):
            with contextlib.suppress(IndexError, TrackEnqueueError):
                trackdata, called_api = await self.api_interface.fetch_track(
                    ctx, player, local_file)
                local_tracks.append(trackdata.tracks[0])
        return local_tracks
コード例 #5
0
    async def post_time_to_game_start(self, bot, time_left):
        """
        Post when there is 60, 30, and 10 minutes until the game starts in all channels
        """
        post_state = ["all", self.home_team, self.away_team]
        msg = _("{time} minutes until {away_emoji} {away} @ {home_emoji} {home} starts!").format(
            time=time_left,
            away_emoji=self.away_emoji,
            away=self.away_team,
            home_emoji=self.home_emoji,
            home=self.home_team,
        )
        tasks = []
        all_channels = await bot.get_cog("Hockey").config.all_channels()
        async for channel_id, data in AsyncIter(all_channels.items(), steps=100):
            channel = await get_channel_obj(bot, channel_id, data)
            if not channel:
                continue

            should_post = await check_to_post(bot, channel, data, post_state, self.game_state)
            team_to_post = await bot.get_cog("Hockey").config.channel(channel).team()
            if should_post and "all" not in team_to_post:
                tasks.append(self.post_game_start(channel, msg))
        await bounded_gather(*tasks)
コード例 #6
0
    async def post_game_state(self, bot):
        """
        When a game state has changed this is called to create the embed
        and post in all channels
        """
        post_state = ["all", self.home_team, self.away_team]
        state_embed = await self.game_state_embed()
        state_text = await self.game_state_text()
        tasks = []
        all_channels = await bot.get_cog("Hockey").config.all_channels()
        async for channel_id, data in AsyncIter(all_channels.items(), steps=100):
            channel = await get_channel_obj(bot, channel_id, data)
            if not channel:
                continue

            should_post = await check_to_post(bot, channel, data, post_state, self.game_state)
            if should_post:
                tasks.append(self.actually_post_state(bot, channel, state_embed, state_text))
        previews = await bounded_gather(*tasks)
        for preview in previews:
            if preview is None:
                continue
            else:
                bot.dispatch("hockey_preview_message", preview[0], preview[1], self)
コード例 #7
0
    async def fetch_all_converter(
        self, scope: str, playlist_name, playlist_id
    ) -> List[PlaylistFetchResult]:
        """Fetch all playlists with the specified filter"""
        scope_type = self.get_scope_type(scope)
        try:
            playlist_id = int(playlist_id)
        except Exception as exc:
            debug_exc_log(log, exc, "Failed converting playlist_id to int")
            playlist_id = -1

        output = []
        with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
            for future in concurrent.futures.as_completed(
                [
                    executor.submit(
                        self.database.cursor().execute,
                        self.statement.get_all_converter,
                        (
                            {
                                "scope_type": scope_type,
                                "playlist_name": playlist_name,
                                "playlist_id": playlist_id,
                            }
                        ),
                    )
                ]
            ):
                try:
                    row_result = future.result()
                except Exception as exc:
                    debug_exc_log(log, exc, "Failed to completed fetch from database")

            async for row in AsyncIter(row_result):
                output.append(PlaylistFetchResult(*row))
        return output
コード例 #8
0
async def get_leaderboard(cog, guild) -> List[tuple]:
    raw_accounts = await cog.config.all_users()
    if guild is not None:
        tmp = raw_accounts.copy()
        for acc in tmp:
            if not guild.get_member(acc):
                del raw_accounts[acc]
    a = []
    async for (k, v) in AsyncIter(raw_accounts.items(), steps=100):
        lsum, rsu, usum, csum, gsum = (
            sum(v["legendary"].values()),
            sum(v["rare"].values()),
            sum(v["uncommon"].values()),
            sum(v["common"].values()),
            sum(v["garbage"].values()),
        )
        _sum = lsum + rsu + usum + csum + gsum
        a.append((k, {"fishes": _sum, "legendaries": lsum}))
    sorted_acc = sorted(
        a,
        key=lambda x: x[1]["fishes"],
        reverse=True,
    )
    return sorted_acc
コード例 #9
0
ファイル: playlists.py プロジェクト: PredaaA/drapercogs
    async def fetch_playlist_tracks(
        self,
        ctx: commands.Context,
        player: lavalink.player_manager.Player,
        query: Query,
        skip_cache: bool = False,
    ) -> Union[discord.Message, None, List[MutableMapping]]:
        search = query.is_search
        tracklist = []

        if query.is_spotify:
            try:
                if self.play_lock[ctx.message.guild.id]:
                    return await self.send_embed_msg(
                        ctx,
                        title=_("Unable To Get Tracks"),
                        description=_(
                            "Wait until the playlist has finished loading."),
                    )
            except KeyError:
                pass
            tracks = await self._get_spotify_tracks(ctx,
                                                    query,
                                                    forced=skip_cache)

            if isinstance(tracks, discord.Message):
                return None

            if not tracks:
                embed = discord.Embed(title=_("Nothing found."))
                if query.is_local and query.suffix in _PARTIALLY_SUPPORTED_MUSIC_EXT:
                    embed = discord.Embed(title=_("Track is not playable."))
                    embed.description = _(
                        "**{suffix}** is not a fully supported format and some "
                        "tracks may not play.").format(suffix=query.suffix)
                return await self.send_embed_msg(ctx, embed=embed)
            async for track in AsyncIter(tracks):
                track_obj = self.get_track_json(player, other_track=track)
                tracklist.append(track_obj)
            self.update_player_lock(ctx, False)
        elif query.is_search:
            try:
                result, called_api = await self.api_interface.fetch_track(
                    ctx, player, query, forced=skip_cache)
            except TrackEnqueueError:
                self.update_player_lock(ctx, False)
                return await self.send_embed_msg(
                    ctx,
                    title=_("Unable to Get Track"),
                    description=_(
                        "I'm unable to get a track from Lavalink at the moment, try again in a few "
                        "minutes."),
                )
            except Exception as e:
                self.update_player_lock(ctx, False)
                raise e

            tracks = result.tracks
            if not tracks:
                embed = discord.Embed(title=_("Nothing found."))
                if query.is_local and query.suffix in _PARTIALLY_SUPPORTED_MUSIC_EXT:
                    embed = discord.Embed(title=_("Track is not playable."))
                    embed.description = _(
                        "**{suffix}** is not a fully supported format and some "
                        "tracks may not play.").format(suffix=query.suffix)
                return await self.send_embed_msg(ctx, embed=embed)
        else:
            try:
                result, called_api = await self.api_interface.fetch_track(
                    ctx, player, query, forced=skip_cache)
            except TrackEnqueueError:
                self.update_player_lock(ctx, False)
                return await self.send_embed_msg(
                    ctx,
                    title=_("Unable to Get Track"),
                    description=_(
                        "I'm unable to get a track from Lavalink at the moment, try again in a few "
                        "minutes."),
                )
            except Exception as e:
                self.update_player_lock(ctx, False)
                raise e

            tracks = result.tracks

        if not search and len(tracklist) == 0:
            async for track in AsyncIter(tracks):
                track_obj = self.get_track_json(player, other_track=track)
                tracklist.append(track_obj)
        elif len(tracklist) == 0:
            track_obj = self.get_track_json(player, other_track=tracks[0])
            tracklist.append(track_obj)
        return tracklist
コード例 #10
0
ファイル: playlists.py プロジェクト: PredaaA/drapercogs
    async def _load_v3_playlist(
        self,
        ctx: commands.Context,
        scope: str,
        uploaded_playlist_name: str,
        uploaded_playlist_url: str,
        track_list: List,
        author: Union[discord.User, discord.Member],
        guild: Union[discord.Guild],
    ) -> None:
        embed1 = discord.Embed(title=_("Please wait, adding tracks..."))
        playlist_msg = await self.send_embed_msg(ctx, embed=embed1)
        track_count = len(track_list)
        uploaded_track_count = len(track_list)
        await asyncio.sleep(1)
        embed2 = discord.Embed(
            colour=await ctx.embed_colour(),
            title=_("Loading track {num}/{total}...").format(
                num=track_count, total=uploaded_track_count),
        )
        await playlist_msg.edit(embed=embed2)

        playlist = await create_playlist(
            ctx,
            self.playlist_api,
            scope,
            uploaded_playlist_name,
            uploaded_playlist_url,
            track_list,
            author,
            guild,
        )
        scope_name = self.humanize_scope(
            scope, ctx=guild if scope == PlaylistScope.GUILD.value else author)
        if not track_count:
            msg = _("Empty playlist {name} (`{id}`) [**{scope}**] created."
                    ).format(name=playlist.name,
                             id=playlist.id,
                             scope=scope_name)
        elif uploaded_track_count != track_count:
            bad_tracks = uploaded_track_count - track_count
            msg = _(
                "Added {num} tracks from the {playlist_name} playlist. {num_bad} track(s) "
                "could not be loaded.").format(num=track_count,
                                               playlist_name=playlist.name,
                                               num_bad=bad_tracks)
        else:
            msg = _("Added {num} tracks from the {playlist_name} playlist."
                    ).format(num=track_count, playlist_name=playlist.name)
        embed3 = discord.Embed(colour=await ctx.embed_colour(),
                               title=_("Playlist Saved"),
                               description=msg)
        await playlist_msg.edit(embed=embed3)
        database_entries = []
        time_now = int(
            datetime.datetime.now(datetime.timezone.utc).timestamp())
        async for t in AsyncIter(track_list):
            uri = t.get("info", {}).get("uri")
            if uri:
                t = {"loadType": "V2_COMPAT", "tracks": [t], "query": uri}
                data = json.dumps(t)
                if all(k in data for k in
                       ["loadType", "playlistInfo", "isSeekable", "isStream"]):
                    database_entries.append({
                        "query": uri,
                        "data": data,
                        "last_updated": time_now,
                        "last_fetched": time_now,
                    })
        if database_entries:
            await self.api_interface.local_cache_api.lavalink.insert(
                database_entries)
コード例 #11
0
    async def player_automated_timer(self) -> None:
        stop_times: Dict = {}
        pause_times: Dict = {}
        while True:
            async for p in AsyncIter(lavalink.all_players()):
                server = p.channel.guild
                if await self.bot.cog_disabled_in_guild(self, server):
                    continue

                if [self.bot.user] == p.channel.members:
                    stop_times.setdefault(server.id, time.time())
                    pause_times.setdefault(server.id, time.time())
                else:
                    stop_times.pop(server.id, None)
                    if p.paused and server.id in pause_times:
                        try:
                            await p.pause(False)
                        except Exception as err:
                            debug_exc_log(
                                log,
                                err,
                                f"Exception raised in Audio's unpausing player for {server.id}.",
                            )
                    pause_times.pop(server.id, None)
            servers = stop_times.copy()
            servers.update(pause_times)
            async for sid in AsyncIter(servers, steps=5):
                server_obj = self.bot.get_guild(sid)
                if sid in stop_times and await self.config.guild(
                        server_obj).emptydc_enabled():
                    emptydc_timer = await self.config.guild(server_obj
                                                            ).emptydc_timer()
                    if (time.time() - stop_times[sid]) >= emptydc_timer:
                        stop_times.pop(sid)
                        try:
                            player = lavalink.get_player(sid)
                            await self.api_interface.persistent_queue_api.drop(
                                sid)
                            await player.stop()
                            await player.disconnect()
                        except Exception as err:
                            if "No such player for that guild" in str(err):
                                stop_times.pop(sid, None)
                            debug_exc_log(
                                log, err,
                                f"Exception raised in Audio's emptydc_timer for {sid}."
                            )
                elif (sid in pause_times and await
                      self.config.guild(server_obj).emptypause_enabled()):
                    emptypause_timer = await self.config.guild(
                        server_obj).emptypause_timer()
                    if (time.time() -
                            pause_times.get(sid, 0)) >= emptypause_timer:
                        try:
                            await lavalink.get_player(sid).pause()
                        except Exception as err:
                            if "No such player for that guild" in str(err):
                                pause_times.pop(sid, None)
                            debug_exc_log(
                                log, err,
                                f"Exception raised in Audio's pausing for {sid}."
                            )
            await asyncio.sleep(5)
コード例 #12
0
    async def post_automatic_standings(bot):
        """
        Automatically update a standings embed with the latest stats
        run when new games for the day is updated
        """
        log.debug("Updating Standings.")
        config = bot.get_cog("Hockey").config
        async with bot.get_cog("Hockey").session.get(
                BASE_URL + "/api/v1/standings") as resp:
            standings_data = await resp.json()

        all_guilds = await config.all_guilds()
        async for guild_id, data in AsyncIter(all_guilds.items(), steps=100):
            guild = bot.get_guild(guild_id)
            if guild is None:
                continue
            log.debug(guild.name)
            if data["post_standings"]:

                search = data["standings_type"]
                if search is None:
                    continue
                standings_channel = data["standings_channel"]
                if standings_channel is None:
                    continue
                channel = guild.get_channel(standings_channel)
                if channel is None:
                    continue
                standings_msg = data["standings_msg"]
                if standings_msg is None:
                    continue
                try:
                    if version_info >= VersionInfo.from_str("3.4.6"):
                        message = channel.get_partial_message(standings_msg)
                    else:
                        message = await channel.fetch_message(standings_msg)
                except (discord.errors.NotFound, discord.errors.Forbidden):
                    await config.guild(guild).post_standings.clear()
                    await config.guild(guild).standings_type.clear()
                    await config.guild(guild).standings_channel.clear()
                    await config.guild(guild).standings_msg.clear()
                    continue

                standings, page = await Standings.get_team_standings_from_data(
                    search, standings_data)
                team_stats = standings[page]

                if search in DIVISIONS:
                    em = await Standings.make_division_standings_embed(
                        team_stats)

                elif search in CONFERENCES:
                    em = await Standings.make_conference_standings_embed(
                        team_stats)
                else:
                    em = await Standings.all_standing_embed(standings)
                if message is not None:
                    try:
                        await message.edit(embed=em)
                    except (discord.errors.NotFound, discord.errors.Forbidden):
                        await config.guild(guild).post_standings.clear()
                        await config.guild(guild).standings_type.clear()
                        await config.guild(guild).standings_channel.clear()
                        await config.guild(guild).standings_msg.clear()
                    except Exception:
                        log.exception(
                            f"Error editing standings message in {guild.id}")
コード例 #13
0
ファイル: miscellaneous.py プロジェクト: jeavinci/Lilith
    async def data_schema_migration(self, from_version: int, to_version: int) -> None:
        database_entries = []
        time_now = int(datetime.datetime.now(datetime.timezone.utc).timestamp())
        if from_version == to_version:
            return
        if from_version < 2 <= to_version:
            all_guild_data = await self.config.all_guilds()
            all_playlist = {}
            async for guild_id, guild_data in AsyncIter(all_guild_data.items()):
                temp_guild_playlist = guild_data.pop("playlists", None)
                if temp_guild_playlist:
                    guild_playlist = {}
                    async for count, (name, data) in AsyncIter(
                        temp_guild_playlist.items()
                    ).enumerate(start=1000):
                        if not data or not name:
                            continue
                        playlist = {"id": count, "name": name, "guild": int(guild_id)}
                        playlist.update(data)
                        guild_playlist[str(count)] = playlist

                        tracks_in_playlist = data.get("tracks", []) or []
                        async for t in AsyncIter(tracks_in_playlist):
                            uri = t.get("info", {}).get("uri")
                            if uri:
                                t = {"loadType": "V2_COMPAT", "tracks": [t], "query": uri}
                                data = json.dumps(t)
                                if all(
                                    k in data
                                    for k in ["loadType", "playlistInfo", "isSeekable", "isStream"]
                                ):
                                    database_entries.append(
                                        {
                                            "query": uri,
                                            "data": data,
                                            "last_updated": time_now,
                                            "last_fetched": time_now,
                                        }
                                    )
                    if guild_playlist:
                        all_playlist[str(guild_id)] = guild_playlist
            await self.config.custom(PlaylistScope.GUILD.value).set(all_playlist)
            # new schema is now in place
            await self.config.schema_version.set(2)

            # migration done, now let's delete all the old stuff
            async for guild_id in AsyncIter(all_guild_data):
                await self.config.guild(
                    cast(discord.Guild, discord.Object(id=guild_id))
                ).clear_raw("playlists")
        if from_version < 3 <= to_version:
            for scope in PlaylistScope.list():
                scope_playlist = await get_all_playlist_for_migration23(
                    self.bot, self.playlist_api, self.config, scope
                )
                async for p in AsyncIter(scope_playlist):
                    await p.save()
                await self.config.custom(scope).clear()
            await self.config.schema_version.set(3)

        if database_entries:
            await self.api_interface.local_cache_api.lavalink.insert(database_entries)
コード例 #14
0
ファイル: commands.py プロジェクト: Vexed01/Vex-Cogs
    async def upcoming(self, ctx: commands.Context, days: int = 7):
        """View upcoming birthdays.

        **Examples:**
            - `[p]birthday upcoming` - default of 7 days
            - `[p]birthday upcoming 14` - 14 days
        """
        # guild only check in group
        if TYPE_CHECKING:
            assert isinstance(ctx.author, discord.Member)
            assert ctx.guild is not None

        if days < 1 or days > 365:
            await ctx.send(
                "You must enter a number of days greater than 0 and smaller than 365."
            )
            return

        today_dt = datetime.datetime.utcnow().replace(hour=0,
                                                      minute=0,
                                                      second=0,
                                                      microsecond=0)

        all_birthdays: dict[int, dict[str,
                                      dict]] = await self.config.all_members(
                                          ctx.guild)

        parsed_bdays: dict[int, list[str]] = defaultdict(list)
        number_day_mapping: dict[int, str] = {}

        async for member_id, member_data in AsyncIter(all_birthdays.items(),
                                                      steps=50):
            member = ctx.guild.get_member(member_id)
            if not isinstance(member, discord.Member):
                continue

            birthday_dt = datetime.datetime(
                year=member_data["birthday"]["year"] or 1,
                month=member_data["birthday"]["month"],
                day=member_data["birthday"]["day"],
            )

            if today_dt.month == birthday_dt.month and today_dt.day == birthday_dt.day:
                parsed_bdays[0].append(member.mention + (
                    "" if birthday_dt.year ==
                    1 else f" turns {today_dt.year - birthday_dt.year}"))
                number_day_mapping[0] = "Today"
                continue

            this_year_bday = birthday_dt.replace(year=today_dt.year)
            next_year_bday = birthday_dt.replace(year=today_dt.year + 1)
            next_birthday_dt = this_year_bday if this_year_bday > today_dt else next_year_bday

            diff = next_birthday_dt - today_dt
            if diff.days > days:
                continue

            parsed_bdays[diff.days].append(member.mention + (
                "" if birthday_dt.year ==
                1 else f" will turn {today_dt.year - birthday_dt.year}"))
            number_day_mapping[diff.days] = next_birthday_dt.strftime("%B %d")

        if len(parsed_bdays) == 0:
            await ctx.send("No upcoming birthdays.")
            return

        sorted_parsed_bdays = sorted(parsed_bdays.items(), key=lambda x: x[0])

        embed = discord.Embed(title="Upcoming Birthdays",
                              colour=await ctx.embed_colour())

        if len(parsed_bdays) > 25:
            embed.description = "Too many days to display. I've had to stop at 25."

        for day, members in sorted_parsed_bdays:
            embed.add_field(name=number_day_mapping.get(day),
                            value="\n".join(members))

        await ctx.send(embed=embed)
コード例 #15
0
    async def command_queue(self, ctx: commands.Context, *, page: int = 1):
        """List the songs in the queue."""

        # Check to avoid an IndexError further down in the code.
        if page < 1:
            page = 1

        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))
コード例 #16
0
 async def command_remove(self, ctx: commands.Context,
                          index_or_url: Union[int, str]):
     """Remove a specific track number from the queue."""
     dj_enabled = self._dj_status_cache.setdefault(
         ctx.guild.id, await self.config.guild(ctx.guild).dj_enabled())
     if not self._player_check(ctx):
         return await self.send_embed_msg(ctx, title=_("Nothing playing."))
     player = lavalink.get_player(ctx.guild.id)
     can_skip = await self._can_instaskip(ctx, ctx.author)
     if not player.queue:
         return await self.send_embed_msg(ctx, title=_("Nothing queued."))
     if dj_enabled and not can_skip:
         return await self.send_embed_msg(
             ctx,
             title=_("Unable To Modify Queue"),
             description=_("You need the DJ role to remove tracks."),
         )
     if (not ctx.author.voice or
             ctx.author.voice.channel != player.channel) and not can_skip:
         return await self.send_embed_msg(
             ctx,
             title=_("Unable To Modify Queue"),
             description=_(
                 "You must be in the voice channel to manage the queue."),
         )
     player.store("notify_channel", ctx.channel.id)
     if isinstance(index_or_url, int):
         if index_or_url > len(player.queue) or index_or_url < 1:
             return await self.send_embed_msg(
                 ctx,
                 title=_("Unable To Modify Queue"),
                 description=
                 _("Song number must be greater than 1 and within the queue limit."
                   ),
             )
         index_or_url -= 1
         removed = player.queue.pop(index_or_url)
         await self.api_interface.persistent_queue_api.played(
             ctx.guild.id, removed.extras.get("enqueue_time"))
         removed_title = await self.get_track_description(
             removed, self.local_folder_current_path)
         await self.send_embed_msg(
             ctx,
             title=_("Removed track from queue"),
             description=_("Removed {track} from the queue.").format(
                 track=removed_title),
         )
     else:
         clean_tracks = []
         removed_tracks = 0
         async for track in AsyncIter(player.queue):
             if track.uri != index_or_url:
                 clean_tracks.append(track)
             else:
                 await self.api_interface.persistent_queue_api.played(
                     ctx.guild.id, track.extras.get("enqueue_time"))
                 removed_tracks += 1
         player.queue = clean_tracks
         if removed_tracks == 0:
             await self.send_embed_msg(
                 ctx,
                 title=_("Unable To Modify Queue"),
                 description=_(
                     "Removed 0 tracks, nothing matches the URL provided."),
             )
         else:
             await self.send_embed_msg(
                 ctx,
                 title=_("Removed track from queue"),
                 description=_("Removed {removed_tracks} tracks from queue "
                               "which matched the URL provided.").format(
                                   removed_tracks=removed_tracks),
             )
コード例 #17
0
    async def loot(self,
                   ctx: commands.Context,
                   box_type: str = None,
                   number: int = 1):
        """This opens one of your precious treasure chests.

        Use the box rarity type with the command: normal, rare, epic, legendary, ascended or set.
        """
        if (not is_dev(ctx.author) and number > 100) or number < 1:
            return await smart_embed(ctx, _("Nice try :smirk:."))
        if self.in_adventure(ctx):
            return await smart_embed(
                ctx,
                _("You tried to open a loot chest but then realised you left them all back at the inn."
                  ),
            )
        if not await self.allow_in_dm(ctx):
            return await smart_embed(
                ctx, _("This command is not available in DM's on this bot."))
        msgs = []
        async with self.get_lock(ctx.author):
            try:
                c = await Character.from_json(ctx, self.config, ctx.author,
                                              self._daily_bonus)
            except Exception as exc:
                log.exception("Error with the new character sheet",
                              exc_info=exc)
                return
            if not box_type:
                return await ctx.send(
                    box(
                        _("{author} owns {normal} normal, "
                          "{rare} rare, {epic} epic, {leg} legendary, {asc} ascended and {set} set chests."
                          ).format(
                              author=escape(ctx.author.display_name),
                              normal=str(c.treasure[0]),
                              rare=str(c.treasure[1]),
                              epic=str(c.treasure[2]),
                              leg=str(c.treasure[3]),
                              asc=str(c.treasure[4]),
                              set=str(c.treasure[5]),
                          ),
                        lang="css",
                    ))
            if c.is_backpack_full(is_dev=is_dev(ctx.author)):
                await ctx.send(
                    _("**{author}**, your backpack is currently full.").format(
                        author=escape(ctx.author.display_name)))
                return
            if box_type == "normal":
                redux = 0
            elif box_type == "rare":
                redux = 1
            elif box_type == "epic":
                redux = 2
            elif box_type == "legendary":
                redux = 3
            elif box_type == "ascended":
                redux = 4
            elif box_type == "set":
                redux = 5
            else:
                return await smart_embed(
                    ctx,
                    _("There is talk of a {} treasure chest but nobody ever saw one."
                      ).format(box_type),
                )
            treasure = c.treasure[redux]
            if treasure < 1 or treasure < number:
                await smart_embed(
                    ctx,
                    _("**{author}**, you do not have enough {box} treasure chests to open."
                      ).format(author=escape(ctx.author.display_name),
                               box=box_type),
                )
            else:
                if number > 1:
                    async with ctx.typing():
                        # atomically save reduced loot count then lock again when saving inside
                        # open chests
                        c.treasure[redux] -= number
                        await self.config.user(ctx.author).set(await c.to_json(
                            ctx, self.config))
                        items = await self._open_chests(ctx,
                                                        box_type,
                                                        number,
                                                        character=c)
                        msg = _("{}, you've opened the following items:\n\n"
                                ).format(escape(ctx.author.display_name))
                        msg_len = len(msg)
                        table = BeautifulTable(default_alignment=ALIGN_LEFT,
                                               maxwidth=500)
                        table.set_style(BeautifulTable.STYLE_RST)
                        msgs = []
                        total = len(items.values())
                        table.columns.header = [
                            "Name",
                            "Slot",
                            "ATT",
                            "CHA",
                            "INT",
                            "DEX",
                            "LUC",
                            "LVL",
                            "QTY",
                            "DEG",
                            "SET",
                        ]
                        async for index, item in AsyncIter(
                                items.values(), steps=100).enumerate(start=1):
                            if len(str(table)) > 1500:
                                table.rows.sort("LVL", reverse=True)
                                msgs.append(
                                    box(msg + str(table) +
                                        f"\nPage {len(msgs) + 1}",
                                        lang="css"))
                                table = BeautifulTable(
                                    default_alignment=ALIGN_LEFT, maxwidth=500)
                                table.set_style(BeautifulTable.STYLE_RST)
                                table.columns.header = [
                                    "Name",
                                    "Slot",
                                    "ATT",
                                    "CHA",
                                    "INT",
                                    "DEX",
                                    "LUC",
                                    "LVL",
                                    "QTY",
                                    "DEG",
                                    "SET",
                                ]
                            table.rows.append((
                                str(item),
                                item.slot[0]
                                if len(item.slot) == 1 else "two handed",
                                item.att,
                                item.cha,
                                item.int,
                                item.dex,
                                item.luck,
                                f"[{r}]" if
                                (r := c.equip_level(item)) is not None
                                and r > c.lvl else f"{r}",
                                item.owned,
                                f"[{item.degrade}]" if item.rarity
                                in ["legendary", "event", "ascended"]
                                and item.degrade >= 0 else "N/A",
                                item.set or "N/A",
                            ))
                            if index == total:
                                table.rows.sort("LVL", reverse=True)
                                msgs.append(
                                    box(msg + str(table) +
                                        f"\nPage {len(msgs) + 1}",
                                        lang="css"))
                else:
                    # atomically save reduced loot count then lock again when saving inside
                    # open chests
                    c.treasure[redux] -= 1
コード例 #18
0
    async def commands_cbackpack_sell(self, ctx: commands.Context, *,
                                      query: BackpackFilterParser):
        """Sell items from your backpack.

        Forged items cannot be sold using this command.

        Please read the usage instructions [here](https://github.com/aikaterna/gobcog/blob/master/docs/cbackpack.md)
        """

        if self.in_adventure(ctx):
            return await smart_embed(
                ctx,
                _("You tried to go sell your items but the monster ahead is not allowing you to leave."
                  ),
            )
        query.pop("degrade", None)  # Disallow selling by degrade levels
        async with self.get_lock(ctx.author):
            try:
                character = await Character.from_json(ctx, self.config,
                                                      ctx.author,
                                                      self._daily_bonus)
            except Exception as exc:
                log.exception("Error with the new character sheet",
                              exc_info=exc)
                return
            slots = await character.get_argparse_backpack_items(
                query, rarity_exclude=["forged"])
            if (total_items := sum(len(i) for s, i in slots)) > 2:
                msg = await ctx.send(
                    "Are you sure you want to sell {count} items in your inventory that match this query?"
                    .format(count=humanize_number(total_items)))
                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:
                    await self._clear_react(msg)
                    return

                if not pred.result:
                    await ctx.send("Not selling those items.")
                    return
            total_price = 0
            msg = ""
            async with ctx.typing():
                async for slot_name, slot_group in AsyncIter(slots, steps=100):
                    async for item_name, item in AsyncIter(slot_group,
                                                           steps=100):
                        old_owned = item.owned
                        item_price = 0
                        async for _loop_counter in AsyncIter(range(
                                0, old_owned),
                                                             steps=100):
                            item.owned -= 1
                            item_price += _sell(character, item)
                            if item.owned <= 0 and item.name in character.backpack:
                                del character.backpack[item.name]
                        item_price = max(item_price, 0)
                        msg += _("{old_item} sold for {price}.\n").format(
                            old_item=str(old_owned) + " " + str(item),
                            price=humanize_number(item_price),
                        )
                        total_price += item_price
                if total_price > 0:
                    try:
                        await bank.deposit_credits(ctx.author, total_price)
                    except BalanceTooHigh as e:
                        await bank.set_balance(ctx.author, e.max_balance)
                character.last_known_currency = await bank.get_balance(
                    ctx.author)
                character.last_currency_check = time.time()
                await self.config.user(ctx.author).set(await character.to_json(
                    ctx, self.config))
            if total_price == 0:
                return await smart_embed(
                    ctx,
                    _("No items matched your query.").format(),
                )
            if msg:
                msg_list = []
                new_msg = _(
                    "{author} sold {number} items and their duplicates for {price}.\n\n{items}"
                ).format(
                    author=escape(ctx.author.display_name),
                    number=humanize_number(total_items),
                    price=humanize_number(total_price),
                    items=msg,
                )
                for page in pagify(new_msg, shorten_by=10, page_length=1900):
                    msg_list.append(box(page, lang="css"))
                await BaseMenu(
                    source=SimpleSource(msg_list),
                    delete_message_after=True,
                    clear_reactions_after=True,
                    timeout=60,
                ).start(ctx=ctx)
コード例 #19
0
    async def _enqueue_tracks(
        self, ctx: commands.Context, query: Union[Query, list], enqueue: bool = True
    ) -> Union[discord.Message, List[lavalink.Track], lavalink.Track]:
        player = lavalink.get_player(ctx.guild.id)
        try:
            if self.play_lock[ctx.message.guild.id]:
                return await self.send_embed_msg(
                    ctx,
                    title=_("Unable To Get Tracks"),
                    description=_("Wait until the playlist has finished loading."),
                )
        except KeyError:
            self.update_player_lock(ctx, True)
        guild_data = await self.config.guild(ctx.guild).all()
        first_track_only = False
        single_track = None
        index = None
        playlist_data = None
        playlist_url = None
        seek = 0
        if type(query) is not list:
            if not await self.is_query_allowed(self.config, ctx, f"{query}", query_obj=query):
                raise QueryUnauthorized(
                    _("{query} is not an allowed query.").format(query=query.to_string_user())
                )
            if query.single_track:
                first_track_only = True
                index = query.track_index
                if query.start_time:
                    seek = query.start_time
            try:
                result, called_api = await self.api_interface.fetch_track(ctx, player, query)
            except TrackEnqueueError:
                self.update_player_lock(ctx, False)
                return await self.send_embed_msg(
                    ctx,
                    title=_("Unable to Get Track"),
                    description=_(
                        "I'm unable to get a track from Lavalink at the moment, "
                        "try again in a few minutes."
                    ),
                )
            except Exception as e:
                self.update_player_lock(ctx, False)
                raise e
            tracks = result.tracks
            playlist_data = result.playlist_info
            if not enqueue:
                return tracks
            if not tracks:
                self.update_player_lock(ctx, False)
                title = _("Nothing found.")
                embed = discord.Embed(title=title)
                if result.exception_message:
                    if "Status Code" in result.exception_message:
                        embed.set_footer(text=result.exception_message[:2000])
                    else:
                        embed.set_footer(text=result.exception_message[:2000].replace("\n", ""))
                if await self.config.use_external_lavalink() and query.is_local:
                    embed.description = _(
                        "Local tracks will not work "
                        "if the `Lavalink.jar` cannot see the track.\n"
                        "This may be due to permissions or because Lavalink.jar is being run "
                        "in a different machine than the local tracks."
                    )
                elif query.is_local and query.suffix in _PARTIALLY_SUPPORTED_MUSIC_EXT:
                    title = _("Track is not playable.")
                    embed = discord.Embed(title=title)
                    embed.description = _(
                        "**{suffix}** is not a fully supported format and some "
                        "tracks may not play."
                    ).format(suffix=query.suffix)
                return await self.send_embed_msg(ctx, embed=embed)
        else:
            tracks = query
        queue_dur = await self.queue_duration(ctx)
        queue_total_duration = self.format_time(queue_dur)
        before_queue_length = len(player.queue)

        if not first_track_only and len(tracks) > 1:
            # a list of Tracks where all should be enqueued
            # this is a Spotify playlist already made into a list of Tracks or a
            # url where Lavalink handles providing all Track objects to use, like a
            # YouTube or Soundcloud playlist
            if len(player.queue) >= 10000:
                return await self.send_embed_msg(ctx, title=_("Queue size limit reached."))
            track_len = 0
            empty_queue = not player.queue
            async for track in AsyncIter(tracks):
                if len(player.queue) >= 10000:
                    continue
                query = Query.process_input(track, self.local_folder_current_path)
                if not await self.is_query_allowed(
                    self.config,
                    ctx,
                    f"{track.title} {track.author} {track.uri} " f"{str(query)}",
                    query_obj=query,
                ):
                    if IS_DEBUG:
                        log.debug(f"Query is not allowed in {ctx.guild} ({ctx.guild.id})")
                    continue
                elif guild_data["maxlength"] > 0:
                    if self.is_track_length_allowed(track, guild_data["maxlength"]):
                        track_len += 1
                        track.extras.update(
                            {
                                "enqueue_time": int(time.time()),
                                "vc": player.channel.id,
                                "requester": ctx.author.id,
                            }
                        )
                        player.add(ctx.author, track)
                        self.bot.dispatch(
                            "red_audio_track_enqueue", player.channel.guild, track, ctx.author
                        )

                else:
                    track_len += 1
                    track.extras.update(
                        {
                            "enqueue_time": int(time.time()),
                            "vc": player.channel.id,
                            "requester": ctx.author.id,
                        }
                    )
                    player.add(ctx.author, track)
                    self.bot.dispatch(
                        "red_audio_track_enqueue", player.channel.guild, track, ctx.author
                    )
            player.maybe_shuffle(0 if empty_queue else 1)

            if len(tracks) > track_len:
                maxlength_msg = _(" {bad_tracks} tracks cannot be queued.").format(
                    bad_tracks=(len(tracks) - track_len)
                )
            else:
                maxlength_msg = ""
            playlist_name = escape(
                playlist_data.name if playlist_data else _("No Title"), formatting=True
            )
            embed = discord.Embed(
                description=bold(f"[{playlist_name}]({playlist_url})")
                if playlist_url
                else playlist_name,
                title=_("Playlist Enqueued"),
            )
            embed.set_footer(
                text=_("Added {num} tracks to the queue.{maxlength_msg}").format(
                    num=track_len, maxlength_msg=maxlength_msg
                )
            )
            if not guild_data["shuffle"] and queue_dur > 0:
                embed.set_footer(
                    text=_(
                        "{time} until start of playlist playback: starts at #{position} in queue"
                    ).format(time=queue_total_duration, position=before_queue_length + 1)
                )
            if not player.current:
                await player.play()
            self.update_player_lock(ctx, False)
            message = await self.send_embed_msg(ctx, embed=embed)
            return tracks or message
        else:
            single_track = None
            # a ytsearch: prefixed item where we only need the first Track returned
            # this is in the case of [p]play <query>, a single Spotify url/code
            # or this is a localtrack item
            try:
                if len(player.queue) >= 10000:
                    return await self.send_embed_msg(ctx, title=_("Queue size limit reached."))

                single_track = (
                    tracks
                    if isinstance(tracks, lavalink.rest_api.Track)
                    else tracks[index]
                    if index
                    else tracks[0]
                )
                if seek and seek > 0:
                    single_track.start_timestamp = seek * 1000
                query = Query.process_input(single_track, self.local_folder_current_path)
                if not await self.is_query_allowed(
                    self.config,
                    ctx,
                    (
                        f"{single_track.title} {single_track.author} {single_track.uri} "
                        f"{str(query)}"
                    ),
                    query_obj=query,
                ):
                    if IS_DEBUG:
                        log.debug(f"Query is not allowed in {ctx.guild} ({ctx.guild.id})")
                    self.update_player_lock(ctx, False)
                    return await self.send_embed_msg(
                        ctx, title=_("This track is not allowed in this server.")
                    )
                elif guild_data["maxlength"] > 0:
                    if self.is_track_length_allowed(single_track, guild_data["maxlength"]):
                        single_track.extras.update(
                            {
                                "enqueue_time": int(time.time()),
                                "vc": player.channel.id,
                                "requester": ctx.author.id,
                            }
                        )
                        player.add(ctx.author, single_track)
                        player.maybe_shuffle()
                        self.bot.dispatch(
                            "red_audio_track_enqueue",
                            player.channel.guild,
                            single_track,
                            ctx.author,
                        )
                    else:
                        self.update_player_lock(ctx, False)
                        return await self.send_embed_msg(
                            ctx, title=_("Track exceeds maximum length.")
                        )

                else:
                    single_track.extras.update(
                        {
                            "enqueue_time": int(time.time()),
                            "vc": player.channel.id,
                            "requester": ctx.author.id,
                        }
                    )
                    player.add(ctx.author, single_track)
                    player.maybe_shuffle()
                    self.bot.dispatch(
                        "red_audio_track_enqueue", player.channel.guild, single_track, ctx.author
                    )
            except IndexError:
                self.update_player_lock(ctx, False)
                title = _("Nothing found")
                desc = EmptyEmbed
                if await self.bot.is_owner(ctx.author):
                    desc = _("Please check your console or logs for details.")
                return await self.send_embed_msg(ctx, title=title, description=desc)
            except Exception as e:
                self.update_player_lock(ctx, False)
                raise e
            description = await self.get_track_description(
                single_track, self.local_folder_current_path
            )
            embed = discord.Embed(title=_("Track Enqueued"), description=description)
            if not guild_data["shuffle"] and queue_dur > 0:
                embed.set_footer(
                    text=_("{time} until track playback: #{position} in queue").format(
                        time=queue_total_duration, position=before_queue_length + 1
                    )
                )

        if not player.current:
            await player.play()
        self.update_player_lock(ctx, False)
        message = await self.send_embed_msg(ctx, embed=embed)
        return single_track or message
コード例 #20
0
    async def backpack_disassemble(self, ctx: commands.Context, *,
                                   backpack_items: ItemsConverter):
        """
        Disassemble items from your backpack.

        This will provide a chance for a chest,
        or the item might break while you are handling it...
        """
        if self.in_adventure(ctx):
            return await smart_embed(
                ctx,
                _("You tried to disassemble an item but the monster ahead of you commands your attention."
                  ),
            )

        async with self.get_lock(ctx.author):
            if len(backpack_items[1]) > 2:
                msg = await ctx.send(
                    "Are you sure you want to disassemble {count} unique items and their duplicates?"
                    .format(count=humanize_number(len(backpack_items[1]))))
                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:
                    await self._clear_react(msg)
                    return

                if not pred.result:
                    await ctx.send("Not disassembling those items.")
                    return

            try:
                character = await Character.from_json(ctx, self.config,
                                                      ctx.author,
                                                      self._daily_bonus)
            except Exception as exc:
                log.exception("Error with the new character sheet",
                              exc_info=exc)
                return
            failed = 0
            success = 0
            op = backpack_items[0]
            disassembled = set()
            async for item in AsyncIter(backpack_items[1], steps=100):
                try:
                    item = character.backpack[item.name]
                except KeyError:
                    continue
                if item.name in disassembled:
                    continue
                if item.rarity in ["forged"]:
                    continue
                index = min(RARITIES.index(item.rarity), 4)
                if op == "single":
                    if character.heroclass["name"] != "Tinkerer":
                        roll = random.randint(0, 5)
                        chests = 1
                    else:
                        roll = random.randint(0, 3)
                        chests = random.randint(1, 2)
                    if roll != 0:
                        item.owned -= 1
                        if item.owned <= 0:
                            del character.backpack[item.name]
                        await self.config.user(ctx.author).set(
                            await character.to_json(ctx, self.config))
                        return await smart_embed(
                            ctx,
                            _("Your attempt at disassembling `{}` failed and it has been destroyed."
                              ).format(item.name),
                        )
                    else:
                        item.owned -= 1
                        if item.owned <= 0:
                            del character.backpack[item.name]
                        character.treasure[index] += chests
                        await self.config.user(ctx.author).set(
                            await character.to_json(ctx, self.config))
                        return await smart_embed(
                            ctx,
                            _("Your attempt at disassembling `{}` was successful and you have received {} {}."
                              ).format(
                                  item.name, chests,
                                  _("chests") if chests > 1 else _("chest")),
                        )
                elif op == "all":
                    disassembled.add(item.name)
                    owned = item.owned
                    async for _loop_counter in AsyncIter(range(0, owned),
                                                         steps=100):
                        if character.heroclass["name"] != "Tinkerer":
                            roll = random.randint(0, 5)
                            chests = 1
                        else:
                            roll = random.randint(0, 3)
                            chests = random.randint(1, 2)
                        if roll != 0:
                            item.owned -= 1
                            if item.owned <= 0 and item.name in character.backpack:
                                del character.backpack[item.name]
                            failed += 1
                        else:
                            item.owned -= 1
                            if item.owned <= 0 and item.name in character.backpack:
                                del character.backpack[item.name]
                            character.treasure[index] += chests
                            success += 1
            await self.config.user(ctx.author
                                   ).set(await
                                         character.to_json(ctx, self.config))
            return await smart_embed(
                ctx,
                _("You attempted to disassemble multiple items: {succ} were successful and {fail} failed."
                  ).format(succ=humanize_number(success),
                           fail=humanize_number(failed)),
            )
コード例 #21
0
 async def initialize(self):
     async for guild_id, guild_data in AsyncIter(
         (await self.config.all_guilds()).items(), steps=100
     ):
         if guild_data["channel"]:
             self.channel_cache[guild_id] = guild_data["channel"]
コード例 #22
0
    async def backpack_sellall(
        self,
        ctx: commands.Context,
        rarity: Optional[RarityConverter] = None,
        *,
        slot: Optional[SlotConverter] = None,
    ):
        """Sell all items in your backpack. Optionally specify rarity or slot."""
        assert isinstance(rarity, str) or rarity is None
        assert isinstance(slot, str) or slot is None
        if self.in_adventure(ctx):
            return await smart_embed(
                ctx,
                _("You tried to go sell your items but the monster ahead is not allowing you to leave."
                  ),
            )
        if rarity:
            rarity = rarity.lower()
            if rarity not in RARITIES:
                return await smart_embed(
                    ctx,
                    _("{} is not a valid rarity, select one of {}").format(
                        rarity, humanize_list(RARITIES)),
                )
            if rarity.lower() in ["forged"]:
                return await smart_embed(
                    ctx,
                    _("You cannot sell `{rarity}` rarity items.").format(
                        rarity=rarity))
        if slot:
            slot = slot.lower()
            if slot not in ORDER:
                return await smart_embed(
                    ctx,
                    _("{} is not a valid slot, select one of {}").format(
                        slot, humanize_list(ORDER)),
                )

        async with self.get_lock(ctx.author):
            if rarity and slot:
                msg = await ctx.send(
                    "Are you sure you want to sell all {rarity} {slot} items in your inventory?"
                    .format(rarity=rarity, slot=slot))
            elif rarity or slot:
                msg = await ctx.send(
                    "Are you sure you want to sell all{rarity}{slot} items in your inventory?"
                    .format(rarity=f" {rarity}" if rarity else "",
                            slot=f" {slot}" if slot else ""))
            else:
                msg = await ctx.send(
                    "Are you sure you want to sell **ALL ITEMS** in your inventory?"
                )

            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:
                await self._clear_react(msg)
                return

            if not pred.result:
                await ctx.send("Not selling those items.")
                return

            msg = ""
            try:
                c = await Character.from_json(ctx, self.config, ctx.author,
                                              self._daily_bonus)
            except Exception as exc:
                log.exception("Error with the new character sheet",
                              exc_info=exc)
                return
            total_price = 0
            async with ctx.typing():
                items = [
                    i for n, i in c.backpack.items()
                    if i.rarity not in ["forged"]
                ]
                count = 0
                async for item in AsyncIter(items, steps=100):
                    if rarity and item.rarity != rarity:
                        continue
                    if slot:
                        if len(item.slot) == 1 and slot != item.slot[0]:
                            continue
                        elif len(item.slot) == 2 and slot != "two handed":
                            continue
                    item_price = 0
                    old_owned = item.owned
                    async for _loop_counter in AsyncIter(range(0, old_owned),
                                                         steps=100):
                        item.owned -= 1
                        item_price += _sell(c, item)
                        if item.owned <= 0:
                            del c.backpack[item.name]
                    item_price = max(item_price, 0)
                    msg += _("{old_item} sold for {price}.\n").format(
                        old_item=str(old_owned) + " " + str(item),
                        price=humanize_number(item_price),
                    )
                    total_price += item_price
                if total_price > 0:
                    try:
                        await bank.deposit_credits(ctx.author, total_price)
                    except BalanceTooHigh as e:
                        await bank.set_balance(ctx.author, e.max_balance)
                c.last_known_currency = await bank.get_balance(ctx.author)
                c.last_currency_check = time.time()
                await self.config.user(ctx.author
                                       ).set(await c.to_json(ctx, self.config))
        msg_list = []
        new_msg = _(
            "{author} sold all their{rarity} items for {price}.\n\n{items}"
        ).format(
            author=escape(ctx.author.display_name),
            rarity=f" {rarity}" if rarity else "",
            price=humanize_number(total_price),
            items=msg,
        )
        for page in pagify(new_msg, shorten_by=10, page_length=1900):
            msg_list.append(box(page, lang="css"))
        await BaseMenu(
            source=SimpleSource(msg_list),
            delete_message_after=True,
            clear_reactions_after=True,
            timeout=60,
        ).start(ctx=ctx)
コード例 #23
0
 async def cache_guild(self, guild_id: int, guild_data: dict):
     async for tag_name, tag_data in AsyncIter(guild_data["tags"].items(),
                                               steps=50):
         tag = Tag.from_dict(self, tag_name, tag_data, guild_id=guild_id)
         tag.add_to_cache()
コード例 #24
0
class BackPackCommands(AdventureMixin):
    """This class will handle interacting with adventures backpack"""
    @commands.group(name="backpack", autohelp=False)
    @commands.bot_has_permissions(add_reactions=True)
    async def _backpack(
        self,
        ctx: commands.Context,
        show_diff: Optional[bool] = False,
        rarity: Optional[RarityConverter] = None,
        *,
        slot: Optional[SlotConverter] = None,
    ):
        """This shows the contents of your backpack.

        Give it a rarity and/or slot to filter what backpack items to show.

        Selling:     `[p]backpack sell item_name`
        Trading:     `[p]backpack trade @user price item_name`
        Equip:       `[p]backpack equip item_name`
        Sell All:    `[p]backpack sellall rarity slot`
        Disassemble: `[p]backpack disassemble item_name`

        Note: An item **degrade** level is how many rebirths it will last, before it is broken down.
        """
        assert isinstance(rarity, str) or rarity is None
        assert isinstance(slot, str) or slot is None
        if not await self.allow_in_dm(ctx):
            return await smart_embed(
                ctx, _("This command is not available in DM's on this bot."))
        if not ctx.invoked_subcommand:
            try:
                c = await Character.from_json(ctx, self.config, ctx.author,
                                              self._daily_bonus)
            except Exception as exc:
                log.exception("Error with the new character sheet",
                              exc_info=exc)
                return
            if rarity:
                rarity = rarity.lower()
                if rarity not in RARITIES:
                    return await smart_embed(
                        ctx,
                        _("{} is not a valid rarity, select one of {}").format(
                            rarity, humanize_list(RARITIES)),
                    )
            if slot:
                slot = slot.lower()
                if slot not in ORDER:
                    return await smart_embed(
                        ctx,
                        _("{} is not a valid slot, select one of {}").format(
                            slot, humanize_list(ORDER)),
                    )

            msgs = await c.get_backpack(rarity=rarity,
                                        slot=slot,
                                        show_delta=show_diff)
            if not msgs:
                return await smart_embed(
                    ctx,
                    _("You have no items in your backpack."),
                )
            await BackpackMenu(
                source=SimpleSource(msgs),
                help_command=self._backpack,
                delete_message_after=True,
                clear_reactions_after=True,
                timeout=60,
            ).start(ctx=ctx)

    @_backpack.command(name="equip")
    async def backpack_equip(self, ctx: commands.Context, *,
                             equip_item: EquipableItemConverter):
        """Equip an item from your backpack."""
        assert isinstance(equip_item, Item)
        if self.in_adventure(ctx):
            return await smart_embed(
                ctx,
                _("You tried to equip an item but the monster ahead of you commands your attention."
                  ),
            )
        async with self.get_lock(ctx.author):
            try:
                c = await Character.from_json(ctx, self.config, ctx.author,
                                              self._daily_bonus)
            except Exception as exc:
                log.exception("Error with the new character sheet",
                              exc_info=exc)
                return
            equiplevel = c.equip_level(equip_item)
            if is_dev(ctx.author):  # FIXME:
                equiplevel = 0

            if not c.can_equip(equip_item):
                return await smart_embed(
                    ctx,
                    _("You need to be level `{level}` to equip this item.").
                    format(level=equiplevel),
                )

            equip = c.backpack.get(equip_item.name)
            if equip:
                slot = equip.slot[0]
                if len(equip.slot) > 1:
                    slot = "two handed"
                if not getattr(c, equip.slot[0]):
                    equip_msg = box(
                        _("{author} equipped {item} ({slot} slot).").format(
                            author=escape(ctx.author.display_name),
                            item=str(equip),
                            slot=slot),
                        lang="css",
                    )
                else:
                    equip_msg = box(
                        _("{author} equipped {item} ({slot} slot) and put {put} into their backpack."
                          ).format(
                              author=escape(ctx.author.display_name),
                              item=str(equip),
                              slot=slot,
                              put=getattr(c, equip.slot[0]),
                          ),
                        lang="css",
                    )
                await ctx.send(equip_msg)
                c = await c.equip_item(equip, True,
                                       is_dev(ctx.author))  # FIXME:
                await self.config.user(ctx.author
                                       ).set(await c.to_json(ctx, self.config))

    @_backpack.command(name="eset", cooldown_after_parsing=True)
    @commands.cooldown(rate=1, per=600, type=commands.BucketType.user)
    async def backpack_eset(self, ctx: commands.Context, *, set_name: str):
        """Equip all parts of a set that you own."""
        if self.in_adventure(ctx):
            ctx.command.reset_cooldown(ctx)
            return await smart_embed(
                ctx,
                _("You tried to magically equip multiple items at once, but the monster ahead nearly killed you."
                  ),
            )
        set_list = humanize_list(
            sorted([f"`{i}`" for i in self.SET_BONUSES.keys()], key=str.lower))
        if set_name is None:
            ctx.command.reset_cooldown(ctx)
            return await smart_embed(
                ctx,
                _("Use this command with one of the following set names: \n{sets}"
                  ).format(sets=set_list),
            )
        async with self.get_lock(ctx.author):
            try:
                character = await Character.from_json(ctx, self.config,
                                                      ctx.author,
                                                      self._daily_bonus)
            except Exception as exc:
                log.exception("Error with the new character sheet",
                              exc_info=exc)
                ctx.command.reset_cooldown(ctx)
                return

            pieces = await character.get_set_count(return_items=True,
                                                   set_name=set_name.title())
            if not pieces:
                ctx.command.reset_cooldown(ctx)
                return await smart_embed(
                    ctx,
                    _("You have no pieces of `{set_name}` that you can equip."
                      ).format(set_name=set_name),
                )
            for piece in pieces:
                character = await character.equip_item(piece,
                                                       from_backpack=True)
            await self.config.user(ctx.author
                                   ).set(await
                                         character.to_json(ctx, self.config))
            await smart_embed(
                ctx,
                _("I've equipped all pieces of `{set_name}` that you are able to equip."
                  ).format(set_name=set_name),
            )

    @_backpack.command(name="disassemble")
    async def backpack_disassemble(self, ctx: commands.Context, *,
                                   backpack_items: ItemsConverter):
        """
        Disassemble items from your backpack.

        This will provide a chance for a chest,
        or the item might break while you are handling it...
        """
        if self.in_adventure(ctx):
            return await smart_embed(
                ctx,
                _("You tried to disassemble an item but the monster ahead of you commands your attention."
                  ),
            )

        async with self.get_lock(ctx.author):
            if len(backpack_items[1]) > 2:
                msg = await ctx.send(
                    "Are you sure you want to disassemble {count} unique items and their duplicates?"
                    .format(count=humanize_number(len(backpack_items[1]))))
                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:
                    await self._clear_react(msg)
                    return

                if not pred.result:
                    await ctx.send("Not disassembling those items.")
                    return

            try:
                character = await Character.from_json(ctx, self.config,
                                                      ctx.author,
                                                      self._daily_bonus)
            except Exception as exc:
                log.exception("Error with the new character sheet",
                              exc_info=exc)
                return
            failed = 0
            success = 0
            op = backpack_items[0]
            disassembled = set()
            async for item in AsyncIter(backpack_items[1], steps=100):
                try:
                    item = character.backpack[item.name]
                except KeyError:
                    continue
                if item.name in disassembled:
                    continue
                if item.rarity in ["forged"]:
                    continue
                index = min(RARITIES.index(item.rarity), 4)
                if op == "single":
                    if character.heroclass["name"] != "Tinkerer":
                        roll = random.randint(0, 5)
                        chests = 1
                    else:
                        roll = random.randint(0, 3)
                        chests = random.randint(1, 2)
                    if roll != 0:
                        item.owned -= 1
                        if item.owned <= 0:
                            del character.backpack[item.name]
                        await self.config.user(ctx.author).set(
                            await character.to_json(ctx, self.config))
                        return await smart_embed(
                            ctx,
                            _("Your attempt at disassembling `{}` failed and it has been destroyed."
                              ).format(item.name),
                        )
                    else:
                        item.owned -= 1
                        if item.owned <= 0:
                            del character.backpack[item.name]
                        character.treasure[index] += chests
                        await self.config.user(ctx.author).set(
                            await character.to_json(ctx, self.config))
                        return await smart_embed(
                            ctx,
                            _("Your attempt at disassembling `{}` was successful and you have received {} {}."
                              ).format(
                                  item.name, chests,
                                  _("chests") if chests > 1 else _("chest")),
                        )
                elif op == "all":
                    disassembled.add(item.name)
                    owned = item.owned
                    async for _loop_counter in AsyncIter(range(0, owned),
                                                         steps=100):
                        if character.heroclass["name"] != "Tinkerer":
                            roll = random.randint(0, 5)
                            chests = 1
                        else:
                            roll = random.randint(0, 3)
                            chests = random.randint(1, 2)
                        if roll != 0:
                            item.owned -= 1
                            if item.owned <= 0 and item.name in character.backpack:
                                del character.backpack[item.name]
                            failed += 1
                        else:
                            item.owned -= 1
                            if item.owned <= 0 and item.name in character.backpack:
                                del character.backpack[item.name]
                            character.treasure[index] += chests
                            success += 1
            await self.config.user(ctx.author
                                   ).set(await
                                         character.to_json(ctx, self.config))
            return await smart_embed(
                ctx,
                _("You attempted to disassemble multiple items: {succ} were successful and {fail} failed."
                  ).format(succ=humanize_number(success),
                           fail=humanize_number(failed)),
            )

    @_backpack.command(name="sellall")
    async def backpack_sellall(
        self,
        ctx: commands.Context,
        rarity: Optional[RarityConverter] = None,
        *,
        slot: Optional[SlotConverter] = None,
    ):
        """Sell all items in your backpack. Optionally specify rarity or slot."""
        assert isinstance(rarity, str) or rarity is None
        assert isinstance(slot, str) or slot is None
        if self.in_adventure(ctx):
            return await smart_embed(
                ctx,
                _("You tried to go sell your items but the monster ahead is not allowing you to leave."
                  ),
            )
        if rarity:
            rarity = rarity.lower()
            if rarity not in RARITIES:
                return await smart_embed(
                    ctx,
                    _("{} is not a valid rarity, select one of {}").format(
                        rarity, humanize_list(RARITIES)),
                )
            if rarity.lower() in ["forged"]:
                return await smart_embed(
                    ctx,
                    _("You cannot sell `{rarity}` rarity items.").format(
                        rarity=rarity))
        if slot:
            slot = slot.lower()
            if slot not in ORDER:
                return await smart_embed(
                    ctx,
                    _("{} is not a valid slot, select one of {}").format(
                        slot, humanize_list(ORDER)),
                )

        async with self.get_lock(ctx.author):
            if rarity and slot:
                msg = await ctx.send(
                    "Are you sure you want to sell all {rarity} {slot} items in your inventory?"
                    .format(rarity=rarity, slot=slot))
            elif rarity or slot:
                msg = await ctx.send(
                    "Are you sure you want to sell all{rarity}{slot} items in your inventory?"
                    .format(rarity=f" {rarity}" if rarity else "",
                            slot=f" {slot}" if slot else ""))
            else:
                msg = await ctx.send(
                    "Are you sure you want to sell **ALL ITEMS** in your inventory?"
                )

            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:
                await self._clear_react(msg)
                return

            if not pred.result:
                await ctx.send("Not selling those items.")
                return

            msg = ""
            try:
                c = await Character.from_json(ctx, self.config, ctx.author,
                                              self._daily_bonus)
            except Exception as exc:
                log.exception("Error with the new character sheet",
                              exc_info=exc)
                return
            total_price = 0
            async with ctx.typing():
                items = [
                    i for n, i in c.backpack.items()
                    if i.rarity not in ["forged"]
                ]
                count = 0
                async for item in AsyncIter(items, steps=100):
                    if rarity and item.rarity != rarity:
                        continue
                    if slot:
                        if len(item.slot) == 1 and slot != item.slot[0]:
                            continue
                        elif len(item.slot) == 2 and slot != "two handed":
                            continue
                    item_price = 0
                    old_owned = item.owned
                    async for _loop_counter in AsyncIter(range(0, old_owned),
                                                         steps=100):
                        item.owned -= 1
                        item_price += _sell(c, item)
                        if item.owned <= 0:
                            del c.backpack[item.name]
                    item_price = max(item_price, 0)
                    msg += _("{old_item} sold for {price}.\n").format(
                        old_item=str(old_owned) + " " + str(item),
                        price=humanize_number(item_price),
                    )
                    total_price += item_price
                if total_price > 0:
                    try:
                        await bank.deposit_credits(ctx.author, total_price)
                    except BalanceTooHigh as e:
                        await bank.set_balance(ctx.author, e.max_balance)
                c.last_known_currency = await bank.get_balance(ctx.author)
                c.last_currency_check = time.time()
                await self.config.user(ctx.author
                                       ).set(await c.to_json(ctx, self.config))
        msg_list = []
        new_msg = _(
            "{author} sold all their{rarity} items for {price}.\n\n{items}"
        ).format(
            author=escape(ctx.author.display_name),
            rarity=f" {rarity}" if rarity else "",
            price=humanize_number(total_price),
            items=msg,
        )
        for page in pagify(new_msg, shorten_by=10, page_length=1900):
            msg_list.append(box(page, lang="css"))
        await BaseMenu(
            source=SimpleSource(msg_list),
            delete_message_after=True,
            clear_reactions_after=True,
            timeout=60,
        ).start(ctx=ctx)

    @_backpack.command(name="sell", cooldown_after_parsing=True)
    @commands.cooldown(rate=3, per=60, type=commands.BucketType.user)
    async def backpack_sell(self, ctx: commands.Context, *,
                            item: ItemConverter):
        """Sell an item from your backpack."""

        if self.in_adventure(ctx):
            return await smart_embed(
                ctx,
                _("You tried to go sell your items but the monster ahead is not allowing you to leave."
                  ),
            )
        if item.rarity in ["forged"]:
            ctx.command.reset_cooldown(ctx)
            return await ctx.send(
                box(
                    _("\n{author}, your {device} is refusing to be sold and bit your finger for trying."
                      ).format(author=escape(ctx.author.display_name),
                               device=str(item)),
                    lang="css",
                ))

        async with self.get_lock(ctx.author):
            try:
                c = await Character.from_json(ctx, self.config, ctx.author,
                                              self._daily_bonus)
            except Exception as exc:
                ctx.command.reset_cooldown(ctx)
                log.exception("Error with the new character sheet",
                              exc_info=exc)
                return
            price_shown = _sell(c, item)
            messages = [
                _("**{author}**, do you want to sell this item for {price} each? {item}"
                  ).format(
                      author=escape(ctx.author.display_name),
                      item=box(str(item), lang="css"),
                      price=humanize_number(price_shown),
                  )
            ]
            try:
                item = c.backpack[item.name]
            except KeyError:
                return

            async def _backpack_sell_menu(
                ctx: commands.Context,
                pages: list,
                controls: dict,
                message: discord.Message,
                page: int,
                timeout: float,
                emoji: str,
            ):
                if message:
                    with contextlib.suppress(discord.HTTPException):
                        await message.delete()
                    await self._backpack_sell_button_action(
                        ctx, emoji, page, item, price_shown, c)
                    return None

            back_pack_sell_controls = {
                "\N{DIGIT ONE}\N{COMBINING ENCLOSING KEYCAP}":
                _backpack_sell_menu,
                "\N{CLOCKWISE RIGHTWARDS AND LEFTWARDS OPEN CIRCLE ARROWS}":
                _backpack_sell_menu,
                "\N{CLOCKWISE RIGHTWARDS AND LEFTWARDS OPEN CIRCLE ARROWS WITH CIRCLED ONE OVERLAY}":
                _backpack_sell_menu,
                "\N{CROSS MARK}":
                _backpack_sell_menu,
            }

            await menu(ctx, messages, back_pack_sell_controls, timeout=60)

    async def _backpack_sell_button_action(self, ctx, emoji, page, item,
                                           price_shown, character):
        currency_name = await bank.get_currency_name(ctx.guild, )
        msg = ""
        if emoji == "\N{DIGIT ONE}\N{COMBINING ENCLOSING KEYCAP}":  # user reacted with one to sell.
            ctx.command.reset_cooldown(ctx)
            # sell one of the item
            price = 0
            item.owned -= 1
            price += price_shown
            msg += _(
                "**{author}** sold one {item} for {price} {currency_name}.\n"
            ).format(
                author=escape(ctx.author.display_name),
                item=box(item, lang="css"),
                price=humanize_number(price),
                currency_name=currency_name,
            )
            if item.owned <= 0:
                del character.backpack[item.name]
            price = max(price, 0)
            if price > 0:
                try:
                    await bank.deposit_credits(ctx.author, price)
                except BalanceTooHigh as e:
                    await bank.set_balance(ctx.author, e.max_balance)
        elif emoji == "\N{CLOCKWISE RIGHTWARDS AND LEFTWARDS OPEN CIRCLE ARROWS}":  # user wants to sell all owned.
            ctx.command.reset_cooldown(ctx)
            price = 0
            old_owned = item.owned
            count = 0
            async for _loop_counter in AsyncIter(range(0, item.owned),
                                                 steps=50):
                item.owned -= 1
                price += price_shown
                if item.owned <= 0:
                    del character.backpack[item.name]
                count += 1
            msg += _(
                "**{author}** sold all their {old_item} for {price} {currency_name}.\n"
            ).format(
                author=escape(ctx.author.display_name),
                old_item=box(str(item) + " - " + str(old_owned), lang="css"),
                price=humanize_number(price),
                currency_name=currency_name,
            )
            price = max(price, 0)
            if price > 0:
                try:
                    await bank.deposit_credits(ctx.author, price)
                except BalanceTooHigh as e:
                    await bank.set_balance(ctx.author, e.max_balance)
        elif (emoji ==
              "\N{CLOCKWISE RIGHTWARDS AND LEFTWARDS OPEN CIRCLE ARROWS WITH CIRCLED ONE OVERLAY}"
              ):  # user wants to sell all but one.
            if item.owned == 1:
                ctx.command.reset_cooldown(ctx)
                return await smart_embed(
                    ctx, _("You already only own one of those items."))
            price = 0
            old_owned = item.owned
            count = 0
            async for _loop_counter in AsyncIter(range(1, item.owned),
                                                 steps=50):
                item.owned -= 1
                price += price_shown
            count += 1
            if price != 0:
                msg += _(
                    "**{author}** sold all but one of their {old_item} for {price} {currency_name}.\n"
                ).format(
                    author=escape(ctx.author.display_name),
                    old_item=box(str(item) + " - " + str(old_owned - 1),
                                 lang="css"),
                    price=humanize_number(price),
                    currency_name=currency_name,
                )
                price = max(price, 0)
                if price > 0:
                    try:
                        await bank.deposit_credits(ctx.author, price)
                    except BalanceTooHigh as e:
                        await bank.set_balance(ctx.author, e.max_balance)
        else:  # user doesn't want to sell those items.
            await ctx.send(_("Not selling those items."))

        if msg:
            character.last_known_currency = await bank.get_balance(ctx.author)
            character.last_currency_check = time.time()
            await self.config.user(ctx.author
                                   ).set(await
                                         character.to_json(ctx, self.config))
            pages = [
                page for page in pagify(msg, delims=["\n"], page_length=1900)
            ]
            await BaseMenu(
                source=SimpleSource(pages),
                delete_message_after=True,
                clear_reactions_after=True,
                timeout=60,
            ).start(ctx=ctx)

    @_backpack.command(name="trade")
    async def backpack_trade(
        self,
        ctx: commands.Context,
        buyer: discord.Member,
        asking: Optional[int] = 1000,
        *,
        item: ItemConverter,
    ):
        """Trade an item from your backpack to another user."""
        if ctx.author == buyer:
            return await smart_embed(
                ctx,
                _("You take the item and pass it from one hand to the other. Congratulations, you traded yourself."
                  ),
            )
        if self.in_adventure(ctx):
            return await smart_embed(
                ctx,
                _("You tried to trade an item to a party member but the monster ahead commands your attention."
                  ),
            )
        if self.in_adventure(user=buyer):
            return await smart_embed(
                ctx,
                _("**{buyer}** is currently in an adventure... you were unable to reach them via pigeon."
                  ).format(buyer=escape(buyer.display_name)),
            )
        if asking < 0:
            return await ctx.send(_("You can't *sell* for less than 0..."))
        try:
            c = await Character.from_json(ctx, self.config, ctx.author,
                                          self._daily_bonus)
        except Exception as exc:
            log.exception("Error with the new character sheet", exc_info=exc)
            return
        try:
            buy_user = await Character.from_json(ctx, self.config, buyer,
                                                 self._daily_bonus)
        except Exception as exc:
            log.exception("Error with the new character sheet", exc_info=exc)
            return

        if buy_user.is_backpack_full(is_dev=is_dev(buyer)):
            await ctx.send(
                _("**{author}**'s backpack is currently full.").format(
                    author=escape(buyer.display_name)))
            return

        if not any([x for x in c.backpack if item.name.lower() == x.lower()]):
            return await smart_embed(
                ctx,
                _("**{author}**, you have to specify an item from your backpack to trade."
                  ).format(author=escape(ctx.author.display_name)),
            )
        lookup = list(x for n, x in c.backpack.items() if str(item) == str(x))
        if len(lookup) > 1:
            await smart_embed(
                ctx,
                _("**{author}**, I found multiple items ({items}) "
                  "matching that name in your backpack.\nPlease be more specific."
                  ).format(
                      author=escape(ctx.author.display_name),
                      items=humanize_list([x.name for x in lookup]),
                  ),
            )
            return
        if any([x for x in lookup if x.rarity == "forged"]):
            device = [x for x in lookup if x.rarity == "forged"]
            return await ctx.send(
                box(
                    _("\n{author}, your {device} does not want to leave you.").
                    format(author=escape(ctx.author.display_name),
                           device=str(device[0])),
                    lang="css",
                ))
        elif any([x for x in lookup if x.rarity == "set"]):
            return await ctx.send(
                box(
                    _("\n{character}, you cannot trade Set items as they are bound to your soul."
                      ).format(character=escape(ctx.author.display_name)),
                    lang="css",
                ))
        else:
            item = lookup[0]
            hand = item.slot[0] if len(item.slot) < 2 else "two handed"
            currency_name = await bank.get_currency_name(ctx.guild, )
            if str(currency_name).startswith("<"):
                currency_name = "credits"
            trade_talk = box(
                _("{author} wants to sell {item}. "
                  "(ATT: {att_item} | "
                  "CHA: {cha_item} | "
                  "INT: {int_item} | "
                  "DEX: {dex_item} | "
                  "LUCK: {luck_item}) "
                  "[{hand}])\n{buyer}, "
                  "do you want to buy this item for {asking} {currency_name}?"
                  ).format(
                      author=escape(ctx.author.display_name),
                      item=item,
                      att_item=str(item.att),
                      cha_item=str(item.cha),
                      int_item=str(item.int),
                      dex_item=str(item.dex),
                      luck_item=str(item.luck),
                      hand=hand,
                      buyer=escape(buyer.display_name),
                      asking=str(asking),
                      currency_name=currency_name,
                  ),
                lang="css",
            )
            async with self.get_lock(ctx.author):
                trade_msg = await ctx.send(f"{buyer.mention}\n{trade_talk}")
                start_adding_reactions(trade_msg,
                                       ReactionPredicate.YES_OR_NO_EMOJIS)
                pred = ReactionPredicate.yes_or_no(trade_msg, buyer)
                try:
                    await ctx.bot.wait_for("reaction_add",
                                           check=pred,
                                           timeout=60)
                except asyncio.TimeoutError:
                    await self._clear_react(trade_msg)
                    return
                if pred.result:  # buyer reacted with Yes.
                    with contextlib.suppress(discord.errors.NotFound):
                        if await bank.can_spend(buyer, asking):
                            if buy_user.rebirths + 1 < c.rebirths:
                                return await smart_embed(
                                    ctx,
                                    _("You can only trade with people that are the same "
                                      "rebirth level, one rebirth level less than you, "
                                      "or a higher rebirth level than yours."),
                                )
                            try:
                                await bank.transfer_credits(
                                    buyer, ctx.author, asking)
                            except BalanceTooHigh as e:
                                await bank.withdraw_credits(buyer, asking)
                                await bank.set_balance(ctx.author,
                                                       e.max_balance)
                            c.backpack[item.name].owned -= 1
                            newly_owned = c.backpack[item.name].owned
                            if c.backpack[item.name].owned <= 0:
                                del c.backpack[item.name]
                            async with self.get_lock(buyer):
                                if item.name in buy_user.backpack:
                                    buy_user.backpack[item.name].owned += 1
                                else:
                                    item.owned = 1
                                    buy_user.backpack[item.name] = item
                                await self.config.user(buyer).set(
                                    await buy_user.to_json(ctx, self.config))
                                item.owned = newly_owned
                                await self.config.user(ctx.author).set(
                                    await c.to_json(ctx, self.config))

                            await trade_msg.edit(content=(box(
                                _("\n{author} traded {item} to {buyer} for {asking} {currency_name}."
                                  ).format(
                                      author=escape(ctx.author.display_name),
                                      item=item,
                                      buyer=escape(buyer.display_name),
                                      asking=asking,
                                      currency_name=currency_name,
                                  ),
                                lang="css",
                            )))
                            await self._clear_react(trade_msg)
                        else:
                            await trade_msg.edit(content=_(
                                "**{buyer}**, you do not have enough {currency_name}."
                            ).format(
                                buyer=escape(buyer.display_name),
                                currency_name=currency_name,
                            ))
                else:
                    with contextlib.suppress(discord.HTTPException):
                        await trade_msg.delete()

    @commands.command(name="ebackpack")
    @commands.bot_has_permissions(add_reactions=True)
    async def commands_equipable_backpack(
        self,
        ctx: commands.Context,
        show_diff: Optional[bool] = False,
        rarity: Optional[RarityConverter] = None,
        *,
        slot: Optional[SlotConverter] = None,
    ):
        """This shows the contents of your backpack that can be equipped.

        Give it a rarity and/or slot to filter what backpack items to show.

        Note: An item **degrade** level is how many rebirths it will last, before it is broken down.
        """
        assert isinstance(rarity, str) or rarity is None
        assert isinstance(slot, str) or slot is None
        if not await self.allow_in_dm(ctx):
            return await smart_embed(
                ctx, _("This command is not available in DM's on this bot."))
        if not ctx.invoked_subcommand:
            try:
                c = await Character.from_json(ctx, self.config, ctx.author,
                                              self._daily_bonus)
            except Exception as exc:
                log.exception("Error with the new character sheet",
                              exc_info=exc)
                return
            if rarity:
                rarity = rarity.lower()
                if rarity not in RARITIES:
                    return await smart_embed(
                        ctx,
                        _("{} is not a valid rarity, select one of {}").format(
                            rarity, humanize_list(RARITIES)),
                    )
            if slot:
                slot = slot.lower()
                if slot not in ORDER:
                    return await smart_embed(
                        ctx,
                        _("{} is not a valid slot, select one of {}").format(
                            slot, humanize_list(ORDER)),
                    )

            backpack_pages = await c.get_backpack(rarity=rarity,
                                                  slot=slot,
                                                  show_delta=show_diff,
                                                  equippable=True)
            if backpack_pages:
                await BackpackMenu(
                    source=SimpleSource(backpack_pages),
                    help_command=self.commands_equipable_backpack,
                    delete_message_after=True,
                    clear_reactions_after=True,
                    timeout=60,
                ).start(ctx=ctx)
            else:
                return await smart_embed(
                    ctx,
                    _("You have no equippable items that match this query."),
                )

    @commands.group(name="cbackpack")
    @commands.bot_has_permissions(add_reactions=True)
    async def commands_cbackpack(
        self,
        ctx: commands.Context,
    ):
        """Complex backpack management tools.

        Please read the usage instructions [here](https://github.com/aikaterna/gobcog/blob/master/docs/cbackpack.md)
        """

    @commands_cbackpack.command(name="show")
    async def commands_cbackpack_show(
        self,
        ctx: commands.Context,
        *,
        query: BackpackFilterParser,
    ):
        """This shows the contents of your backpack.

        Please read the usage instructions [here](https://github.com/aikaterna/gobcog/blob/master/docs/cbackpack.md)
        """
        if not await self.allow_in_dm(ctx):
            return await smart_embed(
                ctx, _("This command is not available in DM's on this bot."))
        try:
            c = await Character.from_json(ctx, self.config, ctx.author,
                                          self._daily_bonus)
        except Exception as exc:
            log.exception("Error with the new character sheet", exc_info=exc)
            return
        backpack_pages = await c.get_argparse_backpack(query)
        if backpack_pages:
            await BackpackMenu(
                source=SimpleSource(backpack_pages),
                help_command=self.commands_cbackpack,
                delete_message_after=True,
                clear_reactions_after=True,
                timeout=60,
            ).start(ctx=ctx)
        else:
            return await smart_embed(
                ctx,
                _("You have no items that match this query."),
            )

    @commands_cbackpack.command(name="disassemble")
    async def commands_cbackpack_disassemble(self, ctx: commands.Context, *,
                                             query: BackpackFilterParser):
        """
        Disassemble items from your backpack.

        This will provide a chance for a chest,
        or the item might break while you are handling it...

        Please read the usage instructions [here](https://github.com/aikaterna/gobcog/blob/master/docs/cbackpack.md)
        """
        if self.in_adventure(ctx):
            return await smart_embed(
                ctx,
                _("You tried to disassemble an item but the monster ahead of you commands your attention."
                  ),
            )
        query.pop("degrade", None)  # Disallow selling by degrade levels
        async with self.get_lock(ctx.author):
            try:
                character = await Character.from_json(ctx, self.config,
                                                      ctx.author,
                                                      self._daily_bonus)
            except Exception as exc:
                log.exception("Error with the new character sheet",
                              exc_info=exc)
                return
            slots = await character.get_argparse_backpack_items(
                query, rarity_exclude=["forged"])
            if (total_items := sum(len(i) for s, i in slots)) > 2:

                msg = await ctx.send(
                    "Are you sure you want to disassemble {count} unique items and their duplicates?"
                    .format(count=humanize_number(total_items)))
                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:
                    await self._clear_react(msg)
                    return

                if not pred.result:
                    await ctx.send("Not disassembling those items.")
                    return
        failed = 0
        success = 0
        disassembled = set()

        async for slot_name, slot_group in AsyncIter(slots, steps=100):
            async for item_name, item in AsyncIter(slot_group, steps=100):
                try:
                    item = character.backpack[item.name]
                except KeyError:
                    continue
                if item.name in disassembled:
                    continue
                if item.rarity in ["forged"]:
                    failed += 1
                    continue
                index = min(RARITIES.index(item.rarity), 4)
                disassembled.add(item.name)
                owned = item.owned
                async for _loop_counter in AsyncIter(range(0, owned),
                                                     steps=100):
                    if character.heroclass["name"] != "Tinkerer":
                        roll = random.randint(0, 5)
                        chests = 1
                    else:
                        roll = random.randint(0, 3)
                        chests = random.randint(1, 2)
                    if roll != 0:
                        item.owned -= 1
                        if item.owned <= 0 and item.name in character.backpack:
                            del character.backpack[item.name]
                        failed += 1
                    else:
                        item.owned -= 1
                        if item.owned <= 0 and item.name in character.backpack:
                            del character.backpack[item.name]
                        character.treasure[index] += chests
                        success += 1
        if (not failed) and (not success):
            return await smart_embed(
                ctx,
                _("No items matched your query.").format(),
            )
        else:

            await self.config.user(ctx.author
                                   ).set(await
                                         character.to_json(ctx, self.config))
            return await smart_embed(
                ctx,
                _("You attempted to disassemble multiple items: {succ} were successful and {fail} failed."
                  ).format(succ=humanize_number(success),
                           fail=humanize_number(failed)),
            )
コード例 #25
0
    async def repboard(self, ctx, page_list: int = 1):
        """Show the reputation leaderboard.
        Give a number to show a page.
        """
        users = []
        title = "Global Rep Leaderboard for {}\n".format(self.bot.user.name)
        all_users = await self.data.all_users()
        if str(all_users) == "{}":
            await ctx.send(
                "The leaderboard is empty... Nobody's popular, for now.")
            return
        for user_id in all_users:
            user_name = await self._get_user(user_id)
            users.append((user_name, all_users[user_id]["points"]))
            if ctx.author.id == user_id:
                user_stat = all_users[user_id]["points"]

        board_type = "Rep"
        icon_url = self.bot.user.avatar_url
        sorted_list = sorted(users, key=operator.itemgetter(1), reverse=True)
        rank = 1
        for allusers in sorted_list:
            if ctx.author.name == allusers[0]:
                author_rank = rank
                break
            rank += 1
        footer_text = "Your Rank: {}                      {}: {}".format(
            author_rank, board_type, user_stat)

        # multiple page support
        page = 1
        per_page = 15
        pages = math.ceil(len(sorted_list) / per_page)
        if 1 <= page_list <= pages:
            page = page_list
        else:
            await ctx.send(
                "**Please enter a valid page number! (1 - {})**".format(
                    str(pages)))
            return

        msg = ""
        msg += "Rank     Name                   (Page {}/{})     \n\n".format(
            page, pages)
        rank = 1 + per_page * (page - 1)
        start_index = per_page * page - per_page
        end_index = per_page * page

        default_label = "  "
        special_labels = ["♔", "♕", "♖", "♗", "♘", "♙"]

        async for single_user in AsyncIter(sorted_list[start_index:end_index]):
            if rank - 1 < len(special_labels):
                label = special_labels[rank - 1]
            else:
                label = default_label

            msg += "{:<2}{:<2}{:<2} # {:<15}".format(
                rank, label, "➤", await
                self._truncate_text(single_user[0], 15))
            msg += "{:>5}{:<2}{:<2}{:<5}\n".format(
                " ", " ", " ",
                " {}: ".format(board_type) + str(single_user[1]))
            rank += 1
        msg += "--------------------------------------------            \n"
        msg += "{}".format(footer_text)

        em = discord.Embed(description="",
                           colour=await self.bot.get_embed_colour(ctx))
        em.set_author(name=title, icon_url=icon_url)
        em.description = box(msg)

        await ctx.send(embed=em)
コード例 #26
0
    async def _backpack_sell_button_action(self, ctx, emoji, page, item,
                                           price_shown, character):
        currency_name = await bank.get_currency_name(ctx.guild, )
        msg = ""
        if emoji == "\N{DIGIT ONE}\N{COMBINING ENCLOSING KEYCAP}":  # user reacted with one to sell.
            ctx.command.reset_cooldown(ctx)
            # sell one of the item
            price = 0
            item.owned -= 1
            price += price_shown
            msg += _(
                "**{author}** sold one {item} for {price} {currency_name}.\n"
            ).format(
                author=escape(ctx.author.display_name),
                item=box(item, lang="css"),
                price=humanize_number(price),
                currency_name=currency_name,
            )
            if item.owned <= 0:
                del character.backpack[item.name]
            price = max(price, 0)
            if price > 0:
                try:
                    await bank.deposit_credits(ctx.author, price)
                except BalanceTooHigh as e:
                    await bank.set_balance(ctx.author, e.max_balance)
        elif emoji == "\N{CLOCKWISE RIGHTWARDS AND LEFTWARDS OPEN CIRCLE ARROWS}":  # user wants to sell all owned.
            ctx.command.reset_cooldown(ctx)
            price = 0
            old_owned = item.owned
            count = 0
            async for _loop_counter in AsyncIter(range(0, item.owned),
                                                 steps=50):
                item.owned -= 1
                price += price_shown
                if item.owned <= 0:
                    del character.backpack[item.name]
                count += 1
            msg += _(
                "**{author}** sold all their {old_item} for {price} {currency_name}.\n"
            ).format(
                author=escape(ctx.author.display_name),
                old_item=box(str(item) + " - " + str(old_owned), lang="css"),
                price=humanize_number(price),
                currency_name=currency_name,
            )
            price = max(price, 0)
            if price > 0:
                try:
                    await bank.deposit_credits(ctx.author, price)
                except BalanceTooHigh as e:
                    await bank.set_balance(ctx.author, e.max_balance)
        elif (emoji ==
              "\N{CLOCKWISE RIGHTWARDS AND LEFTWARDS OPEN CIRCLE ARROWS WITH CIRCLED ONE OVERLAY}"
              ):  # user wants to sell all but one.
            if item.owned == 1:
                ctx.command.reset_cooldown(ctx)
                return await smart_embed(
                    ctx, _("You already only own one of those items."))
            price = 0
            old_owned = item.owned
            count = 0
            async for _loop_counter in AsyncIter(range(1, item.owned),
                                                 steps=50):
                item.owned -= 1
                price += price_shown
            count += 1
            if price != 0:
                msg += _(
                    "**{author}** sold all but one of their {old_item} for {price} {currency_name}.\n"
                ).format(
                    author=escape(ctx.author.display_name),
                    old_item=box(str(item) + " - " + str(old_owned - 1),
                                 lang="css"),
                    price=humanize_number(price),
                    currency_name=currency_name,
                )
                price = max(price, 0)
                if price > 0:
                    try:
                        await bank.deposit_credits(ctx.author, price)
                    except BalanceTooHigh as e:
                        await bank.set_balance(ctx.author, e.max_balance)
        else:  # user doesn't want to sell those items.
            await ctx.send(_("Not selling those items."))

        if msg:
            character.last_known_currency = await bank.get_balance(ctx.author)
            character.last_currency_check = time.time()
            await self.config.user(ctx.author
                                   ).set(await
                                         character.to_json(ctx, self.config))
            pages = [
                page for page in pagify(msg, delims=["\n"], page_length=1900)
            ]
            await BaseMenu(
                source=SimpleSource(pages),
                delete_message_after=True,
                clear_reactions_after=True,
                timeout=60,
            ).start(ctx=ctx)
コード例 #27
0
ファイル: playlists.py プロジェクト: PredaaA/drapercogs
    async def _load_v2_playlist(
        self,
        ctx: commands.Context,
        uploaded_track_list,
        player: lavalink.player_manager.Player,
        playlist_url: str,
        uploaded_playlist_name: str,
        scope: str,
        author: Union[discord.User, discord.Member],
        guild: Union[discord.Guild],
    ):
        track_list = []
        successful_count = 0
        uploaded_track_count = len(uploaded_track_list)

        embed1 = discord.Embed(title=_("Please wait, adding tracks..."))
        playlist_msg = await self.send_embed_msg(ctx, embed=embed1)
        notifier = Notifier(ctx, playlist_msg,
                            {"playlist": _("Loading track {num}/{total}...")})
        async for track_count, song_url in AsyncIter(
                uploaded_track_list).enumerate(start=1):
            try:
                try:
                    result, called_api = await self.api_interface.fetch_track(
                        ctx, player,
                        Query.process_input(song_url,
                                            self.local_folder_current_path))
                except TrackEnqueueError:
                    self.update_player_lock(ctx, False)
                    return await self.send_embed_msg(
                        ctx,
                        title=_("Unable to Get Track"),
                        description=_(
                            "I'm unable to get a track from Lavalink at the moment, "
                            "try again in a few minutes."),
                    )
                except Exception as e:
                    self.update_player_lock(ctx, False)
                    raise e

                track = result.tracks[0]
            except Exception as err:
                debug_exc_log(log, err, f"Failed to get track for {song_url}")
                continue
            try:
                track_obj = self.get_track_json(player, other_track=track)
                track_list.append(track_obj)
                successful_count += 1
            except Exception as err:
                debug_exc_log(log, err, f"Failed to create track for {track}")
                continue
            if (track_count % 2 == 0) or (track_count
                                          == len(uploaded_track_list)):
                await notifier.notify_user(current=track_count,
                                           total=len(uploaded_track_list),
                                           key="playlist")
        playlist = await create_playlist(
            ctx,
            self.playlist_api,
            scope,
            uploaded_playlist_name,
            playlist_url,
            track_list,
            author,
            guild,
        )
        scope_name = self.humanize_scope(
            scope, ctx=guild if scope == PlaylistScope.GUILD.value else author)
        if not successful_count:
            msg = _("Empty playlist {name} (`{id}`) [**{scope}**] created."
                    ).format(name=playlist.name,
                             id=playlist.id,
                             scope=scope_name)
        elif uploaded_track_count != successful_count:
            bad_tracks = uploaded_track_count - successful_count
            msg = _(
                "Added {num} tracks from the {playlist_name} playlist. {num_bad} track(s) "
                "could not be loaded.").format(num=successful_count,
                                               playlist_name=playlist.name,
                                               num_bad=bad_tracks)
        else:
            msg = _("Added {num} tracks from the {playlist_name} playlist."
                    ).format(num=successful_count, playlist_name=playlist.name)
        embed3 = discord.Embed(colour=await ctx.embed_colour(),
                               title=_("Playlist Saved"),
                               description=msg)
        await playlist_msg.edit(embed=embed3)
コード例 #28
0
    async def timerole_update(self):
        utcnow = datetime.utcnow()
        all_guilds = await self.config.all_guilds()

        # all_mrs = await self.config.custom("RoleMember").all()

        # log.debug(f"Begin timerole update")

        for guild in self.bot.guilds:
            guild_id = guild.id
            if guild_id not in all_guilds:
                log.debug(f"Guild has no configured settings: {guild}")
                continue

            add_results = ""
            remove_results = ""
            reapply = all_guilds[guild_id]["reapply"]
            role_dict = all_guilds[guild_id]["roles"]

            if not any(role_dict.values()):  # No roles
                log.debug(f"No roles are configured for guild: {guild}")
                continue

            # all_mr = await self.config.all_custom("RoleMember")
            # log.debug(f"{all_mr=}")

            async for member in AsyncIter(guild.members, steps=10):
                addlist = []
                removelist = []

                for role_id, role_data in role_dict.items():
                    # Skip non-configured roles
                    if not role_data:
                        continue

                    mr_dict = await self.config.custom("RoleMember", role_id, member.id).all()

                    # Stop if they've had the role and reapplying is disabled
                    if not reapply and mr_dict["had_role"]:
                        log.debug(f"{member.display_name} - Not reapplying")
                        continue

                    # Stop if the check_again_time hasn't passed yet
                    if (
                        mr_dict["check_again_time"] is not None
                        and datetime.fromisoformat(mr_dict["check_again_time"]) >= utcnow
                    ):
                        log.debug(f"{member.display_name} - Not time to check again yet")
                        continue
                    member: discord.Member
                    has_roles = {r.id for r in member.roles}

                    # Stop if they currently have or don't have the role, and mark had_role
                    if (int(role_id) in has_roles and not role_data["remove"]) or (
                        int(role_id) not in has_roles and role_data["remove"]
                    ):
                        if not mr_dict["had_role"]:
                            await self.config.custom(
                                "RoleMember", role_id, member.id
                            ).had_role.set(True)
                        log.debug(f"{member.display_name} - applying had_role")
                        continue

                    # Stop if they don't have all the required roles
                    if role_data is None or (
                        "required" in role_data and not set(role_data["required"]) & has_roles
                    ):
                        continue

                    check_time = member.joined_at + timedelta(
                        days=role_data["days"],
                        hours=role_data.get("hours", 0),
                    )

                    # Check if enough time has passed to get the role and save the check_again_time
                    if check_time >= utcnow:
                        await self.config.custom(
                            "RoleMember", role_id, member.id
                        ).check_again_time.set(check_time.isoformat())
                        log.debug(
                            f"{member.display_name} - Not enough time has passed to qualify for the role\n"
                            f"Waiting until {check_time}"
                        )
                        continue

                    if role_data["remove"]:
                        removelist.append(role_id)
                    else:
                        addlist.append(role_id)

                # Done iterating through roles, now add or remove the roles
                if not addlist and not removelist:
                    continue

                # log.debug(f"{addlist=}\n{removelist=}")
                add_roles = [
                    discord.utils.get(guild.roles, id=int(role_id)) for role_id in addlist
                ]
                remove_roles = [
                    discord.utils.get(guild.roles, id=int(role_id)) for role_id in removelist
                ]

                if None in add_roles or None in remove_roles:
                    log.info(
                        f"Timerole ran into an error with the roles in: {add_roles + remove_roles}"
                    )

                if addlist:
                    try:
                        await member.add_roles(*add_roles, reason="Timerole", atomic=False)
                    except (discord.Forbidden, discord.NotFound) as e:
                        log.exception("Failed Adding Roles")
                        add_results += f"{member.display_name} : **(Failed Adding Roles)**\n"
                    else:
                        add_results += " \n".join(
                            f"{member.display_name} : {role.name}" for role in add_roles
                        )
                        for role_id in addlist:
                            await self.config.custom(
                                "RoleMember", role_id, member.id
                            ).had_role.set(True)

                if removelist:
                    try:
                        await member.remove_roles(*remove_roles, reason="Timerole", atomic=False)
                    except (discord.Forbidden, discord.NotFound) as e:
                        log.exception("Failed Removing Roles")
                        remove_results += f"{member.display_name} : **(Failed Removing Roles)**\n"
                    else:
                        remove_results += " \n".join(
                            f"{member.display_name} : {role.name}" for role in remove_roles
                        )
                        for role_id in removelist:
                            await self.config.custom(
                                "RoleMember", role_id, member.id
                            ).had_role.set(True)

            # Done iterating through members, now maybe announce to the guild
            channel = await self.config.guild(guild).announce()
            if channel is not None:
                channel = guild.get_channel(channel)

            if add_results:
                title = "**These members have received the following roles**\n"
                await announce_to_channel(channel, add_results, title)
            if remove_results:
                title = "**These members have lost the following roles**\n"
                await announce_to_channel(channel, remove_results, title)
コード例 #29
0
ファイル: playlists.py プロジェクト: PredaaA/drapercogs
    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)]
        close_emoji = self.get_cross_emoji(context)
        emojis.append(close_emoji)
        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] == close_emoji:
            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
ファイル: formatting.py プロジェクト: PredaaA/drapercogs
 async def _build_search_page(self, ctx: commands.Context, tracks: List,
                              page_num: int) -> discord.Embed:
     search_num_pages = math.ceil(len(tracks) / 5)
     search_idx_start = (page_num - 1) * 5
     search_idx_end = search_idx_start + 5
     search_list = ""
     command = ctx.invoked_with
     folder = False
     async for i, track in AsyncIter(
             tracks[search_idx_start:search_idx_end]).enumerate(
                 start=search_idx_start):
         search_track_num = i + 1
         if search_track_num > 5:
             search_track_num = search_track_num % 5
         if search_track_num == 0:
             search_track_num = 5
         try:
             query = Query.process_input(track.uri,
                                         self.local_folder_current_path)
             if query.is_local:
                 search_list += "`{0}.` **{1}**\n[{2}]\n".format(
                     search_track_num,
                     track.title,
                     LocalPath(
                         track.uri,
                         self.local_folder_current_path).to_string_user(),
                 )
             else:
                 search_list += "`{0}.` **[{1}]({2})**\n".format(
                     search_track_num, track.title, track.uri)
         except AttributeError:
             track = Query.process_input(track,
                                         self.local_folder_current_path)
             if track.is_local and command != "search":
                 search_list += "`{}.` **{}**\n".format(
                     search_track_num, track.to_string_user())
                 if track.is_album:
                     folder = True
             else:
                 search_list += "`{}.` **{}**\n".format(
                     search_track_num, track.to_string_user())
     if hasattr(tracks[0], "uri") and hasattr(tracks[0],
                                              "track_identifier"):
         title = _("Tracks Found:")
         footer = _("search results")
     elif folder:
         title = _("Folders Found:")
         footer = _("local folders")
     else:
         title = _("Files Found:")
         footer = _("local tracks")
     embed = discord.Embed(colour=await ctx.embed_colour(),
                           title=title,
                           description=search_list)
     embed.set_footer(text=(_("Page {page_num}/{total_pages}") +
                            " | {num_results} {footer}").format(
                                page_num=page_num,
                                total_pages=search_num_pages,
                                num_results=len(tracks),
                                footer=footer,
                            ))
     return embed