async def setoutrotts(self, ctx, *, outrotts: str = None): """Sets your outro tts This is what is said after saying your name when announcing that you have left the channel Calling this command without any text will tell you your current tts intro. The default is simply `has left!`. To set your tts to be nothing, try `?setoutrotts nothing` or `?setoutrotts none` The argument is the name of the clip that will introduce you, for example: `{cmdpfx}setoutrotts dun gone left` **Note:** your intro tts cannot be longer than 32 characters """ user = ctx.message.author if outrotts is None: outrotts = botdata.userinfo(user.id).outrotts await ctx.send( f"Your outro tts sounds like: {ctx.author.name} {outrotts}") await self.play_clip(f"tts:{ctx.author.name} {outrotts}", ctx) return if len(outrotts) > 32: await ctx.send( f"Dat text is {len(outrotts)} characters long, and outrotts text must be 32 characters or less" ) return if outrotts.lower() in ["nothing", "none", ""]: outrotts = " " botdata.userinfo(user.id).outrotts = outrotts await ctx.send(f"Yer intro tts is now `{outrotts}`")
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 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)
async def setsteam(self, ctx, steam_id: int, user: discord.User = None): """Links a discord user to their steam/dota account *The user parameter can only be specified by the bot owner* You have to give this command either your steam32 or steam64 id. An easy way to find this is to look at the end of your dotabuff/opendota profile url. **Example:** If my dotabuff url is https://www.dotabuff.com/players/70388657 I would do: `{cmdpfx}setsteam 70388657` """ if user is None: user = ctx.message.author else: if not checks.is_owner_check(ctx.message.author): await ctx.send("You aint the boss of me 😠") return if steam_id > 76561197960265728: steam_id -= 76561197960265728 player = await opendota_query(f"/players/{steam_id}") if player.get("profile") is None: raise UserError( "Either thats a bad id, you don't play dota, or ya haven't enabled public match data" ) userinfo = botdata.userinfo(user.id) userinfo.steam32 = steam_id await ctx.send("Linked to {}".format(player['profile']['personaname']))
async def convert(cls, ctx, player): is_author = player is None if is_author: player = ctx.message.author try: player = int(player) except (ValueError, TypeError): pass # This either this is a discord user or an invalid argument if isinstance(player, int): if player > 76561197960265728: player -= 76561197960265728 # Don't have to rate limit here because this will be first query ran player_info = await httpgetter.get(f"https://api.opendota.com/api/players/{player}", cache=False, errors=opendota_html_errors) if player_info.get("profile") is None: raise CustomBadArgument(NoMatchHistoryError(player)) return cls(player, f"[{player_info['profile']['personaname']}](https://www.opendota.com/players/{player})", is_author) if not isinstance(player, discord.abc.User): try: player = await commands.MemberConverter().convert(ctx, str(player)) except commands.BadArgument: raise CustomBadArgument(UserError("Ya gotta @mention a user who has been linked to a steam id, or just give me their steam id")) userinfo = botdata.userinfo(player.id) if userinfo.steam is None: if is_author: raise CustomBadArgument(SteamNotLinkedError()) else: raise CustomBadArgument(SteamNotLinkedError(player)) return cls(userinfo.steam, player.mention, is_author)
async def setoutro(self, ctx, clipname: str = None, user: discord.User = None): """Sets your outro clip Calling this command without a clipname will tell you your current outro The argument is the name of the clip that will 'outroduce' you, for example: `{cmdpfx}setoutro math` **Note:** your outro clip cannot be longer than 4 seconds """ if user is None: user = ctx.message.author else: if not checks.is_owner_check(ctx.message.author): await ctx.send("You aint the boss of me 😠") return if clipname is None: outro = botdata.userinfo(user.id).outro if outro is None or outro == "": await ctx.send( "Yer outro isn't set. Try doin somethin' like `?setoutro dota:troll_lose_03`" ) return else: await ctx.send("Your outro is: {}".format(outro)) await self.play_clip("tts:your outro is", ctx) await self.play_clip(outro, ctx) return clip = await self.get_clip_try_types(clipname, "local|dota") audiolength = clip.audiolength if audiolength > 3.1: await ctx.send( f"Dat clip is {audiolength:.1f} seconds long, and outros gotta be less than 3." ) return botdata.userinfo(user.id).outro = clip.clipid await ctx.send("Yer outro is now " + clip.clipid)
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 userconfig(self, ctx, name, *, value=None): """Configures the bot's user-specific settings Below are the different user-specific settings that you can tweak to customize mangobyte. You can get more information about a setting by typing `{cmdpfx}userconfig <settingname>`, and you can configure a setting by typing `{cmdpfx}userconfig <settingname> <value>` {userconfig_help} """ var = next((v for v in UserInfo.variables if v["key"] == name), None) if not var: vars_list = "\n".join( map(lambda v: f"`{v['key']}`", UserInfo.variables)) await ctx.send( f"There is no userconfig setting called '{name}'. Try one of these:\n{vars_list}" ) return if not value: # We are just getting a value value = botdata.userinfo(ctx.message.author)[var["key"]] await ctx.send(embed=await botdatatypes.localize_embed( ctx, var, value, f"{self.cmdpfx(ctx)}userconfig")) else: # We are setting a value value = await botdatatypes.parse(ctx, var, value) botdata.userinfo(ctx.message.author)[var["key"]] = value await ctx.message.add_reaction("✅")
async def get_check_steamid(player, ctx, mention=False, no_error=False): is_author = player is None if is_author: player = ctx.message.author try: player = int(player) except (ValueError, TypeError): pass # This either this is a discord user or an invalid argument if isinstance(player, int): if player > 76561197960265728: player -= 76561197960265728 # Don't have to rate limit here because this will be first query ran player_info = await opendota_query(f"/players/{player}") if player_info.get("profile") is None: raise UserError("Either this person doesn't play dota, or they haven't enabled public match data") if mention: return player, f"[{player_info['profile']['personaname']}](https://www.opendota.com/players/{player})" else: return player if not isinstance(player, discord.User): try: player = await commands.MemberConverter().convert(ctx, str(player)) except commands.BadArgument: if no_error: return None raise UserError("Ya gotta @mention a user who has been linked to a steam id, or just give me a their steam id") userinfo = botdata.userinfo(player.id) if userinfo.steam32 is None: if no_error: return None if is_author: raise SteamNotLinkedError() else: raise SteamNotLinkedError(player) if mention: return userinfo.steam32, player.mention else: return userinfo.steam32
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}")
async def friendstats(self, ctx, player): """Statistics of games played with a friend""" await ctx.channel.trigger_typing() author_id = botdata.userinfo(ctx.message.author.id).steam32 if not author_id: raise SteamNotLinkedError() friend_id, friend_mention = await get_check_steamid(player, ctx, mention=True) author_mention = ctx.message.author.mention if author_id == friend_id: raise UserError( "🙄 ...Try giving me someone other than yourself...") author_info = await opendota_query(f"/players/{author_id}") friend_info = await opendota_query(f"/players/{friend_id}") def on_same_team(match): heroes = match["heroes"] player1 = heroes[next(x for x in heroes if heroes[x].get("account_id") == author_id)] player2 = heroes[next(x for x in heroes if heroes[x].get("account_id") == friend_id)] return (player1["player_slot"] < 128) == (player2["player_slot"] < 128) def won_match(match): heroes = match["heroes"] player = heroes[next(x for x in heroes if heroes[x].get("account_id") == author_id)] return (player["player_slot"] < 128) == match["radiant_win"] url = f"/players/{author_id}/matches?included_account_id={friend_id}" matches = await opendota_query(url) matches = list(filter(on_same_team, matches)) if len(matches) == 0: raise UserError("You haven't played any matches with them!") winrate = len(list(filter(won_match, matches))) / len(matches) def format_match(match): heroes = match["heroes"] author = heroes[next(x for x in heroes if heroes[x].get("account_id") == author_id)] friend = heroes[next(x for x in heroes if heroes[x].get("account_id") == friend_id)] timediff = time.time() - match['start_time'] timediff -= timediff % 60 if timediff > (60 * 60 * 24 * 30): timediff -= timediff % (60 * 60) return ( f"{get_pretty_time(timediff)} ago, " f"you [{'won' if won_match(match) else 'lost'} a match](https://www.opendota.com/matches/{match['match_id']}) where " f"{author_mention} played **{self.hero_info[author['hero_id']]['name']}**, and " f"{friend_mention} played **{self.hero_info[friend['hero_id']]['name']}**" ) embed = discord.Embed(description=( f"[Games Played](https://www.opendota.com{url}): {len(matches)}\n" f"Winrate: {winrate:.2%}\n"), color=self.embed_color) embed.add_field(name="First Match", value=format_match(matches[-1])) embed.add_field(name="Most Recent Match", value=format_match(matches[0])) embed.set_author( name= f"{author_info['profile']['personaname']} + {friend_info['profile']['personaname']}", url=f"https://www.opendota.com{url}") image = discord.File( await drawdota.combine_image_halves( author_info['profile']['avatarfull'], friend_info['profile']['avatarfull']), "profile.png") embed.set_thumbnail(url=f"attachment://{image.filename}") await ctx.send(embed=embed, file=image)