async def botunban(self, ctx, user: discord.Member): """Unbans the user, allowing them to use commands""" if checks.is_owner_check(user) or user == self.bot.user: await ctx.send("Ha ha. Very funny.") return botdata.guildinfo(ctx.message.guild).botunban(user) await ctx.send("{} is free of their restraints and may once again use commands".format(user.mention))
async def on_voice_state_update(self, member, before, after): if member.bot and member.id != self.bot.user.id: return # ignore bots except for mahself if before and after and before.channel == after.channel: return # if the member didnt change channels, dont worry about it if before and before.channel and botdata.guildinfo( before.channel.guild).outros: beforeplayer = await self.audioplayer(before.channel, error_on_none=False) if beforeplayer is not None and beforeplayer.voice is not None and beforeplayer.voice.channel.id == before.channel.id: guildinfo = botdata.guildinfo(before.channel.guild) userinfo = botdata.userinfo(member.id) outroclip = userinfo.outro outrotts = userinfo.outrotts name = member.name if guildinfo.usenickname and member.nick: name = member.nick text = (await self.fix_name(name)) + " " + outrotts print(text) await asyncio.sleep(0.5) if not outroclip is None: await self.play_clip(outroclip, before.channel) await self.play_clip("tts:" + text, before.channel) if after and after.channel and botdata.guildinfo( after.channel.guild).intros: afterplayer = await self.audioplayer(after.channel, error_on_none=False) if afterplayer is not None and afterplayer.voice is not None and afterplayer.voice.channel.id == after.channel.id: guildinfo = botdata.guildinfo(after.channel.guild) if member.id == self.bot.user.id: guildinfo.voicechannel = after.channel.id userinfo = botdata.userinfo(member.id) introclip = userinfo.intro introtts = userinfo.introtts name = member.name if guildinfo.usenickname and member.nick: name = member.nick # Special case for default if userinfo.intro == "local:helloits" and introtts == "it's": introtts = "" text = introtts + " " + await self.fix_name(name) print(text + " joined the channel") await asyncio.sleep(3) if not introclip is None: await self.play_clip(introclip, after.channel) await self.play_clip("tts:" + text, after.channel)
async def on_message(self, message): if message.guild and (not message.content.startswith("?")) and message.author.id != self.bot.user.id: if botdata.guildinfo(message.guild).is_banned(message.author): return # banned users cant talk ttschannel = botdata.guildinfo(message.guild.id).ttschannel if ttschannel == message.channel.id: try: await self.do_smarttts(message.clean_content, message.guild) except UserError as e: await message.channel.send(e.message) except Exception as e: await message.channel.send("Uh-oh, sumthin dun gone wrong 😱") report_error(message, TtsChannelError(e))
async def botban(self, ctx, user: discord.Member): """Bans the user from using commands""" if checks.is_owner_check(user): await ctx.send("Ya can't ban mah owner, man. 😠") return if checks.is_admin_check(ctx.message.channel, ctx, user): await ctx.send("Ya can't ban other admins") return if user.id == self.bot.user.id: await ctx.send("Lol you can't ban me, silly") return botdata.guildinfo(ctx.message.guild).botban(user) await ctx.send("{} has henceforth been banned from using commands 😤".format(user.mention))
async def resummon(self, ctx): """Re-summons the bot to the voice channel This command is useful if you are having issues with mangobyte not being responsive""" audio = self.bot.get_cog("Audio") if not audio: raise UserError("You must have the Audio cog enabled to do this") if not ctx.message.guild: raise UserError("You have to be in a server to use this command") guildinfo = botdata.guildinfo(ctx.message.guild.id) save_channel = False channel = None if ctx.message.guild.me.voice: channel = ctx.message.guild.me.voice.channel elif ctx.message.author.voice: channel = ctx.message.author.voice.channel elif guildinfo.voicechannel is not None: channel = self.bot.get_channel(guildinfo.voicechannel) else: raise UserError("I'm not sure where you want me to resummon to. I'm not in any channel currently.") await audio.disconnect(ctx.message.guild) await asyncio.sleep(1) try: await audio.connect_voice(channel) guildinfo.voicechannel = channel.id except asyncio.TimeoutError: cmdpfx = botdata.command_prefix(ctx) raise UserError(f"There was a timeout when attempting to do the `{cmdpfx}summon`") await ctx.message.add_reaction("✅")
async def disablecommand(self, ctx, command: str): """Disabled the specified command or command category **Examples:** `{cmdpfx}disablecommand wiki` `{cmdpfx}disablecommand Audio`""" guildinfo = botdata.guildinfo(ctx) if not guildinfo: raise UserError("This command must be called in a guild") cmd = self.get_command_or_cog(ctx, command) if cmd is None: raise UserError( "Couldn't find a command or command category by that name") secure_cogs = ["Admin", "Owner"] if isinstance(cmd, discord.ext.commands.Command): if guildinfo.is_disabled(cmd.cog_name): raise UserError( f"The category this command belongs to ({cmd.cog_name}) is already disabled" ) if cmd.cog_name in secure_cogs: raise UserError("You can't disable a command in this category") else: if cmd.name in secure_cogs: raise UserError("You can't disable this category") if guildinfo.is_disabled(cmd.name): raise UserError("This has already been disabled") guildinfo.disable_command(cmd.name) await ctx.message.add_reaction("✅")
async def on_message(self, message): if message.author.bot and settings.debug: ctx = await self.bot.get_context(message) await self.bot.invoke(ctx) if message.guild is not None and not botdata.guildinfo( message.guild.id).reactions: return if (message.author == self.bot.user) or message.content.startswith( self.cmdpfx(message.guild)): return random.seed(message.content) for check in self.reactions: expression = check["regex"] if check.get("word"): expression = "\\b({})\\b".format(expression) match = re.search(expression, message.clean_content, re.IGNORECASE) else: match = re.search(expression, message.clean_content) if match and (random.random() < check.get("chance", 1.0)): await message.add_reaction(random.choice(check["reaction"])) break
async def on_message(self, message): if message.author == self.bot.user: return # ignore stuff from myself if message.guild is None: return # only keep going if we're in a guild guildinfo = botdata.guildinfo(message.guild.id) if message.author.bot and (message.author.id in guildinfo.allowedbots ) or (message.webhook_id and guildinfo.allowwebhooks): # execute this command from a bot because we're allowing it ctx = await self.bot.get_context(message) await self.bot.invoke(ctx) if message.content.startswith(self.cmdpfx(message.guild)): return # ignore stuff that starts with the command prefix if not guildinfo.reactions: return # only keep going for guilds with reactions enabled random.seed(message.content) for check in self.reactions: expression = check["regex"] if check.get("word"): expression = "\\b({})\\b".format(expression) match = re.search(expression, message.clean_content, re.IGNORECASE) else: match = re.search(expression, message.clean_content) if match and (random.random() < check.get("chance", 1.0)): await message.add_reaction(random.choice(check["reaction"])) break
async def summon(self, ctx, channel: discord.VoiceChannel = None): """Summons the bot to the voice channel you are currently in You can specify the specific voice channel that you would like to connect to. If no channel is specified, it will connect to whatever channel you are currently in. **Example:** `{cmdpfx}summon General`""" if not channel: if not ctx.message.guild: raise UserError("You have to say that in a server") if not ctx.message.author.voice: raise UserError("You are not currently in a voice channel") channel = ctx.message.author.voice.channel if channel.guild != ctx.message.guild: raise UserError( "You are not currently in a voice channel on this server/guild" ) audio = self.bot.get_cog("Audio") if not audio: raise UserError("You must have the Audio cog enabled to do this") try: await audio.connect_voice(channel) botdata.guildinfo(channel.guild.id).voicechannel = channel.id except asyncio.TimeoutError: raise UserError( "There was a timeout when attempting to do the `?summon`")
async def audioplayer(self, ctx, error_on_none=True): # TODO: ACCOUNT FOR WHEN THIS MESSAGE IS A PM if isinstance(ctx, discord.ext.commands.Context): if ctx.message.guild is None: # This is a private channel, so give it user ctx = ctx.message.author else: ctx = ctx.message.guild if isinstance(ctx, discord.User): author = ctx for audioplayer in self.audioplayers: member = audioplayer.guild.get_member(author.id) if member and member.voice and audioplayer.voice and audioplayer.voice.channel.id == member.voice.channel.id: if botdata.guildinfo(audioplayer.guild).is_banned(member): raise AudioPlayerNotFoundError("Nice try, but you're banned in the voice channel that I'm in") return audioplayer if error_on_none: raise AudioPlayerNotFoundError("You're not in any voice channels that I'm in") else: return None elif isinstance(ctx, discord.Guild): guild = ctx elif isinstance(ctx, discord.abc.GuildChannel): guild = ctx.guild else: raise ValueError(f"Incorrect type '{type(ctx)}' given to audioplayer function") for audioplayer in self.audioplayers: if audioplayer.guild == guild: return audioplayer if error_on_none: raise AudioPlayerNotFoundError(f"I'm not in a voice channel on this server/guild. Have an admin do `{botdata.command_prefix(ctx)}summon` to put me in one.") else: return None
async def on_message(self, message): if message.guild and (not message.content.startswith( self.cmdpfx( message.guild))) and message.author.id != self.bot.user.id: guildinfo = botdata.guildinfo(message.guild) if guildinfo.is_banned(message.author): return # banned users cant talk ttschannel = guildinfo.ttschannel if ttschannel == message.channel.id: if message.content.startswith( "//") or message.content.startswith("#"): return # commented out stuff should be ignored await loggingdb.insert_message(message, "smarttts") try: if guildinfo.announcetts: name = message.author.name if guildinfo.usenickname and message.author.nick: name = message.author.nick name = await self.fix_name(name) await self.do_tts(f"{name} says", message.guild) await self.do_smarttts(message.clean_content, message.guild) except UserError as e: await message.channel.send(e.message) except Exception as e: await message.channel.send( "Uh-oh, sumthin dun gone wrong 😱") await report_error(message, TtsChannelError(e))
async def on_message(self, message): if message.guild and (not message.content.startswith( self.cmdpfx( message.guild))) and message.author.id != self.bot.user.id: guildinfo = botdata.guildinfo(message.guild) if guildinfo.is_banned(message.author): return # banned users cant talk if message.author.bot: if message.webhook_id: if not guildinfo.allowwebhooks: return # if this is a webhook then ignore it because we're not allowing it else: if message.author.id not in guildinfo.allowedbots: return # ignore bots unless theyre explicitly allowed ttschannel = guildinfo.ttschannel if ttschannel == message.channel.id: if message.content.startswith( "//") or message.content.startswith("#"): return # commented out stuff should be ignored try: if guildinfo.announcetts: name = message.author.name if guildinfo.usenickname and message.author.nick: name = message.author.nick name = await self.fix_name(name) await self.do_tts(f"{name} says", message.guild) if guildinfo.simpletts: await loggingdb.insert_message(message, "tts") await self.do_tts(message.clean_content, message.guild) else: await loggingdb.insert_message(message, "smarttts") await self.do_smarttts(message.clean_content, message.guild) except AudioPlayerNotFoundError as e: if not guildinfo.ttschannelwarn and ( "I'm not in a voice channel on this server/guild" in e.message): return # just dont warn em if theyve said to not warn try: await message.channel.send(e.message) except discord.errors.Forbidden as e: print( "on_message usererror blocked because permissions") pass except UserError as e: try: await message.channel.send(e.message) except discord.errors.Forbidden as e: print( "on_message usererror blocked because permissions") pass except Exception as e: try: await message.channel.send( "Uh-oh, sumthin dun gone wrong 😱") except discord.errors.Forbidden as e: print( "on_message usererror blocked because permissions") pass await report_error(message, TtsChannelError(e))
async def getbotdata(self, ctx, selector, identifier : int): """Gets info about a user or a server, depending on the selector given""" if selector in ["user", "player", "member"]: data = botdata.userinfo(identifier) user = self.bot.get_user(identifier) if user is None: raise UserError("Couldn't find that user") embed = discord.Embed(description=(user.mention + "\n```json\n" + json.dumps(data.json_data, indent='\t') + "\n```")) embed.set_thumbnail(url=user.avatar_url) if data.steam: embed.add_field(name="Profiles", value=( f"[Steam](http://steamcommunity.com/id/{data.steam})\n" f"[OpenDota](https://www.opendota.com/players/{data.steam})\n")) await ctx.send(embed=embed) elif selector in ["server", "guild"]: data = botdata.guildinfo(identifier) guild = self.bot.get_guild(identifier) if guild is None: raise UserError("Couldn't find that guild") invite = None for channel in guild.text_channels: if channel.permissions_for(guild.me).create_instant_invite: invite = await channel.create_invite() break embed = discord.Embed(description=("```json\n" + json.dumps(data.json_data, indent='\t') + "\n```")) embed.set_author(name=guild.name) if guild.icon_url != "": embed.set_thumbnail(url=guild.icon_url) if invite: embed.add_field(name="Invite", value=invite.url) await ctx.send(embed=embed)
def __global_check(self, ctx): """Checks to make sure the user has permissions""" if not isinstance(ctx.message.channel, discord.abc.PrivateChannel): if botdata.guildinfo(ctx.message.guild).is_banned( ctx.message.author): return False return True
async def remoteresummon(self, ctx, guild_id : int): """Re-summons the bot for the given guild This command is useful if you are having issues with mangobyte not being responsive""" audio = self.bot.get_cog("Audio") if not audio: raise UserError("You must have the Audio cog enabled to do this") guild = self.bot.get_guild(guild_id) if guild is None: raise UserError(f"guild '{guild_id}' not found") guildinfo = botdata.guildinfo(guild_id) channel = None if guild.me.voice: channel = guild.me.voice.channel elif guildinfo.voicechannel is not None: channel = self.bot.get_channel(guildinfo.voicechannel) else: raise UserError("I'm not sure where you want me to resummon to. I'm not in any channel currently.") await audio.disconnect(guild) await asyncio.sleep(1) try: await audio.connect_voice(channel) guildinfo.voicechannel = channel.id except asyncio.TimeoutError: cmdpfx = botdata.command_prefix(ctx) raise UserError(f"There was a timeout when attempting to do the `{cmdpfx}summon`") await ctx.message.add_reaction("✅")
async def unsummon(self, ctx): """Removes the bot from the voice channel it is currently in""" audio = self.bot.get_cog("Audio") if not audio: raise UserError("You must have the Audio cog enabled to do this") await audio.disconnect(ctx.message.guild) botdata.guildinfo(ctx.message.guild.id).voicechannel = None
def bot_check(self, ctx): """Checks to make sure the user has permissions""" guildinfo = botdata.guildinfo(ctx) if not isinstance(ctx.message.channel, discord.abc.PrivateChannel): if guildinfo.is_banned(ctx.message.author): return False if guildinfo.is_disabled(ctx.command): return False return True
def __init__(self, text, bot, ctx): tempfile = settings.resource("temp/{}.wav".format( int(random.random() * 1000000000))) data = botdata.guildinfo(ctx) if data: tts_save(tempfile, text, data.ttslang) else: tts_save(tempfile, text) Clip.__init__(self, text, tempfile, text)
def embed_description(self, description, helptarget): if not description: return discord.Embed() description = self.fill_template(description) guildinfo = botdata.guildinfo(self.context) if helptarget and guildinfo and guildinfo.is_disabled(helptarget): emoji = simple_get_emoji("command_disabled", self.context.bot) thing = "command" if isinstance(helptarget, Command) else "category" description = f"{emoji} *This {thing} has been disabled on this server*\n{description}" return discord.Embed(description=description, color=discord.Color.blue())
async def unsummon(self, ctx): """Removes the bot from the voice channel""" audio = self.bot.get_cog("Audio") if not audio: raise UserError("You must have the Audio cog enabled to do this") if not ctx.message.guild: raise UserError("You have to say that in a server") await audio.disconnect(ctx.message.guild) botdata.guildinfo(ctx.message.guild.id).voicechannel = None await ctx.message.add_reaction("✅")
async def config(self, ctx, name, *, value = None): """Configures the bot's settings for this server Below are the different settings that you can tweak to customize mangobyte for this server. You can get more information about a setting by typing `{cmdpfx}config <settingname>`, and you can configure a setting by typing `{cmdpfx}config <settingname> <value>` {config_help} """ var = next((v for v in GuildInfo.variables if v["key"] == name), None) if not var: vars_list = "\n".join(map(lambda v: f"`{v['key']}`", GuildInfo.variables)) await ctx.send(f"There is no config setting called '{name}'. Try one of these:\n{vars_list}") return currentvalue = botdata.guildinfo(ctx.guild)[var["key"]] if not value: # We are just getting a value await ctx.send(embed=await botdatatypes.localize_embed(ctx, var, currentvalue, f"{self.cmdpfx(ctx)}config")) else: # We are setting a value value = await botdatatypes.parse(ctx, var, value, currentvalue) botdata.guildinfo(ctx.guild)[var["key"]] = value await ctx.message.add_reaction("✅")
async def on_voice_state_update(self, member, before, after): if before and after and before.channel == after.channel: return # if the member didnt change channels, dont worry about it if before and before.channel and botdata.guildinfo( before.channel.guild).outros: beforeplayer = await self.audioplayer(before.channel, error_on_none=False) if beforeplayer is not None and beforeplayer.voice.channel.id == before.channel.id: text = (await self.fix_name(member.name)) + " has left!" print(text) outroclip = "local:farewell" userinfo = botdata.userinfo(member.id) if userinfo.outro != "" and userinfo.outro != outroclip: outroclip = userinfo.outro await asyncio.sleep(0.5) await self.play_clip(outroclip, before.channel) await self.play_clip("tts:" + text, before.channel) if after and after.channel and botdata.guildinfo( after.channel.guild).intros: afterplayer = await self.audioplayer(after.channel, error_on_none=False) if afterplayer is not None and afterplayer.voice.channel.id == after.channel.id: if member.id == self.bot.user.id: botdata.guildinfo( after.channel.guild.id).voicechannel = after.channel.id text = await self.fix_name(member.name) print(text + " joined the channel") introclip = "local:helloits" userinfo = botdata.userinfo(member.id) if userinfo.intro != "" and userinfo.intro != introclip: introclip = userinfo.intro text = "its " + text await asyncio.sleep(3) await self.play_clip(introclip, after.channel) await self.play_clip("tts:" + text, after.channel)
async def on_message(self, message): if message.guild and (not message.content.startswith( self.cmdpfx( message.guild))) and message.author.id != self.bot.user.id: if botdata.guildinfo(message.guild).is_banned(message.author): return # banned users cant talk ttschannel = botdata.guildinfo(message.guild.id).ttschannel if ttschannel == message.channel.id: if message.content.startswith( "//") or message.content.startswith("#"): return # commented out stuff should be ignored loggingdb.insert_message(message, "smarttts", loggingdb_session) try: await self.do_smarttts(message.clean_content, message.guild) except UserError as e: await message.channel.send(e.message) except Exception as e: await message.channel.send( "Uh-oh, sumthin dun gone wrong 😱") report_error(message, TtsChannelError(e))
async def init(self, text, bot, ctx): data = botdata.guildinfo(ctx) ttslang = "en-au" if not data else data.ttslang uri = f"clip_tts_{ttslang}:{text}" filename = httpgetter.cache.get_filename(uri) if not filename: filename = await httpgetter.cache.new(uri, "wav") try: tts_save(filename, text, ttslang) except: await httpgetter.cache.remove(uri) raise return await Clip.init(self, text, filename, text)
async def init(self, text, bot, ctx): data = botdata.guildinfo(ctx) ttslang = "en-au" if not data else data.ttslang uri = f"clip_tts_{ttslang}:{text}" filename = httpgetter.cache.get_filename(uri) if not filename: filename = await httpgetter.cache.new(uri, "wav") try: await bot.loop.run_in_executor( ThreadPoolExecutor(), functools.partial(tts_save, filename, text, ttslang)) except: await httpgetter.cache.remove(uri) raise return await Clip.init(self, text, filename, text)
def is_admin_check(channel, ctx): author = ctx.message.author if isinstance(channel, discord.abc.PrivateChannel): return False # All admin commands should be guild specific and not work on PM channels if is_owner_check(author): return True admin_role = botdata.guildinfo(ctx.message.guild).botadmin if admin_role: admin_role = discord.utils.get(ctx.guild.roles, id=admin_role) if admin_role: for member in admin_role.members: if member.id == author.id: return True perms = channel.permissions_for(author) return perms.administrator
async def summon(self, ctx, channel: str = None): """Summons the bot to the voice channel You can specify the specific voice channel that you would like to connect to. If no channel is specified, it will connect to whatever channel you are currently in. **Examples:** `{cmdpfx}summon` `{cmdpfx}summon General`""" if channel: actual_channel = None if ctx.message.guild: for ch in ctx.message.guild.voice_channels: if channel.lower() == ch.name.lower(): actual_channel = ch break if not actual_channel: for ch in ctx.message.guild.voice_channels: if channel.lower() in ch.name.lower(): actual_channel = ch break channel = actual_channel if not channel: if not ctx.message.guild: raise UserError("You have to say that in a server") if not ctx.message.author.voice: raise UserError("You are not currently in a voice channel") channel = ctx.message.author.voice.channel if channel.guild != ctx.message.guild: raise UserError( "You are not currently in a voice channel on this server/guild" ) audio = self.bot.get_cog("Audio") if not audio: raise UserError("You must have the Audio cog enabled to do this") try: await audio.connect_voice(channel) botdata.guildinfo(channel.guild.id).voicechannel = channel.id except asyncio.TimeoutError: cmdpfx = botdata.command_prefix(ctx) raise UserError( f"There was a timeout when attempting to do the `{cmdpfx}summon`" ) await ctx.message.add_reaction("✅")
async def enablecommand(self, ctx, command: str): """Re-enables the specified command or command category Only works on commands that have already been disabled by the `{cmdpfx}disablecommand` command **Examples:** `{cmdpfx}enablecommand wiki` `{cmdpfx}enablecommand Audio`""" guildinfo = botdata.guildinfo(ctx) if not guildinfo: raise UserError("This command must be called in a guild") cmd = self.get_command_or_cog(ctx, command) if cmd is None: raise UserError("Couldn't find a command or command category by that name") if not guildinfo.is_disabled(cmd.name): if guildinfo.is_disabled(cmd): raise UserError(f"This command is not disabled, but its category ({cmd.cog_name}) is") else: raise UserError("This is not currently disabled") guildinfo.enable_command(cmd.name) await ctx.message.add_reaction("✅")
async def on_voice_state_update(self, member, before, after): channel_id = "not sure yet" try: if member.bot and member.id != self.bot.user.id: return # ignore bots except for mahself if before and after and before.channel == after.channel: return # if the member didnt change channels, dont worry about it if before and before.channel and botdata.guildinfo(before.channel.guild).outros: beforeplayer = await self.audioplayer(before.channel, error_on_none=False) if beforeplayer is not None and beforeplayer.voice is not None and beforeplayer.voice.channel.id == before.channel.id: ctx = before.channel.guild guildinfo = botdata.guildinfo(before.channel.guild) userinfo = botdata.userinfo(member.id) channel_id = before.channel.id if member.id == self.bot.user.id: return # dont play outros for self, thatd be a bug try: outroclip = userinfo.outro if outroclip: outroclip = await self.get_clip(userinfo.outro, ctx) if outroclip.audiolength > botdatatypes.max_intro_outro_length + 0.5: userinfo.set_default(ctx, "outro") outroclip = userinfo.outro except: userinfo.set_default(ctx, "outro") outroclip = userinfo.outro outrotts = userinfo.outrotts name = member.name if guildinfo.usenickname and member.nick: name = member.nick text = (await self.fix_name(name)) + " " + outrotts print(text) await asyncio.sleep(0.5) if not outroclip is None: await self.play_clip(outroclip, before.channel) await self.play_clip("tts:" + text, before.channel) if after and after.channel and botdata.guildinfo(after.channel.guild).intros: afterplayer = await self.audioplayer(after.channel, error_on_none=False) if afterplayer is not None and afterplayer.voice is not None and afterplayer.voice.channel.id == after.channel.id: ctx = after.channel.guild guildinfo = botdata.guildinfo(after.channel.guild) channel_id = after.channel.id if member.id == self.bot.user.id: guildinfo.voicechannel = after.channel.id return # dont play intros for self. userinfo = botdata.userinfo(member.id) try: introclip = userinfo.intro if introclip: introclip = await self.get_clip(userinfo.intro, ctx) if introclip.audiolength > botdatatypes.max_intro_outro_length + 0.5: userinfo.set_default(ctx, "intro") introclip = userinfo.intro except: userinfo.set_default(ctx, "intro") introclip = userinfo.intro introtts = userinfo.introtts name = member.name if guildinfo.usenickname and member.nick: name = member.nick # Special case for default if userinfo.intro == "local:helloits" and introtts == "it's": introtts = "" text = introtts + " " + await self.fix_name(name) print(text + " joined the channel") await asyncio.sleep(3) if not introclip is None: await self.play_clip(introclip, after.channel) await self.play_clip("tts:" + text, after.channel) except UserError as e: print(f"Bad voice channel connection to ({channel_id}) from on_voice_state_update: {e.message}")