async def on_guild_join(self, guild: Guild): """Send welcome message on guild join""" channel = None if guild.system_channel is not None: channel: TextChannel = guild.system_channel # Try to use system message channel else: y = find( lambda x: x.name == "general", guild.text_channels ) # if that doesn't exist, try to find a 'general' channel if y: channel: TextChannel = y if channel is not None: embed = StyledEmbed( title="Welcome to chime", description= ":wave: Thanks for having me!\n\nchime is a versatile, yet intuitive music bot for discord. It aims to have the best performance while being as user-friendly as possible. \n\n" "chime sports a **webinterface where you can manage settings for your server and create and manage personal playlists.** [Check it out here](https://chime.realmayus.xyz). \n" "With using chime you agree to our **[Terms of Service](https://chime.realmayus.xyz/terms)** and our **[Privacy Policy](https://chime.realmayus.xyz/privacy)**.\n" "**More info and invite link [here](https://chime.realmayus.xyz)**\n\n**See all available commands with** `" + prefix + "help`") embed.set_image( url= "https://raw.githubusercontent.com/realmayus/chime/master/assets/chime_banner.png?token=AJC6B5VTHEZ5UHNY7QNDCU263LCCK" ) await channel.send(embed=embed)
def get(self) -> Embed: embed = StyledEmbed(title="Search results (Page " + str(self.current_page + 1) + ")") description, count = get_song_selector_embed_desc_for_current_page( self.current_page, self.results) self.count = count embed.description = description return embed
async def success_callback_url(tracks): if not player.is_connected: await ctx.invoke(self.join) # Join channel if not connected if isinstance(tracks, TrackPlaylist): tracks = tracks.tracks await ctx.send(embed=StyledEmbed(description=f"**Added** {len(tracks)} **tracks to queue.**")) else: try: await ctx.send(embed=StyledEmbed(description=f"**Added** {tracks[0]} **to queue.**")) except TypeError: raise BadRequestException("Couldn't add this item to the queue!") for track in tracks: self.get_controller(ctx).queue.append(track)
async def handle_timeout(self): await self.last_msg.clear_reactions() expired_embed = StyledEmbed( title="Expired", description= "This song selector has expired because no one selected a song.") await self.last_msg.edit(embed=expired_embed, delete_after=15.0)
async def success_callback(track_, last_msg: Message): await last_msg.clear_reactions() await last_msg.edit(embed=StyledEmbed( description= f"**Added** {track_} **to playlist {playlist}.**"), delete_after=10.0) tracks_to_add.append(track_)
async def pause(self, ctx): """Pauses the current track""" player = self.bot.wavelink.get_player(ctx.guild.id) if player.is_playing and not player.is_paused: await player.set_pause(True) await ctx.send(embed=StyledEmbed(description=f"Stopped song! Use `{prefix}resume` to resume it."), delete_after=20.0) else: raise BadRequestException("I am currently not playing any track!")
async def playlists(self, ctx: Context): """Shows a list of all your playlists. Alias of `""" + prefix + """playlist list`""" playlists = self.database.get_all_playlists(ctx.author.id) await ctx.send(embed=StyledEmbed( title="Your playlists", description= f"use the `{prefix}playlist` commands for adding songs to a playlist, creating playlists, viewing the playlist's contents etc. \n\n" + "\n".join([f"• **{playlist['name']}**" for playlist in playlists])))
async def success_callback_url(tracks): nonlocal tracks_to_add if isinstance(tracks, TrackPlaylist): tracks = tracks.tracks await ctx.send(embed=StyledEmbed( description= f"**Added** {len(tracks)} **tracks to playlist {playlist}.**" ), delete_after=10.0) else: try: await ctx.send(embed=StyledEmbed( description= f"**Added** {tracks[0]} **to playlist {playlist}.**" ), delete_after=10.0) except TypeError: raise BadRequestException( "Couldn't add this item to the queue!") tracks_to_add = tracks
def get(self) -> Embed: embed = StyledEmbed( suppress_tips=True, title=self.title + " (Page " + str(self.current_page + 1) + "/" + str(math.ceil(len(self.contents) / self.show_per_page)) + ")") desc = "" count = 1 for track_index in range(len(self.contents)): try: track = self.contents[self.current_page * self.show_per_page + track_index] if count == self.show_per_page + 1: break desc += str(track) + "\n" count += 1 except IndexError: pass self.count = count embed.description = desc return embed
async def on_voice_state_update(self, member: Member, before: VoiceState, after: VoiceState): player: Player = self.bot.wavelink.get_player(member.guild.id) controller: MusicController = self.get_controller(player) if not before.channel: return if before.channel.id == player.channel_id: """It's actually the bot's channel!""" if not after.channel or after.channel != before.channel: """Member has left or switched the channel""" channel: VoiceChannel = before.channel if len(channel.members) <= 1: embed = StyledEmbed(suppress_tips=True, description="**I left the channel due to inactivity.**") await controller.channel.send(embed=embed) if controller.now_playing_msg: await controller.now_playing_msg.delete() try: controller.task.cancel() del self.bot.controllers[member.guild.id] del controller except Exception as e: print(e) await player.stop() await player.disconnect()
async def send_bot_help(self, mapping): embed = StyledEmbed(title='chime help') embed.set_thumbnail(url="https://raw.githubusercontent.com/realmayus/chime/master/assets/chime_banner.png?token=AJC6B5VTHEZ5UHNY7QNDCU263LCCK") embed.description = "chime is a versatile, yet intuitive music bot for discord. It aims to be as user-friendly as possible while still boasting many features. \n\n" \ "**More info and invite link [here](https://chime.realmayus.xyz)** \n\n" \ "Chime has a **web app** where you can manage and set up personal playlists and manage settings of your servers! https://chime.realmayus.xyz \n\n" \ "**Use** `" + self.clean_prefix + "help [command]` **for more info on a command.**" for cog, commands in mapping.items(): if cog is not None: # We don't want commands without categories! >:c name = cog.qualified_name filtered = await self.filter_commands(commands, sort=True) if filtered: builder = [] for command in commands: # filtering out hidden commands command: Command builder.append(f"`{prefix + command.name}`" if not command.hidden else "") value = ' '.join(builder) if cog and cog.description: value = '{0}\n{1}'.format(cog.description, value) embed.add_field(name=name, value=value) await self.get_destination().send(embed=embed)
async def on_command_error(self, ctx, error): """A local error handler for all errors arising from commands in this cog.""" if isinstance(error, commands.NoPrivateMessage): try: return await ctx.send(embed=StyledEmbed( description= "<:warning:746377344393936997> This command can't be executed in DMs.'" )) except discord.HTTPException: pass elif isinstance( error, discord.ext.commands.errors.CommandInvokeError) and isinstance( error.original, chime.misc.BadRequestException.BadRequestException): return await ctx.send(embed=StyledEmbed( description='<:warning:746377344393936997> ' + str(error.original.text))) elif isinstance(error, discord.ext.commands.errors.MissingRequiredArgument): return await ctx.send(embed=StyledEmbed( description='<:warning:746377344393936997> ' + str(error))) elif isinstance(error, discord.ext.commands.errors.BadArgument): return await ctx.send(embed=StyledEmbed( description='<:warning:746377344393936997> ' + str(error))) elif isinstance(error, discord.ext.commands.errors.CommandOnCooldown): return await ctx.send(embed=StyledEmbed( description='<:warning:746377344393936997> ' + str(error))) elif isinstance(error, discord.ext.commands.errors.CommandNotFound): return elif isinstance( error, discord.ext.commands.errors.CommandInvokeError) and isinstance( error.original, wavelink.errors.ZeroConnectedNodes): report_channel_ = await self.bot.fetch_channel(report_channel) error_embed = StyledEmbed( suppress_tips=True, title="<:warning:746377344393936997> Outage Report") error_embed.description = "Chime detected an outage:\n\n" + "```" + '\n'.join( [ line.strip('\n') for line in traceback.format_exception( type(error), error, error.__traceback__, limit=1) ]) + "```" error_embed.set_author(name="Automatic Outage Report") await report_channel_.send("<@&718113149651255386>", embed=error_embed) return await ctx.send(embed=StyledEmbed( description= '<:warning:746377344393936997> A critical outage has been detected and the developers **have been notified**. Sorry! You can get support here: \nhttps://discord.gg/DGd8T53' )) try: await ctx.send(embed=StyledEmbed( description= "<:warning:746377344393936997> Sorry, an unknown error occurred whilst executing this command. The error has been reported automatically. You can get support here: \nhttps://discord.gg/DGd8T53" )) except: pass print('Ignoring exception in command {}:'.format(ctx.command), file=sys.stderr) traceback.print_exception(type(error), error, error.__traceback__, file=sys.stderr) channel = await self.bot.fetch_channel(report_channel) error_embed = StyledEmbed( suppress_tips=True, title=f"<:warning:746377344393936997> `{type(error)}`") error_embed.set_author(name="Unhandled Error") # upload to hastebin key = json.loads( requests.post('https://hasteb.in/documents', data='Ignoring Exception in command ' + str(ctx.command) + ":\n\n" + '\n'.join([ line.strip('\n') for line in traceback.format_exception( type(error), error, error.__traceback__) ])).text)["key"] # send to auto-reports channel in chime lounge error_embed.description = f"chime witnessed an [unhandled exception](https://hasteb.in/{key}) whilst executing command `{ctx.command}`:\n\n```" + '\n'.join( [ line.strip('\n') for line in traceback.format_exception( type(error), error, error.__traceback__, limit=1) ]) + "```" await channel.send(embed=error_embed)
async def send_group_help(self, group: CustomCommand): embed = StyledEmbed(title='`' + group.qualified_name + '`') desc = "" if group.help: desc += group.help if group.usage: embed.add_field(name="**Usage**", value=f"`{prefix + group.usage}`", inline=False) if group.aliases and len(group.aliases) > 0: embed.add_field(name="**Aliases**", value=' '.join([f"`{prefix + alias}`" for alias in group.aliases]), inline=False) if hasattr(group, "available_args") and group.available_args: arg_builder = "" for typ in group.available_args: arg_builder += f"\n**{typ['type']}**" for arg in typ['args']: arg_builder += f"\n`{arg['name']}`\n***{arg['desc']}***" embed.add_field(name="**Arguments**", value=arg_builder) if hasattr(group, "examples") and group.examples: example_builder = "" for ex in group.examples: example_builder += f"\n`{ex['ex']}`\n{ex['desc']}" embed.add_field(name="**Examples**", value=example_builder) embed.description = desc await self.get_destination().send(embed=embed)
async def playlist(self, ctx: Context, action: str, playlist: str = None, *, additional_args=None): """Manage all your personal playlists. You can also manage them on [chime's web app](https://chime.realmayus.xyz)""" if action == "create": if not additional_args: # if the playlist name contained spaces, the individual parts would be in additional_args self.database.create_playlist(ctx.author.id, playlist) await ctx.message.add_reaction("<:ok:746377326245445653>") else: raise BadRequestException( "If you want spaces in your playlist's name, you have to wrap it in quotation marks!" ) elif action == "show" or action == "view": if playlist is not None: contents = self.database.get_playlist_contents( ctx.author.id, playlist) if len(contents) == 0: raise BadRequestException("Playlist is empty!") embed = PagedListEmbed(f"Contents of `{playlist}`", [ f"{i + 1}. {song['title']}" for i, song in enumerate(contents) ], ctx, self.bot) await embed.send(embed.get()) else: raise BadRequestException("Please enter a playlist name!") elif action == "play": if playlist is not None: contents = self.database.get_playlist_contents( ctx.author.id, playlist) await self.join_channel(ctx) if len(contents) == 0: raise BadRequestException("Playlist is empty!") index = 0 failed = 0 for index, song_data_raw in enumerate(contents): try: track = await self.bot.wavelink.build_track( song_data_raw["data"]) controller = self.get_controller(ctx) controller.queue.append(track) except BuildTrackError: failed += 1 print("Failed to reconstruct track with data " + song_data_raw["data"]) await ctx.send(embed=StyledEmbed( description=f"**Added** {index + 1} **tracks to queue**.")) if failed > 0: raise BadRequestException( f"**Failed to add** {failed} **track(s)**!") else: raise BadRequestException("Please enter a playlist name!") elif action == "list": await ctx.invoke(self.playlists) elif action == "add": tracks_to_add = [] if playlist is None: raise BadRequestException("Please enter a playlist name!") async def success_callback(track_, last_msg: Message): await last_msg.clear_reactions() await last_msg.edit(embed=StyledEmbed( description= f"**Added** {track_} **to playlist {playlist}.**"), delete_after=10.0) tracks_to_add.append(track_) async def success_callback_url(tracks): nonlocal tracks_to_add if isinstance(tracks, TrackPlaylist): tracks = tracks.tracks await ctx.send(embed=StyledEmbed( description= f"**Added** {len(tracks)} **tracks to playlist {playlist}.**" ), delete_after=10.0) else: try: await ctx.send(embed=StyledEmbed( description= f"**Added** {tracks[0]} **to playlist {playlist}.**" ), delete_after=10.0) except TypeError: raise BadRequestException( "Couldn't add this item to the queue!") tracks_to_add = tracks if not additional_args: raise BadRequestException( "You have to provide either a search term or a URL!") await search_song(additional_args, ctx, self.bot, success_callback, success_callback_url) if len(tracks_to_add) > 0: self.database.add_to_playlist(ctx.author.id, playlist, tracks_to_add) await ctx.message.add_reaction("<:ok:746377326245445653>") else: raise BadRequestException("No track selected!") elif action == "share": if playlist is None: raise BadRequestException("Please enter a playlist name!") playlist_id = self.database.raise_if_not_exists( ctx.author.id, playlist) message = f"{ctx.author.id}:{playlist_id}:{playlist}:{ctx.author.name}" message_bytes = message.encode("utf8") base64_bytes = base64.b64encode(message_bytes) base64_message = base64_bytes.decode("ascii") await ctx.send(embed=StyledEmbed( title="Share this link", description=f"https://chime.realmayus.xyz/view/{base64_message}" )) elif action == "delete": raise BadRequestException( "This feature has not been implemented yet.") else: raise BadRequestException( "This action does not exist. Valid actions are: `create`, `list`, `add`, `show`, `play`, `delete` and `share`." )
def get_currently_playing_embed(current_track: Track, current_time=None): currently_playing_embed = StyledEmbed( title="<:music_note:718120922367787099> " + current_track.title) currently_playing_embed.set_author(name="Now playing", url=current_track.uri) if current_time: currently_playing_embed.description = get_song_progress_bar( current_time, current_track.duration) currently_playing_embed.add_field(name="Duration", value=get_friendly_time_delta( current_track.duration)) currently_playing_embed.add_field(name="Artist", value=current_track.author) if current_track.thumb is not None: currently_playing_embed.set_thumbnail(url=current_track.thumb) return currently_playing_embed
async def stats(self, ctx): """Shows useful information about the current node your chime player is connected to. Useful for troubleshooting.""" player = self.bot.wavelink.get_player(ctx.guild.id) node = player.node embed = StyledEmbed(title="chime stats") embed.description = f'Connected to {len(self.bot.wavelink.nodes)} node(s).\n' \ f'Best available node: **{self.bot.wavelink.get_best_node().__repr__()}**\n' embed.add_field(name="Stream count", value=f"{str(node.stats.playing_players)}") embed.add_field(name="Server Count", value=f"{len(self.bot.guilds)}") embed.add_field( name="Lavalink uptime", value= f"{str(datetime.timedelta(seconds=round(node.stats.uptime / 1000)))}" ) current_time = time.time() difference = int(round(current_time - self.bot.start_time)) timestamp = str(datetime.timedelta(seconds=difference)) embed.add_field(name="Bot uptime", value=f"{timestamp}") await ctx.send(embed=embed)
async def success_callback(track_, last_msg: Message): await last_msg.clear_reactions() await last_msg.edit(embed=StyledEmbed(description=f"**Added** {track_} **to queue.**"), delete_after=10.0) if not player.is_connected: await ctx.invoke(self.join) # Join channel if not connected self.get_controller(ctx).queue.append(track_)
async def feedback(self, ctx): """Gives you options to send feedback or to report bugs.""" msg: Message = await ctx.send(embed=StyledEmbed( title="Feedback", description= "Thanks for helping to improve chime! What's the problem? \n \n " u"1\N{variation selector-16}\N{combining enclosing keycap}" + " I'd like to send feedback\n" u"2\N{variation selector-16}\N{combining enclosing keycap}" + " I'd like to report an outage\n" u"3\N{variation selector-16}\N{combining enclosing keycap}" + " I'd like to report a bug\n")) [ await msg.add_reaction( u"%s\N{variation selector-16}\N{combining enclosing keycap}" % str(x + 1)) for x in range(3) ] def check_reaction(reaction: RawReactionActionEvent): return reaction.member == ctx.author and isinstance( reaction.emoji.name, str) and ((reaction.emoji.name[0].isdigit() and int(str(reaction.emoji.name)[0]) in range(4)) and reaction.message_id == msg.id) try: reaction: RawReactionActionEvent = await self.bot.wait_for( 'raw_reaction_add', timeout=20.0, check=check_reaction) except asyncio.TimeoutError: """Handle Timeout""" else: if str(reaction.emoji.name[0]).isdigit() and int( str(reaction.emoji.name)[0]) in range(4): selected_number = int(str(reaction.emoji.name[0])) what_to_do = None if selected_number == 1: what_to_do = "Send Feedback" elif selected_number == 2: what_to_do = "Report Outage" elif selected_number == 3: what_to_do = "Report Bug" if selected_number == 2 or selected_number == 3: await msg.edit(embed=StyledEmbed( title=what_to_do, description= "Got it. Please describe the issue as precise as possible in your next message. Bonus points for steps to reproduce. Send `stop` to abort" )) await msg.clear_reactions() else: await msg.edit(embed=StyledEmbed( title=what_to_do, description= "Got it. Please describe your feedback in the next message you send. Send `stop` to abort" )) await msg.clear_reactions() try: description: Message = await self.bot.wait_for( 'message', timeout=60.0, check=lambda m: m.channel == ctx.channel and m.author == ctx.author) except asyncio.TimeoutError: await ctx.channel.send( "Aborting feedback wizard because no answer was sent.") else: if description.content == "stop": await ctx.send("Ok.") return if selected_number == 2 or selected_number == 3: if selected_number == 2: """Urgent issue""" captcha_solved = False while not captcha_solved: solution, file = self.get_captcha_file() embed: StyledEmbed = StyledEmbed( title="Please solve the captcha.", description= "Not case sensitive. To quit, enter `stop`, for a new captcha enter `new`" ) await ctx.send(file=file, embed=embed) try: captcha_sol: Message = await self.bot.wait_for( 'message', timeout=30.0, check=lambda ms: ms.channel == ctx. channel and ms.author == ctx.author) captcha_sol: str = captcha_sol.content except asyncio.TimeoutError: await ctx.channel.send( "Aborting feedback wizard because no captcha answer was sent." ) else: if captcha_sol.lower() == "stop": await ctx.send("Ok.") return if captcha_sol.lower().replace( "o", "0").replace("7", "1").replace( "8", "b") == solution.lower( ).replace("o", "0").replace( "7", "1").replace("8", "b"): captcha_solved = True report_channel_ = await self.bot.fetch_channel( report_channel) error_embed = StyledEmbed( suppress_tips=True, title=f"👥 Outage Report") error_embed.description = "A user has submitted an outage report:\n\n" + description.content error_embed.set_author(name="User Report") await report_channel_.send( "<@&718113149651255386>", embed=error_embed) await ctx.channel.send( "Thanks for the report and for making chime better! A developer will look into the issue as soon as possible." ) elif selected_number == 3: report_channel_ = await self.bot.fetch_channel( report_channel) error_embed = StyledEmbed( suppress_tips=True, title=f"👥 Bug Report") error_embed.description = "A user has submitted a bug report:\n\n" + description.content error_embed.set_author(name="User Report") await report_channel_.send(embed=error_embed) await ctx.channel.send( "Thanks for the report and for making chime better! A developer will look into the issue" + ("." if selected_number == 3 else " as soon as possible.")) elif selected_number == 1: report_channel_ = await self.bot.fetch_channel( report_channel) error_embed = StyledEmbed(suppress_tips=True, title=f"👥 Feedback") error_embed.description = "A user has submitted feedback:\n\n" + description.content error_embed.set_author(name="User Feedback") await report_channel_.send(embed=error_embed) await ctx.channel.send( "Thanks for the report and for making chime better! The feedback was sent to the developers." )