async def game(self, ctx, *, game): await ctx.trigger_typing() msgs = await lang.get_lang(ctx) trending = "" if game.endswith(" --trending"): trending = "&trending=true" r = http.TwitchAPIRequest( "https://api.twitch.tv/kraken/search/games?" + urlencode({"query": game.strip(' --trending')})) if r.status_code != 200: await ctx.send( f"{msgs['games']['generic_error']} {r.status_code}-1") return elif r.json().get('games') == None: return await ctx.send(msgs['streams']['game_not_found']) elif len(r.json()['games']) < 1: await ctx.send(msgs['clips']['no_clips']) return game = r.json()['games'][0]['name'] r = http.TwitchAPIRequest( "https://api.twitch.tv/kraken/clips/top?limit=50&" + urlencode({"game": game}) + trending) if r.status_code != 200: await ctx.send( f"{msgs['games']['generic_error']} {r.status_code}-2") return elif len(r.json()['clips']) < 1: await ctx.send(msgs['clips']['no_clips']) return clip = choice(r.json()['clips']) m = await ctx.send(msgs['clips']['clip_message'].format( user=clip['broadcaster']['display_name'], game=clip['game'], url=clip['url'].split('?')[0]))
async def game(self, ctx, *, name): await ctx.trigger_typing() msgs = await lang.get_lang(ctx) g = http.TwitchAPIRequest("https://api.twitch.tv/helix/games?" + urllib.parse.urlencode({"name": name})) g.raise_for_status() try: g = g.json()['data'][0] except: return await ctx.send(msgs['streams']['game_not_found']) game = g['name'] s = http.TwitchAPIRequest( "https://api.twitch.tv/helix/streams?game_id=" + g['id']) s.raise_for_status() if len(s.json()['data']) < 1: return await ctx.send(msgs['streams']['game_no_streams']) stream = choice(s.json()['data']) u = http.TwitchAPIRequest("https://api.twitch.tv/helix/users?id=" + stream['user_id']) u.raise_for_status() u = u.json()['data'][0] await ctx.send(msgs['streams']['game_desc'].format( user=u['display_name'].replace("_", "\\_"), game=game, view_count=stream['viewer_count']))
async def user(self, ctx, *, user): await ctx.trigger_typing() msgs = await lang.get_lang(ctx) user = user.split('/')[-1] e = discord.Embed(color=discord.Color(0x6441A4)) r = http.TwitchAPIRequest( "https://api.twitch.tv/helix/streams?user_login="******"data") in [[], None]: await ctx.send(msgs['streams']['stream_not_found']) else: r = r.json()["data"][0] u = http.TwitchAPIRequest( "https://api.twitch.tv/helix/users?login="******"data"][0] g = http.TwitchAPIRequest("https://api.twitch.tv/helix/games?id=" + r["game_id"]) g.raise_for_status() try: g = g.json()["data"][0] except: g = {"id": 0, "name": "Unknown"} e.set_author(icon_url=u["profile_image_url"], name=u["display_name"], url="https://twitch.tv/{}".format(u["login"])) e.title = r["title"] e.description = msgs['streams']['stream_desc'].format( game=g['name'], view_count=r['viewer_count'], channel=u['login']) e.set_image( url=r["thumbnail_url"].format(width="1920", height="1080") + f"?{secrets.token_urlsafe(5)}") await ctx.send(embed=e)
async def listen(self, ctx, *, url: str): """Listen to the specified Twitch user in the current voice channel.""" msgs = await lang.get_lang(ctx) url = "https://www.twitch.tv/" + url.split('/')[-1] if (not hasattr(ctx.author, "voice")) or ctx.author.voice is None: return await ctx.send(msgs['audio']['author_not_in_voice_channel']) voice_channel = ctx.author.voice.channel prem_check = requests.get(f"https://api.twitchbot.io/premium/{ctx.author.id}", headers={"X-Auth-Key": settings.DashboardKey}) if prem_check.json().get('premium') != True or prem_check.status_code != 200: r = requests.get(f"https://dash.twitchbot.io/api/users/{ctx.author.id}/votes", headers={"X-Auth-Key": settings.DashboardKey}) if r.status_code != 200 or r.json()['active'] == False: # Fallback in case the dashboard failed r = http.BotLists.DBLRequest(f"/bots/375805687529209857/check?userId={ctx.author.id}") if r.status_code == 200 and not r.json()['voted'] == 1: return await ctx.send(embed=lang.EmbedBuilder(msgs['audio']['need_upvote_to_continue'])) m = await ctx.send(msgs['audio']['please_wait']) try: try: channel = await voice_channel.connect() except discord.ClientException: session = ctx.message.guild.voice_client await session.disconnect() await asyncio.sleep(2) channel = await voice_channel.connect() except Exception as ex: return await ctx.send(f"A {type(ex).__name__} occurred: {ex}") try: r = http.TwitchAPIRequest("https://api.twitch.tv/helix/streams?user_login="******"twitch.tv/")[1]) if len(r.json()["data"]) < 1: return await m.edit(content=msgs['audio']['user_does_not_exist_or_not_streaming']) r = r.json()["data"][0] r2 = http.TwitchAPIRequest("https://api.twitch.tv/helix/users?login="******"twitch.tv/")[1]) if len(r2.json()["data"]) < 1: return await m.edit(content=msgs['audio']['user_does_not_exist_or_not_streaming']) r2 = r2.json()["data"][0] e = discord.Embed(color=0x6441A4, title=msgs['audio']['now_playing']['title'].format(channel=voice_channel.name), description=msgs['audio']['now_playing']['description'].format(title=r['title'], viewer_count=r['viewer_count'])) e.set_author(name=r2['display_name'], url=url, icon_url=r2['profile_image_url']) e.set_image(url=r['thumbnail_url'].format(width=1920, height=1080) + f"?{secrets.token_urlsafe(5)}") e.set_footer(icon_url=ctx.author.avatar_url or ctx.author.default_avatar_url, text=f"{ctx.author} - {msgs['audio']['now_playing']['footer']}") player = await YTDLSource.from_url(url, loop=self.bot.loop, stream=True) channel.play(player, after=lambda e: logging.error("{}: {}".format(type(e).__name__, e)) if e else None) self.bot.active_vc[ctx.message.guild.id] = e await m.edit(content=None, embed=e) except youtube_dl.DownloadError: await ctx.send(msgs['audio']['user_does_not_exist_or_not_streaming']) except TimeoutError: await ctx.send(msgs['audio']['connection_timeout']) except discord.ClientException as ex: await ctx.send(f"{type(ex).__name__}: {ex}") #await ctx.send("I'm already in a voice channel. Please stop the existing stream and then start it in the new channel.") except: raise
async def top(self, ctx): await ctx.trigger_typing() msgs = await lang.get_lang(ctx) r = http.TwitchAPIRequest( "https://api.twitch.tv/helix/streams?first=20") r.raise_for_status() stream = choice(r.json()['data']) u = http.TwitchAPIRequest("https://api.twitch.tv/helix/users?id=" + stream['user_id']) u.raise_for_status() u = u.json()["data"][0] g = http.TwitchAPIRequest("https://api.twitch.tv/helix/games?id=" + stream["game_id"]) g.raise_for_status() g = g.json()["data"][0] return await ctx.send(msgs['streams']['game_desc'].format( user=u['login'], game=g['name'], view_count=stream['viewer_count']))
async def preview(self, ctx, discord_channel: discord.TextChannel, twitch_user: str): """Sends a preview for a notification.""" msgs = await lang.get_lang(ctx) username = twitch_user.split('/')[-1] if self.regex.match(username) is None: return await ctx.send(msgs['notifs']['malformed_user']) await ctx.trigger_typing() req = http.TwitchAPIRequest( f"https://api.twitch.tv/helix/users?login={username}") if req.status_code == 404 or len(req.json().get('data', [])) < 1: return await ctx.send(msgs['notifs']['twitch_user_not_found_alt']) elif req.status_code != 200: return await ctx.send( f"{msgs['notifs']['invalid_data']} {req.status_code}") req = req.json()['data'][0] notif = r.table('notifications').filter( (r.row['streamer'] == req['id']) & (r.row['channel'] == str(discord_channel.id))) if notif.count().run(self.bot.rethink, durability="soft") == 0: return await ctx.send(msgs['notifs']['del_fail']) notif = list(notif.run(self.bot.rethink, durability="soft"))[0] e = discord.Embed(color=discord.Color(0x6441A4)) e.title = "**Stream Title**" e.description = f"\nPlaying Game for 123 viewers\n[Watch Stream](https://twitch.tv/{req['login']})" e.timestamp = datetime.datetime.now() e.url = "https://twitch.tv/" + req['login'] e.set_footer(text="Notification preview") author_info = { "name": "{} is now live on Twitch!".format(req['display_name']), "url": e.url, "icon_url": req['profile_image_url'] } e.set_author(**author_info) e.set_image( url= "https://images-ext-1.discordapp.net/external/FueXlfSkrjOeYMx92Qe3Y2AaV4G5dk9ijVlNGpF-AgU/https/static-cdn.jtvnw.net/previews-ttv/live_user_overwatchcontenders-1920x1080.jpg" ) fmt_vars = { "$title$": "Stream Title", "$viewers$": "123", "$game$": "Game", "$url$": "https://twitch.tv/{}".format(req['login']), "$name$": req['display_name'], "$everyone$": "@everyone", "$here$": "@here" } msg = functions.ReplaceAllInStr(notif['message'], fmt_vars) msg = functions.ReplaceAllInStr(msg, { "@everyone": "@\u200beveryone", "@here": "@\u200bhere" }) return await ctx.send(msg, embed=e)
async def game(self, ctx, *, name): try: await ctx.trigger_typing() msgs = await lang.get_lang(ctx) e = discord.Embed(color=discord.Color(0x6441A4)) r = http.TwitchAPIRequest( "https://api.twitch.tv/helix/games/?name=" + name) r.raise_for_status() try: r = r.json()["data"][0] except IndexError: return await ctx.send(msgs['games']['no_results']) e.title = r["name"] e.description = f"[{msgs['games']['view_streams']}](https://www.twitch.tv/directory/game/{r['name'].replace(' ', '%20')})" e.set_thumbnail(url=r["box_art_url"].format(width=285, height=380)) r2 = http.Games.IGDBSearchGame(name) rjson = r2.json() if r2.status_code != 200 or len(rjson) == 0: e.add_field(name=msgs['games']['game_details_title'], value=msgs['games']['igdb_fetch_error'].format( error=r2.status_code)) e.set_footer(text=msgs['games']['game_id'].format(id=r['id'])) else: rjson = rjson[0] ratings = round(rjson['rating'] / 10, 1) summary = rjson['summary'][:1000] if len(summary) == 1000: summary += f"... {msgs['games']['info_cutoff']}" if ratings > 5: rate_emoji = "\\👍" else: rate_emoji = "\\👎" e.add_field(name=msgs['games']['game_rating']['name'], value=msgs['games']['game_rating']['value'].format( emoji=rate_emoji, score=ratings, count=rjson['rating_count'])) e.add_field(name=msgs['games']['release_date'], value=time.strftime( '%B %d, %Y', time.gmtime(rjson['first_release_date']))) e.add_field(name=msgs['games']['game_description'], value=summary, inline=False) e.description += f" • [{msgs['games']['view_on_igdb']}](https://www.igdb.com/games/{rjson['slug']})" e.set_footer( text= f"{msgs['games']['game_id'].format(id=r['id'])} • IGDB ID: {rjson['id']}" ) await ctx.send(embed=e) except: await ctx.send(traceback.format_exc())
async def watch(self, ctx, *, user): await ctx.trigger_typing() msgs = await lang.get_lang(ctx) user = user.split('/')[-1] r = http.TwitchAPIRequest( "https://api.twitch.tv/helix/streams?user_login="******"data"] == []: await ctx.send(msgs['streams']['stream_not_found']) else: await ctx.send( f"**<:twitch:404633403603025921> {msgs['streams']['live']}**\nhttps://twitch.tv/{user}" )
async def trending(self, ctx): await ctx.trigger_typing() msgs = await lang.get_lang(ctx) r = http.TwitchAPIRequest( "https://api.twitch.tv/kraken/clips/top?limit=50") if r.status_code != 200: await ctx.send(f"{msgs['games']['generic_error']} {r.status_code}") elif len(r.json()['clips']) < 1: await ctx.send(msgs['clips']['no_clips']) return else: clip = choice(r.json()['clips']) m = await ctx.send(msgs['clips']['clip_message'].format( user=clip['broadcaster']['display_name'], game=clip['game'], url=clip['url'].split('?')[0]))
async def top(self, ctx, cnt: int = 10): await ctx.trigger_typing() msgs = await lang.get_lang(ctx) e = discord.Embed(color=discord.Color(0x6441A4), title=msgs['games']['top_games']) r = http.TwitchAPIRequest( "https://api.twitch.tv/kraken/games/top?limit=10") r.raise_for_status() r = r.json()["top"] place = 1 for game in r: e.add_field(inline=False, name=f"`{place}.` {game['game']['name']}", value=msgs['games']['top_games_desc'].format( view_count=game['viewers'], channel_count=game['channels'])) place += 1 await ctx.send(embed=e)
async def _from(self, ctx, twitch_user: str, *args): await ctx.trigger_typing() msgs = await lang.get_lang(ctx) twitch_user = twitch_user.split('/')[-1] if self.regex.match(twitch_user) is None: return await ctx.send(msgs['notifs']['malformed_user']) trending = "" if "--trending" in args: trending = "&trending=true" r = http.TwitchAPIRequest( "https://api.twitch.tv/kraken/clips/top?limit=50&channel=" + twitch_user + trending) if r.status_code != 200: await ctx.send(f"{msgs['games']['generic_error']} {r.status_code}") elif len(r.json()['clips']) < 1: await ctx.send(msgs['clips']['no_clips']) return else: clip = choice(r.json()['clips']) m = await ctx.send(msgs['clips']['clip_message'].format( user=clip['broadcaster']['display_name'], game=clip['game'], url=clip['url'].split('?')[0]))
async def remove(self, ctx, discord_channel: discord.TextChannel, twitch_user: str = None, *, flags=""): """Deletes notifications for a Twitch user in the specified channel.""" msgs = await lang.get_lang(ctx) flags = flags.split(" ") if not ctx.guild: return await ctx.send(msgs['permissions']['no_pm']) if not ctx.message.author.permissions_in( ctx.message.channel).manage_guild: return await ctx.send(msgs['permissions']['user_need_perm'].format( permission="Manage Server")) if twitch_user == None: notifs = r.table('notifications').filter(r.row['channel'].eq( str(discord_channel.id))) cnt = notifs.count().run(self.bot.rethink, durability="soft") await ctx.send( f":warning: {msgs['notifs']['bulk_delete_confirm']}".format( count=cnt, channel=discord_channel.mention)) try: m = await self.bot.wait_for( 'message', check=lambda m: m.author.id == ctx.author.id and m.channel. id == ctx.channel.id, timeout=60) if not "yes" in m.clean_content.lower(): return await ctx.send(msgs['notifs']['command_cancelled']) notifs.delete().run(self.bot.rethink, durability="soft", noreply=True) return await ctx.send( msgs['notifs']['bulk_delete_success'].format( count=cnt, channel=discord_channel.mention)) except asyncio.TimeoutError: return await ctx.send(msgs['notifs']['response_timeout']) else: username = twitch_user.split('/')[-1] if self.regex.match(username) is None: return await ctx.send(msgs['notifs']['malformed_user']) try: if not "--force-user-id" in flags: s = http.TwitchAPIRequest( "https://api.twitch.tv/helix/users?login="******"data": [{"id": twitch_user}]} r.table('notifications').filter( (r.row['streamer'] == s['data'][0]['id']) & (r.row['channel'] == str(discord_channel.id))).delete( ).run(self.bot.rethink, durability="soft", noreply=True) except KeyError: await ctx.send(msgs['notifs']['del_fail']) except IndexError: await ctx.send(msgs['notifs']['del_fail']) except: await ctx.send(traceback.format_exc()) else: await ctx.send(msgs['notifs']['del_success'].format( channel=discord_channel.mention, user=username))
async def add(self, ctx, discord_channel: discord.TextChannel = None, twitch_user: str = None, *, msg: str = None): """Sets up notifications for a Twitch user in the specified channel.""" msgs = await lang.get_lang(ctx) try: if not ctx.guild: return await ctx.send(msgs['permissions']['no_pm']) if not ctx.message.author.permissions_in( ctx.message.channel).manage_guild: return await ctx.send( msgs['permissions']['user_need_perm'].format( permission="Manage Server")) await ctx.send( "**Notice:** The preferred method for adding notifications is now through the dashboard. We recommend that you use <https://dash.twitchbot.io> for a better experience." ) prem_check = requests.get( f"https://api.twitchbot.io/premium/{ctx.author.id}", headers={"X-Auth-Key": settings.DashboardKey}) if prem_check.json().get( 'premium') != True or prem_check.status_code != 200: channels = list(map(lambda c: str(c.id), ctx.guild.channels)) serv_notifs = r.table('notifications').filter( lambda obj: r.expr(channels).contains(obj[ 'channel'])).count().run(self.bot.rethink, durability="soft") if serv_notifs > 25: return await ctx.send(msgs['notifs']['limit_reached']) s = None username = None if discord_channel is None: await ctx.send(msgs['notifs']['prompt1']) try: m = await self.bot.wait_for( 'message', check=lambda m: m.channel == ctx.channel and m.author. id == ctx.author.id, timeout=60) discord_channel = discord.utils.find( lambda c: c.name.lower().startswith( m.clean_content.strip("#").lower()), ctx.guild.text_channels) if discord_channel is None: return await ctx.send( msgs['notifs']['text_channel_not_found']) except asyncio.TimeoutError: return await ctx.send(msgs['notifs']['response_timeout']) perms = discord_channel.permissions_for(ctx.guild.me) check_val = functions.CheckMultiplePerms(perms, "read_messages", "send_messages", "embed_links", "external_emojis") if check_val != True: return await ctx.send( msgs['permissions']['bot_need_perm'].format( permission=check_val)) if twitch_user == None: await ctx.send(msgs['notifs']['prompt2']) try: m = await self.bot.wait_for( 'message', check=lambda m: m.channel == ctx.channel and m.author. id == ctx.author.id, timeout=60) username = m.content.split('/')[-1] s = http.TwitchAPIRequest( "https://api.twitch.tv/helix/users?login="******"data" not in s.json().keys(): return await ctx.send( f"{msgs['games']['generic_error']} {s.status_code}" ) elif len(s.json().get('data', {})) == 0: return await ctx.send( msgs['notifs']['twitch_user_not_found']) elif s.status_code > 399: return await ctx.send( f"{msgs['games']['generic_error']} {s.status_code}" ) except asyncio.TimeoutError: return await ctx.send(msgs['notifs']['response_timeout']) else: username = twitch_user.split('/')[-1] s = http.TwitchAPIRequest( "https://api.twitch.tv/helix/users?login="******"{msgs['notifs']['invalid_data']} KeyError: f{str(e)}") except IndexError as e: return await ctx.send( f"{msgs['notifs']['invalid_data']} IndexError: f{str(e)}") if self.regex.match(username) is None: return await ctx.send(msgs['notifs']['malformed_user']) if msg == None: await ctx.send(msgs['notifs']['prompt3']) try: m = await self.bot.wait_for( 'message', check=lambda m: m.channel == ctx.channel and m.author. id == ctx.author.id, timeout=180) if m.content.lower() == 'default' or m.content.lower( ) == '`default`': msg = msgs['notifs']['default_msg'].format( channel=username) else: msg = m.content except asyncio.TimeoutError: return await ctx.send(msgs['notifs']['response_timeout']) try: object = { "channel": str(discord_channel.id), "streamer": s['id'], "name": username, "last_stream_id": None, "message": msg } existing_notif = r.table('notifications').filter( (r.row['streamer'] == s['id']) & (r.row['channel'] == str(discord_channel.id))) if existing_notif.count().run(self.bot.rethink, durability="soft") == 0: r.table('notifications').insert(object).run( self.bot.rethink, durability="soft", noreply=True) else: existing_notif.update(object).run(self.bot.rethink, durability="soft") return await ctx.send(msgs['notifs']['add_success'].format( user=username, channel=discord_channel.mention)) except KeyError as e: return await ctx.send( msgs['notifs']['twitch_user_not_found_alt']) except IndexError as e: return await ctx.send( msgs['notifs']['twitch_user_not_found_alt']) except: raise except: await ctx.send(traceback.format_exc())
async def user(self, ctx, *, user): try: await ctx.trigger_typing() msgs = await lang.get_lang(ctx) user = user.split('/')[-1] if self.regex.match(user) is None: return await ctx.send(msgs['notifs']['malformed_user']) e = discord.Embed(color=discord.Color(0x6441A4)) # get user info r = http.TwitchAPIRequest( "https://api.twitch.tv/helix/users?login="******"data") == [] or r.status_code == 400 or len( r.json()['data']) == 0: return await ctx.send( msgs['notifs']['twitch_user_not_found_alt']) r.raise_for_status() r = r.json()["data"][0] # get user streaming status s = http.TwitchAPIRequest( "https://api.twitch.tv/helix/streams?user_login="******"data"] # get user follows ft = http.TwitchAPIRequest( "https://api.twitch.tv/helix/users/follows?first=1&to_id=" + r['id']) ft.raise_for_status() # get user following ff = http.TwitchAPIRequest( "https://api.twitch.tv/helix/users/follows?first=1&from_id=" + r['id']) ff.raise_for_status() emote = self.badges.get(r['type'] or r['broadcaster_type'], '') e.set_author(icon_url=r["profile_image_url"], name=r["display_name"], url="https://twitch.tv/{}".format(r["login"])) e.set_thumbnail(url=r["profile_image_url"]) e.title = r["login"] + emote e.description = r["description"] e.add_field(name=msgs['users']['followers'], value="{:,}".format(ft.json()['total'])) e.add_field(name=msgs['users']['following'], value="{:,}".format(ff.json()['total'])) e.add_field(name=msgs['users']['views'], value="{:,}".format(r["view_count"])) if not s == []: s = s[0] # get stream tags t = http.TwitchAPIRequest( f"https://api.twitch.tv/helix/streams/tags?broadcaster_id={r['id']}" ) t.raise_for_status() tag_text = [] for tag in t.json()['data']: if not tag['is_auto']: tag_text.append( f"[{tag['localization_names']['en-us']}](https://www.twitch.tv/directory/all/tags/{tag['tag_id']})" ) if tag_text == []: tag_text = ["No stream tags"] e.add_field(inline=False, name=msgs['users']['tags'], value=", ".join(tag_text)) # get game info g = http.TwitchAPIRequest( f"https://api.twitch.tv/helix/games?id={s['game_id']}") g.raise_for_status() try: g = g.json()["data"][0] except: g = {"name": msgs['users']['unknown']} e.add_field( inline=False, name=msgs['users']['live'], value=f"**{s['title']}**\n" + msgs['users']['playing'].format( game=g['name'], view_count=s['viewer_count']) + f"\n\n**[{msgs['users']['watch_on_twitch']}](https://twitch.tv/{user})**" ) e.set_image( url=s['thumbnail_url'].format(width=1920, height=1080) + f"?{secrets.token_urlsafe(5)}") else: e.add_field( inline=False, name=msgs['users']['not_live'], value= f"[{msgs['users']['view_profile']}](https://twitch.tv/{user})" ) e.set_image(url=r['offline_image_url']) e.set_footer(text=f"{msgs['users']['streamer_id']} {r['id']}") await ctx.send(embed=e) except: await ctx.send(traceback.format_exc())