示例#1
0
    async def pay_winners(self, multiplier: float):
        """Pay the winner(s) of this trivia session.

        Payout only occurs if there are at least 3 human contestants.
        If a tie occurs the payout is split evenly among the winners.

        Parameters
        ----------
        multiplier : float
            The coefficient of the winning score, used to determine the amount
            paid.

        """
        if not self.scores:
            return
        top_score = self.scores.most_common(1)[0][1]
        winners = []
        num_humans = 0
        for (player, score) in self.scores.items():
            if not player.bot:
                if score == top_score:
                    winners.append(player)
                num_humans += 1
        if not winners or num_humans < 3:
            return
        payout = int(top_score * multiplier / len(winners))
        if payout <= 0:
            return
        for winner in winners:
            LOG.debug("Paying trivia winner: %d credits --> %s", payout,
                      winner.name)
            try:
                await bank.deposit_credits(winner, payout)
            except errors.BalanceTooHigh as e:
                await bank.set_balance(winner, e.max_balance)
        if len(winners) > 1:
            msg = _(
                "Congratulations {users}! You have each received {num} {currency} for winning!"
            ).format(
                users=humanize_list(
                    [bold(winner.display_name) for winner in winners]),
                num=payout,
                currency=await bank.get_currency_name(self.ctx.guild),
            )
        else:
            msg = _(
                "Congratulations {user}! You have received {num} {currency} for winning!"
            ).format(
                user=bold(winners[0].display_name),
                num=payout,
                currency=await bank.get_currency_name(self.ctx.guild),
            )
        await self.ctx.send(msg)
示例#2
0
    async def run(self):
        """Run the trivia session.

        In order for the trivia session to be stopped correctly, this should
        only be called internally by `TriviaSession.start`.
        """
        await self._send_startup_msg()
        max_score = self.settings["max_score"]
        delay = self.settings["delay"]
        timeout = self.settings["timeout"]
        for question, answers in self._iter_questions():
            async with self.ctx.typing():
                await asyncio.sleep(3)
            self.count += 1
            msg = bold(_("Question number {num}!").format(
                num=self.count)) + "\n\n" + question
            await self.ctx.send(msg)
            continue_ = await self.wait_for_answer(answers, delay, timeout)
            if continue_ is False:
                break
            if any(score >= max_score for score in self.scores.values()):
                await self.end_game()
                break
        else:
            await self.ctx.send(_("There are no more questions!"))
            await self.end_game()
示例#3
0
    async def custom_trivia_list(self, ctx: commands.Context):
        """List uploaded custom trivia."""
        personal_lists = sorted(
            [p.resolve().stem for p in cog_data_path(self).glob("*.yaml")])
        no_lists_uploaded = _("No custom Trivia lists uploaded.")

        if not personal_lists:
            if await ctx.embed_requested():
                await ctx.send(
                    embed=discord.Embed(colour=await ctx.embed_colour(),
                                        description=no_lists_uploaded))
            else:
                await ctx.send(no_lists_uploaded)
            return

        if await ctx.embed_requested():
            await ctx.send(embed=discord.Embed(
                title=_("Uploaded trivia lists"),
                colour=await ctx.embed_colour(),
                description=", ".join(sorted(personal_lists)),
            ))
        else:
            msg = box(
                bold(_("Uploaded trivia lists")) + "\n\n" +
                ", ".join(sorted(personal_lists)))
            if len(msg) > 1000:
                await ctx.author.send(msg)
            else:
                await ctx.send(msg)
示例#4
0
 async def trivia_list(self, ctx: commands.Context):
     """List available trivia categories."""
     lists = set(p.stem for p in self._all_lists())
     if await ctx.embed_requested():
         await ctx.send(embed=discord.Embed(
             title=_("Available trivia lists"),
             colour=await ctx.embed_colour(),
             description=", ".join(sorted(lists)),
         ))
     else:
         msg = box(
             bold(_("Available trivia lists")) + "\n\n" +
             ", ".join(sorted(lists)))
         if len(msg) > 1000:
             await ctx.author.send(msg)
         else:
             await ctx.send(msg)
