def getTwitchInformation(): twitchAppId = getConfigInformationAsString("TwitchAPI", "TwitchAppId") twitchAppSecret = getConfigInformationAsString("TwitchAPI", "TwitchAppSecret") broadcasterId = getConfigInformationAsString("TwitchAPI", "TwitchBoradcastId") twitch = Twitch(twitchAppId, twitchAppSecret) twitch.authenticate_app([]) return twitch.get_channel_information(broadcasterId)
def start(bot): target_scope = [ AuthScope.ANALYTICS_READ_GAMES, AuthScope.BITS_READ, AuthScope.CHANNEL_EDIT_COMMERCIAL, AuthScope.CHANNEL_MANAGE_BROADCAST, AuthScope.CHANNEL_MANAGE_POLLS, AuthScope.CHANNEL_MANAGE_PREDICTIONS, AuthScope.CHANNEL_MANAGE_REDEMPTIONS, AuthScope.CHANNEL_MODERATE, AuthScope.CHANNEL_READ_HYPE_TRAIN, AuthScope.CHANNEL_READ_POLLS, AuthScope.CHANNEL_READ_PREDICTIONS, AuthScope.CHANNEL_READ_REDEMPTIONS, AuthScope.CHANNEL_READ_SUBSCRIPTIONS, AuthScope.CHANNEL_SUBSCRIPTIONS, AuthScope.CHAT_EDIT, AuthScope.CHAT_READ, AuthScope.CLIPS_EDIT, AuthScope.MODERATION_READ, AuthScope.USER_EDIT_BROADCAST, AuthScope.USER_MANAGE_BLOCKED_USERS, AuthScope.USER_READ_BLOCKED_USERS, AuthScope.USER_READ_BROADCAST, AuthScope.USER_READ_FOLLOWS, AuthScope.USER_READ_SUBSCRIPTIONS ] twitch = Twitch(config['client_id'], config['client_secret']) twitch.authenticate_app([]) twitch.set_user_authentication(config['twitch_token'], target_scope, config['refresh_token']) def callback_bits(uuid: UUID, data: dict): print(f"Got callback for UUID {str(uuid)}") print(data) pubsub.bits(bot, data['data']) def callback_subscriptions(uuid: UUID, data: dict): print(f"Got callback for UUID {str(uuid)}") print(data) pubsub.subscription(bot, data['data']) def callback_points(uuid: UUID, data: dict): print(f"Got callback for UUID {str(uuid)}") print(data) pubsub.points(bot, data['data']) user_id = twitch.get_users(logins=['will_am_i_'])['data'][0]['id'] pubsubs = PubSub(twitch) pubsubs.start() bits_uuid = pubsubs.listen_bits(user_id, callback_bits) subscriptions_uuid = pubsubs.listen_channel_subscriptions( user_id, callback_subscriptions) points_uuid = pubsubs.listen_channel_points(user_id, callback_points) #pubsubs.stop()
def setup_platform( hass: HomeAssistant, config: ConfigType, add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up the Twitch platform.""" channels = config[CONF_CHANNELS] client_id = config[CONF_CLIENT_ID] client_secret = config[CONF_CLIENT_SECRET] oauth_token = config.get(CONF_TOKEN) client = Twitch(app_id=client_id, app_secret=client_secret) client.auto_refresh_auth = False try: client.authenticate_app(scope=OAUTH_SCOPES) except TwitchAuthorizationException: _LOGGER.error("INvalid client ID or client secret") return if oauth_token: try: client.set_user_authentication( token=oauth_token, scope=OAUTH_SCOPES, validate=True ) except MissingScopeException: _LOGGER.error("OAuth token is missing required scope") return except InvalidTokenException: _LOGGER.error("OAuth token is invalid") return channels = client.get_users(logins=channels) add_entities( [TwitchSensor(channel=channel, client=client) for channel in channels["data"]], True, )
'bananaslamjamma', 'gunnardota2', 'monkeys_forever', 'xcaliburye', 'gorgc', 'purgegamers' ] streamerstate = {} for user_ids in streamers: streamerstate[user_ids] = 0 check_game = operations.check_game bot = operations.send_telegram_photo twitch = Twitch(ClientID, ClientSecret) while True: twitch.authenticate_app([]) for streamername in streamers: data = twitch.get_streams(user_login=[streamername]) if data['data']: streamer = data['data'][0]['user_name'] game_id = data['data'][0]['game_id'] game_name = data['data'][0]['game_name'] stream_title = data['data'][0]['title'] viewers = data['data'][0]['viewer_count'] check_game(game_id, game_name) stream_start_time = datetime.datetime.strptime( data['data'][0]['started_at'], '%Y-%m-%dT%H:%M:%SZ') time_now = datetime.datetime.utcnow() photo_url = f"https://static-cdn.jtvnw.net/previews-ttv/live_user_{streamername}-720x480.jpg" uptime = humanize.precisedelta(time_now - stream_start_time, minimum_unit='seconds')
class TwitchNotifier(commands.Cog): def __init__(self, bot): self.bot: 'PixlBot' = bot self.config = bot.config['TwitchNotifier'] self.backend = FileBackend('db') self.backend.autocommit = True self.bot.logger.info("Twitch notifier plugin ready") self.uuids = [] self.online_uuids = [] self.sslcontext = ssl.SSLContext() self.sslcontext.load_cert_chain(self.config['cert_path'], self.config['key_path']) self._twitch_init_() def _twitch_init_(self): self.bot.logger.info("Registering with Twitch...") self.twitch = Twitch(self.config['id'], self.config['secret']) self.twitch.authenticate_app([]) self.bot.logger.info( f"Registering webhook endpoint {self.config['myurl']} ...") self.hook = TwitchWebHook(self.config['myurl'], self.config['id'], self.config['port'], ssl_context=self.sslcontext) self.hook.authenticate(self.twitch) self.bot.logger.info("Clearing all hook subscriptions...") self.hook.unsubscribe_all(self.twitch) # Clear all subs on startup self.hook.start() self._register_all() def _login_to_id(self, name: str) -> Optional[str]: """Returns the twitch ID for a given login name, or None if the name couldn't be resolved.""" try: res: dict = self.twitch.get_users(logins=[name]) except TwitchBackendException as e: self.bot.logger.error(f"Backend error fetching user! {e}") return None if len(res) == 0: return None else: return res['data'][0]['id'] def _register_all(self): """Attempts to register stream_changed callbacks for all configured users.""" self.bot.logger.info("Registering callbacks for all watched users..") users = self.backend.filter(TwitchWatchedUser, {'twitch_name': { "$exists": True }}) if not users: self.bot.logger.info("No users to watch. No callbacks registered.") else: for u in users: self.bot.logger.info(f"Registering: {u['twitch_name']}") success, uuid = self.hook.subscribe_stream_changed( u['twitch_id'], self._cb_stream_changed) if success and uuid: self.uuids.append(uuid) self.bot.logger.info( f"{success}: registered subscription UUID: {uuid}") else: self.bot.logger.error( f"{success}: failed registering subscription: {uuid}") def _cb_stream_changed(self, uuid, data): """Callback for Twitch webhooks, fires on stream change event""" self.bot.logger.debug(f"Callback data for {uuid}: {data}") if data["type"] == "offline": if uuid in self.online_uuids: self.online_uuids.remove( uuid ) # Stupid twitch sending the same damn webhook multiple times... return else: self.bot.logger.debug( f"Ignoring duplicate offline callback for {uuid}") return elif data["type"] == "live": if uuid in self.online_uuids: self.bot.logger.debug( f"Ignoring duplicate live callback for {uuid}") return else: self.online_uuids.append(uuid) else: self.bot.logger.error( f"Got a callback type we can't handle: {data['type']}") return if uuid not in self.uuids: self.bot.logger.error( f"Got a callback for a UUID we're not tracking: {uuid}, my UUIDs: {self.uuids}" ) return try: item = self.backend.get(TwitchWatchedUser, {"twitch_id": data["user_id"]}) except TwitchWatchedUser.DoesNotExist: self.bot.logger.error( f"Got a callback for a USER we're not tracking: {data['user_id']} -> {data['user_name']}" ) return channel: discord.TextChannel = self.bot.get_channel( item['notify_channel']) width = 640 height = 360 url = data['thumbnail_url'].format(width=width, height=height) tu = self.twitch.get_users(data['user_id'])['data'][0] self.bot.logger.debug(tu) embed = discord.Embed( title=f"Now streaming {data['game_name']}", description=data['title'], color=discord.Color.green(), ) embed.set_image(url=url) embed.set_thumbnail(url=tu["profile_image_url"]) embed.set_author(name=item["twitch_name"], url=f"https://twitch.tv/{data['user_name']}") embed.add_field(name="Watch live at", value=f"https://twitch.tv/{data['user_name']}") self.bot.loop.create_task( channel. send( # This isn't an async function, so enqueue it manually embed=embed)) self.bot.logger.info( f"Successfully sent online notification for {data['user_id']}") @cog_ext.cog_subcommand( base="Twitchwatch", name="add_notification", description="Add a go live notification for Twitch", options=[twitch_name, notify_channel, notify_text], guild_ids=util.guilds) async def add_notification(self, ctx: SlashContext, twitch_name: str, notify_channel: discord.TextChannel, notify_text: str): twitch_id = self._login_to_id(twitch_name) try: self.backend.get(TwitchWatchedUser, {'twitch_name': twitch_name}) except TwitchWatchedUser.DoesNotExist: pass except TwitchWatchedUser.MultipleDocumentsReturned: self.bot.logger.error( "Multiple users returned - database inconsistent???") return if not twitch_id: await ctx.send(embed=mkembed( 'error', f"Unable to get the Twitch ID for the name {twitch_name}")) return await ctx.defer() # This bit can take a minute. success, uuid = self.hook.subscribe_stream_changed( twitch_id, self._cb_stream_changed) if success and uuid: self.uuids.append(uuid) self.bot.logger.info( f"{success}: registered subscription UUID: {uuid}") else: self.bot.logger.error( f"{success}: failed registering subscription: {uuid}") await ctx.send("Bluh, couldn't register the webhook with twitch :(" ) return item = TwitchWatchedUser({ 'twitch_name': twitch_name, 'twitch_id': twitch_id, 'discord_name': ctx.author.id, 'notify_channel': notify_channel.id, 'notify_text': notify_text, 'uuid': str(uuid) }) self.bot.logger.debug(f"DB object dump: {item.__dict__}") self.backend.save(item) await ctx.send(embed=mkembed("done", f"Notification added for {twitch_name}", channel=notify_channel.name)) @cog_ext.cog_subcommand( base="Twitchwatch", name="del_notification", description="Remove a go live notification for Twitch", options=[twitch_name], guild_ids=util.guilds) async def del_notification(self, ctx: SlashContext, twitch_name: str): try: item = self.backend.get(TwitchWatchedUser, {'twitch_name': twitch_name}) except TwitchWatchedUser.DoesNotExist: await ctx.send(embed=mkembed( "error", f"No notification exists for {twitch_name}")) return self.hook.unsubscribe(item['uuid']) self.bot.logger.info(f"Removing watch {item['uuid']}: {twitch_name}") self.backend.delete(item) if item['uuid'] in self.uuids: self.uuids.remove(item['uuid']) await ctx.send( embed=mkembed("done", f"Notification for {twitch_name} removed."))
class DiscordTwitchWebhook(): def __init__(self, twitch_appid, twitch_secret, callback_url): self.discord_username = "******" self.twitch = Twitch(twitch_appid, twitch_secret) self.callback_url = callback_url self.hook = TwitchWebHook(callback_url, twitch_appid, 8080) self.authenticated = False self.subscriptions = [] def authenticate(self): self.authenticated = False try: self.twitch.authenticate_app([]) self.hook.authenticate(self.twitch) except TwitchAuthorizationException: print("Twitch authentication failed") except RuntimeError: print("Webhook must be https") else: self.authenticated = True return self.authenticated def subscribe_user(self, user: DiscordTwitchCallback): if not self.authenticated: raise Exception #TODO handle more exceptions user_data = self.twitch.get_users(logins=[user.username]) user.data = user_data['data'][0] user.id = user.data['id'] if user not in self.subscriptions: ret, user.uuid = self.hook.subscribe_stream_changed( user.id, self.callback_stream_changed) if ret: self.subscriptions.append(user) else: print(f"Failed to subscribe to {user.username}") def unsubscribe_user(self, user: DiscordTwitchCallback): #TODO return def start(self): self.hook.start() def stop(self): self.hook.unsubscribe_all(self.twitch) self.hook.stop() def callback_stream_changed(self, uuid, twdata): print('Callback for UUID ' + str(uuid), twdata) user = next((user for user in self.subscriptions if user.uuid == uuid), None) if user == None: print("Callback failed") return if twdata['type'] == 'live': emb = self.create_embed(twdata) user.run_callback(emb) def create_embed(self, twdata): return Embed(title=f"{twdata['user_name']}", description=f"{twdata['user_name']} is streaming {twdata['game_name']}! Get in here!", color=6570404, url=f"https://twitch.tv/{twdata['user_name']}") \ .set_image(url=twdata['thumbnail_url'].format(width="1280", height="720"))
# basic twitch API authentication, this will yield a app token but not a user token auth_scope = [ AuthScope.BITS_READ, AuthScope.USER_EDIT, AuthScope.WHISPERS_READ, AuthScope.CHANNEL_READ_SUBSCRIPTIONS, AuthScope.CHANNEL_READ_STREAM_KEY, AuthScope.ANALYTICS_READ_EXTENSION, AuthScope.ANALYTICS_READ_GAMES, AuthScope.CHANNEL_EDIT_COMMERCIAL, AuthScope.CHANNEL_READ_HYPE_TRAIN, AuthScope.CHANNEL_MANAGE_BROADCAST, AuthScope.CHANNEL_READ_REDEMPTIONS, AuthScope.CLIPS_EDIT, AuthScope.USER_EDIT_BROADCAST, AuthScope.USER_READ_BROADCAST, AuthScope.USER_READ_EMAIL, AuthScope.USER_EDIT_FOLLOWS, AuthScope.CHANNEL_MODERATE, AuthScope.CHAT_EDIT, AuthScope.CHAT_READ, AuthScope.WHISPERS_READ, AuthScope.WHISPERS_EDIT, AuthScope.MODERATION_READ, AuthScope.CHANNEL_SUBSCRIPTIONS ] twitch = Twitch(APP_ID, APP_SECRET) twitch.authenticate_app(auth_scope) # since we want user information, we require a OAuth token, lets get one # you dont need to generate a fresh user token every time, you can also refresh a old one or get one using a different online service # for refreshing look here: https://github.com/Teekeks/pyTwitchAPI#user-authentication # please note that you have to add http://localhost:17563 as a OAuth redirect URL for your app, see the above link for more information # auth = UserAuthenticator( # twitch, auth_scope) # token, refresh_token = auth.authenticate() # this will open a webpage # print(token, refresh_token) # setting the user authentication so any api call will also use it twitch.set_user_authentication(TOKEN, auth_scope, REFRESH_TOKEN) # setting up the Webhook itself sslContext = ssl.SSLContext(protocol=ssl.PROTOCOL_TLS) hook = TwitchWebHook(PUBLIC_ADDR, APP_ID, PORT, ssl_context=sslContext) # this will use the highest authentication set, which is the user authentication. hook.authenticate(twitch)
class Streamer: global TWITCH_APP_ID global TWITCH_SECRET global StreamerName global StreamerID isID = False def __init__(self, TWITCH_APP_ID, TWITCH_SECRET, User, isID = False): self.reader = codecs.getreader('utf-8') self.TWITCH_APP_ID = TWITCH_APP_ID self.TWITCH_SECRET = TWITCH_SECRET self.twitch = Twitch(self.TWITCH_APP_ID, self.TWITCH_SECRET) self.StreamerName = User self.isID = isID self.LOCK = False if not self.isID: self.StreamerID = self.getUserID() def getUserID(self): if self.isID: return self.StreamerName self.twitch.authenticate_app([]) data = self.twitch.get_users(logins=[self.StreamerName]) return data['data'][0]['id'] def isStreaming(self): data = self.twitch.get_streams(user_id=self.StreamerID) if len(data['data']) != 0: if data['data'][0]['type'] == 'live': return True return False def getStreamLink(self): return 'https://www.twitch.tv/' + str(self.StreamerName.lower()) def lock(self): self.LOCK = True def unlock(self): self.LOCK = False def lockStatus(self): return self.LOCK def getGame(self): if self.isStreaming(): data = self.twitch.get_streams(user_id=self.StreamerID) game = data['data'][0]['game_name'] return game return False def getViewers(self): if self.isStreaming(): data = self.twitch.get_streams(user_id=self.StreamerID) viewers = data['data'][0]['viewer_count'] return viewers return False def getTitle(self): if self.isStreaming(): data = self.twitch.get_streams(user_id=self.StreamerID) title = data['data'][0]['title'] return title return False def getThumbnail(self): if self.isStreaming(): data = self.twitch.get_streams(user_id=self.StreamerID) thumbnail = data['data'][0]['thumbnail_url'] thumbnail = thumbnail.format(width='1920', height='1080') return thumbnail return False def getProfilePicture(self): data = self.twitch.get_users(logins=self.StreamerName) picture = data['data'][0]['profile_image_url'] return picture def getStreamerName(self): return self.StreamerName
class Twitch(commands.Cog): """Get live updates for your favourite twitch streamers """ def __init__(self, disclient, twitch_id, twitch_sec): self.disclient = disclient self.disclient.loop.create_task(self.get_online_streams()) self.twitch = Twitchy( twitch_id, twitch_sec, ) self.twitch.authenticate_app([]) self.time_format = '%Y-%m-%d %H:%M:%S' async def get_online_streams(self): await self.disclient.wait_until_ready() print('Looking for live Twitch streams!') while not self.disclient.is_closed(): try: # print("checking twitch") check = get_all_twitch_channels_to_check(3) if not check: await asyncio.sleep(60) continue if len(check) > 100: # need to split list into lengths of 100 in future # get_streams only takes 100 inputs at a time check = check[:99] ids = [str(x) for x in check.keys()] b = self.twitch.get_streams(user_id=ids) for stream in b["data"]: c = self.twitch.get_users(user_ids=stream["user_id"]) linkend = c['data'][0]['login'] link = f"https://www.twitch.tv/{linkend}" username = stream['user_name'] desc = stream['title'] msg = f"`{username}` is live! {link}" image_url = f"""{stream['thumbnail_url'].format( width=852, height=480)}?{str(datetime.datetime.now().timestamp())}""" embed = discord.Embed(title=msg, description=desc, color=discord.Color.purple()) embed.set_image(url=image_url) embed.add_field(name='Playing', value=stream['game_name']) check_time = datetime.datetime.strptime( stream['started_at'], '%Y-%m-%dT%H:%M:%SZ') check_time.strftime(self.time_format) if check[int(stream['user_id'])] != check_time: update_twitch_last_live(stream['user_id'], check_time) else: continue channels = get_channels_following_twitch_stream( stream['user_id']) if not channels: await asyncio.sleep(60) continue for chan in channels: channel = self.disclient.get_channel(int(chan)) # set ping roles up in the future # gid = channel.guild.id # rol = self.disclient.get_guild(gid).roles # tr = discord.utils.get(rol, name="twitch") await channel.send( embed=embed ) # content=tr.mention, for when roles are assigned except Exception as e: print(e) # delay 60 seconds before checking again await asyncio.sleep(60) @commands.command( aliases=['followstream', 'follow_stream', 'followtwitch']) @commands.guild_only() @commands.has_permissions(administrator=True) async def follow_twitch(self, ctx, stream): """Follows a twitch stream in this channel! Live updates will be posted here. .follow_twitch <username or link>""" channel = ctx.channel.id if "twitch.tv" in stream: stream = stream.split("/")[-1].lower() else: stream = stream.lower() user = self.twitch.get_users(logins=stream) print(user) if not user: await ctx.send( embed=error_embed(f"Failed to find Twitch user {stream}!")) return for d in user["data"]: ayed = str(d["id"]) add_channel(channel) add_twitch_channel_to_db(ayed) followed = follow_twitch_channel_db(channel, ayed) if followed: display_name = d['display_name'] profile_image = d['profile_image_url'] link = f"https://www.twitch.tv/{d['login']}" msg = f'This channel will now receive updates on when {display_name} goes live at {link}' embed = discord.Embed( title=f'Successfully Followed {display_name}!', description=msg, color=discord.Color.purple()) embed.set_thumbnail(url=profile_image) await ctx.send(embed=embed) else: await ctx.send(embed=error_embed( f"Failed to follow {stream} in this channel!")) @commands.command( aliases=['unfollowstream', 'unfollow_stream', 'unfollowtwitch']) @commands.guild_only() @commands.has_permissions(administrator=True) async def unfollow_twitch(self, ctx, stream): """Unfollows a twitch stream followed in this channel. .unfollow_twitch <username or link>""" channel = ctx.channel.id if "twitch.tv" in stream: stream = stream.split("/")[-1].lower() else: stream = stream.lower() user = self.twitch.get_users(logins=stream) if not user: await ctx.send( embed=error_embed(f"Failed to find Twitch user {stream}!")) return for d in user["data"]: ayed = str(d["id"]) unfollowed = unfollow_twitch_channel_db(channel, ayed) if unfollowed: await ctx.send(embed=success_embed( f"Unfollowed {stream} in this channel!")) else: await ctx.send(embed=error_embed( f"Failed to unfollow {stream} in this channel!")) @commands.command(name='twitch', aliases=['twitchs', 'twitches']) @commands.guild_only() async def twitches(self, ctx): """Returns a list of all twitch users followed in this server!""" guild = ctx.guild chans = get_all_twitch_followed_in_guild() chan_dict = {} for pair in chans: if pair[0] not in chan_dict: chan_dict.update({pair[0]: [pair[-1]]}) else: chan_dict[pair[0]].append(pair[-1]) msg = '' for channel in guild.channels: if channel.id in chan_dict: for twitch in chan_dict[channel.id]: twitch_str = str(twitch) twitch = self.twitch.get_users(user_ids=[twitch_str]) twitch = twitch['data'][0]['login'] spacing = 39 - len(channel.name + twitch) chan_str = f"`#{channel.name}{' ' * spacing}{twitch}`\n" msg = msg + chan_str if msg == '': await ctx.send( embed=error_embed('No Twitch streams followed in this server!') ) else: add_to_start = f"`Channel Name{' ' * 17}Twitch User`\n" msg = add_to_start + msg embed = discord.Embed( title=f'Twitch Streams Followed in {guild.name}!', description=msg, color=discord.Color.purple()) await ctx.send(embed=embed)