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)
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()
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)
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)
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)
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