示例#5
0
    async def serverinfo(self, ctx, details: bool = False):
        """
        Show server information.

        `details`: Shows more information when set to `True`.
        Default to False.
        """
        guild = ctx.guild
        passed = (ctx.message.created_at - guild.created_at).days
        created_at = _(
            "Created on {date}. That's over {num} days ago!").format(
                date=guild.created_at.strftime("%d %b %Y %H:%M"),
                num=humanize_number(passed),
            )
        online = humanize_number(
            len([
                m.status for m in guild.members
                if m.status != discord.Status.offline
            ]))
        total_users = humanize_number(guild.member_count)
        text_channels = humanize_number(len(guild.text_channels))
        voice_channels = humanize_number(len(guild.voice_channels))
        if not details:
            data = discord.Embed(description=created_at,
                                 colour=await ctx.embed_colour())
            data.add_field(name=_("Region"), value=str(guild.region))
            data.add_field(name=_("Users online"),
                           value=f"{online}/{total_users}")
            data.add_field(name=_("Text Channels"), value=text_channels)
            data.add_field(name=_("Voice Channels"), value=voice_channels)
            data.add_field(name=_("Roles"),
                           value=humanize_number(len(guild.roles)))
            data.add_field(name=_("Owner"), value=str(guild.owner))
            data.set_footer(
                text=_("Server ID: ") + str(guild.id) +
                _("  •  Use {command} for more info on the server.").format(
                    command=f"{ctx.clean_prefix}serverinfo 1"))
            if guild.icon_url:
                data.set_author(name=guild.name, url=guild.icon_url)
                data.set_thumbnail(url=guild.icon_url)
            else:
                data.set_author(name=guild.name)
        else:

            def _size(num: int):
                for unit in ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB"]:
                    if abs(num) < 1024.0:
                        return "{0:.1f}{1}".format(num, unit)
                    num /= 1024.0
                return "{0:.1f}{1}".format(num, "YB")

            def _bitsize(num: int):
                for unit in ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB"]:
                    if abs(num) < 1000.0:
                        return "{0:.1f}{1}".format(num, unit)
                    num /= 1000.0
                return "{0:.1f}{1}".format(num, "YB")

            shard_info = (_("\nShard ID: **{shard_id}/{shard_count}**").format(
                shard_id=humanize_number(guild.shard_id + 1),
                shard_count=humanize_number(ctx.bot.shard_count),
            ) if ctx.bot.shard_count > 1 else "")
            # Logic from: https://github.com/TrustyJAID/Trusty-cogs/blob/master/serverstats/serverstats.py#L159
            online_stats = {
                _("Humans: "):
                lambda x: not x.bot,
                _(" • Bots: "):
                lambda x: x.bot,
                "\N{LARGE GREEN CIRCLE}":
                lambda x: x.status is discord.Status.online,
                "\N{LARGE ORANGE CIRCLE}":
                lambda x: x.status is discord.Status.idle,
                "\N{LARGE RED CIRCLE}":
                lambda x: x.status is discord.Status.do_not_disturb,
                "\N{MEDIUM WHITE CIRCLE}\N{VARIATION SELECTOR-16}":
                lambda x: (x.status is discord.Status.offline),
                "\N{LARGE PURPLE CIRCLE}":
                lambda x: any(a.type is discord.ActivityType.streaming
                              for a in x.activities),
                "\N{MOBILE PHONE}":
                lambda x: x.is_on_mobile(),
            }
            member_msg = _(
                "Users online: **{online}/{total_users}**\n").format(
                    online=online, total_users=total_users)
            count = 1
            for emoji, value in online_stats.items():
                try:
                    num = len([m for m in guild.members if value(m)])
                except Exception as error:
                    print(error)
                    continue
                else:
                    member_msg += f"{emoji} {bold(humanize_number(num))} " + (
                        "\n" if count % 2 == 0 else "")
                count += 1

            vc_regions = {
                "vip-us-east": _("__VIP__ US East ") + "\U0001F1FA\U0001F1F8",
                "vip-us-west": _("__VIP__ US West ") + "\U0001F1FA\U0001F1F8",
                "vip-amsterdam":
                _("__VIP__ Amsterdam ") + "\U0001F1F3\U0001F1F1",
                "eu-west": _("EU West ") + "\U0001F1EA\U0001F1FA",
                "eu-central": _("EU Central ") + "\U0001F1EA\U0001F1FA",
                "europe": _("Europe ") + "\U0001F1EA\U0001F1FA",
                "london": _("London ") + "\U0001F1EC\U0001F1E7",
                "frankfurt": _("Frankfurt ") + "\U0001F1E9\U0001F1EA",
                "amsterdam": _("Amsterdam ") + "\U0001F1F3\U0001F1F1",
                "us-west": _("US West ") + "\U0001F1FA\U0001F1F8",
                "us-east": _("US East ") + "\U0001F1FA\U0001F1F8",
                "us-south": _("US South ") + "\U0001F1FA\U0001F1F8",
                "us-central": _("US Central ") + "\U0001F1FA\U0001F1F8",
                "singapore": _("Singapore ") + "\U0001F1F8\U0001F1EC",
                "sydney": _("Sydney ") + "\U0001F1E6\U0001F1FA",
                "brazil": _("Brazil ") + "\U0001F1E7\U0001F1F7",
                "hongkong": _("Hong Kong ") + "\U0001F1ED\U0001F1F0",
                "russia": _("Russia ") + "\U0001F1F7\U0001F1FA",
                "japan": _("Japan ") + "\U0001F1EF\U0001F1F5",
                "southafrica": _("South Africa ") + "\U0001F1FF\U0001F1E6",
                "india": _("India ") + "\U0001F1EE\U0001F1F3",
                "dubai": _("Dubai ") + "\U0001F1E6\U0001F1EA",
                "south-korea": _("South Korea ") + "\U0001f1f0\U0001f1f7",
            }
            verif = {
                "none": _("0 - None"),
                "low": _("1 - Low"),
                "medium": _("2 - Medium"),
                "high": _("3 - High"),
                "extreme": _("4 - Extreme"),
            }

            features = {
                "ANIMATED_ICON":
                _("Animated Icon"),
                "BANNER":
                _("Banner Image"),
                "COMMERCE":
                _("Commerce"),
                "COMMUNITY":
                _("Community"),
                "DISCOVERABLE":
                _("Server Discovery"),
                "FEATURABLE":
                _("Featurable"),
                "INVITE_SPLASH":
                _("Splash Invite"),
                "MEMBER_LIST_DISABLED":
                _("Member list disabled"),
                "MEMBER_VERIFICATION_GATE_ENABLED":
                _("Membership Screening enabled"),
                "MORE_EMOJI":
                _("More Emojis"),
                "NEWS":
                _("News Channels"),
                "PARTNERED":
                _("Partnered"),
                "PREVIEW_ENABLED":
                _("Preview enabled"),
                "PUBLIC_DISABLED":
                _("Public disabled"),
                "VANITY_URL":
                _("Vanity URL"),
                "VERIFIED":
                _("Verified"),
                "VIP_REGIONS":
                _("VIP Voice Servers"),
                "WELCOME_SCREEN_ENABLED":
                _("Welcome Screen enabled"),
            }
            guild_features_list = [
                f"\N{WHITE HEAVY CHECK MARK} {name}"
                for feature, name in features.items()
                if feature in guild.features
            ]

            joined_on = _(
                "{bot_name} joined this server on {bot_join}. That's over {since_join} days ago!"
            ).format(
                bot_name=ctx.bot.user.name,
                bot_join=guild.me.joined_at.strftime("%d %b %Y %H:%M:%S"),
                since_join=humanize_number(
                    (ctx.message.created_at - guild.me.joined_at).days),
            )

            data = discord.Embed(
                description=(f"{guild.description}\n\n"
                             if guild.description else "") + created_at,
                colour=await ctx.embed_colour(),
            )
            data.set_author(
                name=guild.name,
                icon_url=
                "https://cdn.discordapp.com/emojis/457879292152381443.png"
                if "VERIFIED" in guild.features else
                "https://cdn.discordapp.com/emojis/508929941610430464.png"
                if "PARTNERED" in guild.features else discord.Embed.Empty,
            )
            if guild.icon_url:
                data.set_thumbnail(url=guild.icon_url)
            data.add_field(name=_("Members:"), value=member_msg)
            data.add_field(
                name=_("Channels:"),
                value=_("\N{SPEECH BALLOON} Text: {text}\n"
                        "\N{SPEAKER WITH THREE SOUND WAVES} Voice: {voice}").
                format(text=bold(text_channels), voice=bold(voice_channels)),
            )
            data.add_field(
                name=_("Utility:"),
                value=_(
                    "Owner: {owner}\nVoice region: {region}\nVerif. level: {verif}\nServer ID: {id}{shard_info}"
                ).format(
                    owner=bold(str(guild.owner)),
                    region=
                    f"**{vc_regions.get(str(guild.region)) or str(guild.region)}**",
                    verif=bold(verif[str(guild.verification_level)]),
                    id=bold(str(guild.id)),
                    shard_info=shard_info,
                ),
                inline=False,
            )
            data.add_field(
                name=_("Misc:"),
                value=
                _("AFK channel: {afk_chan}\nAFK timeout: {afk_timeout}\nCustom emojis: {emoji_count}\nRoles: {role_count}"
                  ).format(
                      afk_chan=bold(str(guild.afk_channel))
                      if guild.afk_channel else bold(_("Not set")),
                      afk_timeout=bold(
                          humanize_timedelta(seconds=guild.afk_timeout)),
                      emoji_count=bold(humanize_number(len(guild.emojis))),
                      role_count=bold(humanize_number(len(guild.roles))),
                  ),
                inline=False,
            )
            if guild_features_list:
                data.add_field(name=_("Server features:"),
                               value="\n".join(guild_features_list))
            if guild.premium_tier != 0:
                nitro_boost = _(
                    "Tier {boostlevel} with {nitroboosters} boosts\n"
                    "File size limit: {filelimit}\n"
                    "Emoji limit: {emojis_limit}\n"
                    "VCs max bitrate: {bitrate}").format(
                        boostlevel=bold(str(guild.premium_tier)),
                        nitroboosters=bold(
                            humanize_number(guild.premium_subscription_count)),
                        filelimit=bold(_size(guild.filesize_limit)),
                        emojis_limit=bold(str(guild.emoji_limit)),
                        bitrate=bold(_bitsize(guild.bitrate_limit)),
                    )
                data.add_field(name=_("Nitro Boost:"), value=nitro_boost)
            if guild.splash:
                data.set_image(url=guild.splash_url_as(format="png"))
            data.set_footer(text=joined_on)

        await ctx.send(embed=data)
示例#6
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