def __init__(self, season_year=None): if season_year is None: self.season_year = self.DEFAULT_SEASON_YEAR else: self.season_year = season_year self.full_schedule = get_cache( path=f'{self.season_year}_full_schedule', fn=client.season_schedule, season_end_year=self.season_year) self.player_total = get_cache(path=f'{self.season_year}_player_total', fn=client.players_season_totals, season_end_year=self.season_year) self.SLUG_NAME_CONVERTER = dict( (player['slug'], player['name'].lower()) for player in self.player_total) self.NAME_SLUG_CONVERTER = dict( (player['name'].lower(), player['slug']) for player in self.player_total) self.game_dates = sorted( list( set(game['start_time'].astimezone(EST).date() for game in self.full_schedule)))
async def created_date(user: str, *, data: 'cache.CacheStore'=None) -> Optional[datetime]: if data is None: async with cache.get_cache() as data: return await created_date(user, data=data) if not await data.twitch_load_id(user): return None id: Optional[str] = await data.twitch_get_id(user) if id is None: return None with suppress(aiohttp.ClientConnectionError, aiohttp.ClientResponseError, asyncio.TimeoutError): response: aiohttp.ClientResponse userData: Dict[str, Any] uri: str = f'/kraken/users/{id}' response, userData = await get_call(None, uri) createdDate: datetime try: createdDate = datetime.strptime(userData['created_at'], '%Y-%m-%dT%H:%M:%S.%fZ') except ValueError: createdDate = datetime.strptime(userData['created_at'], '%Y-%m-%dT%H:%M:%SZ') return createdDate return None
async def handle_emote_set(emoteSet: Set[int]) -> None: if lock.locked(): return async with lock: cacheStore: cache.CacheStore async with cache.get_cache() as cacheStore: await cacheStore.twitch_load_emotes(emoteSet, background=True)
async def update(channel: str, *, status: Optional[str]=None, game: Optional[str]=None, data: 'cache.CacheStore'=None) -> Optional[bool]: if data is None: async with cache.get_cache() as data: return await update(channel, status=status, game=game, data=data) if not await data.twitch_load_id(channel): return None id: Optional[str] = await data.twitch_get_id(channel) if id is None: return None postData: Dict[str, str] = {} if isinstance(status, str): postData['channel[status]'] = status or ' ' if isinstance(game, str): postData['channel[game]'] = game if not postData: return None with suppress(aiohttp.ClientConnectionError, aiohttp.ClientResponseError, asyncio.TimeoutError): response: aiohttp.ClientResponse jsonData: Optional[Dict[str, Any]] response, jsonData = await put_call( channel, f'/kraken/channels/{id}', headers={'Content-Type': 'application/x-www-form-urlencoded'}, data=postData) return response.status == 200 return None
async def active_streams(channels: Iterable[str], *, data: 'cache.CacheStore'=None ) -> Optional[OnlineStreams]: if data is None: async with cache.get_cache() as data: return await active_streams(channels, data=data) with suppress(aiohttp.ClientConnectionError, aiohttp.ClientResponseError, asyncio.TimeoutError): channels = set(channels) if not await data.twitch_load_ids(channels): return None ids: Dict[str, Optional[str]] = await data.twitch_get_ids(channels) allChannels: List[str] = [id for id in ids.values() if id is not None] if not allChannels: return {} uri: str = '/kraken/streams?limit=100&channel=' + ','.join(allChannels) response: aiohttp.ClientResponse streamsData: Dict[str, Any] response, streamsData = await get_call(None, uri) if response.status != 200: return None online: Dict[str, TwitchStatus] = {} _handle_streams(streamsData['streams'], online) for offset in range(100, streamsData['_total'], 100): await asyncio.sleep(0.05) offsetUri: str = uri + '&offset=' + str(offset) response, streamsData = await get_call(None, offsetUri) if response.status != 200: break _handle_streams(streamsData['streams'], online) return online return None
async def channel_community(channel: str, *, data: 'cache.CacheStore'=None ) -> Optional[List[TwitchCommunity]]: if data is None: async with cache.get_cache() as data: return await channel_community(channel, data=data) if not await data.twitch_load_id(channel): return None id: Optional[str] = await data.twitch_get_id(channel) if id is None: return None uri: str = f'/kraken/channels/{id}/communities' with suppress(ConnectionError, aiohttp.ClientResponseError): response: aiohttp.ClientResponse communities: Optional[Dict[str, List[Dict[str, str]]]] response, communities = await get_call(None, uri) if response.status not in [200, 204]: return None if communities is None: return [] chanCommunities: List[TwitchCommunity] = [] community: Dict[str, str] for community in communities['communities']: chanCommunities.append( TwitchCommunity(community['_id'], community['name'])) return chanCommunities return None
async def checkStreamsAndChannel(timestamp: datetime) -> None: if not bot.globals.channels: return dataCache: cache.CacheStore async with cache.get_cache() as dataCache: channels: Dict[str, data.Channel] = copy.copy(bot.globals.channels) onlineStreams: Optional[twitch.OnlineStreams] onlineStreams = await twitch.active_streams(channels.keys(), data=dataCache) if onlineStreams is None: return communityIds: Set[str] = set() channel: str for channel in onlineStreams: chat: data.Channel = channels[channel] chat.twitchCache = timestamp (chat.streamingSince, chat.twitchStatus, chat.twitchGame, chat.community) = onlineStreams[channel] communityId: str for communityId in chat.community: communityIds.add(communityId) for channel in channels: if channel in onlineStreams: continue channels[channel].streamingSince = None await dataCache.twitch_load_community_ids(communityIds)
async def checkTwitchIds(timestamp: datetime) -> None: if not bot.globals.channels: return dataCache: cache.CacheStore async with cache.get_cache() as dataCache: channels: List[str] = list(bot.globals.channels.keys()) await dataCache.twitch_load_ids(channels)
async def refreshBttvRandomBroadcasterEmotes(timestamp: datetime) -> None: channels: Dict[str, data.Channel] = copy.copy(bot.globals.channels) channel: data.Channel for channel in channels.values(): if 'bttvLock' not in channel.sessionData: channel.sessionData['bttvLock'] = asyncio.Lock() dataCache: cache.CacheStore async with cache.get_cache() as dataCache: ttlChannels: Dict[str, int] ttlChannels = await dataCache.bttv_get_cached_broadcasters() toUpdate: List[data.Channel] toUpdate = [ chan for chan in channels.values() if (chan.channel not in ttlChannels or ttlChannels[chan.channel] < 30) and chan.isStreaming and not chan.sessionData['bttvLock'].locked() ] if not toUpdate: toUpdate = [ chan for chan in channels.values() if (chan.channel not in ttlChannels or ttlChannels[chan.channel] < 30) and not chan.isStreaming and not chan.sessionData['bttvLock'].locked() ] if toUpdate: channel = random.choice(toUpdate) async with channel.sessionData['bttvLock']: await dataCache.bttv_load_broadcaster_emotes(channel.channel, background=True)
def per_day_game_data(self): for date in self.game_dates: data = get_cache(path=f'{self.season_year}_{date}_game_data', fn=client.player_box_scores, day=date.day, month=date.month, year=date.year) yield (date, data)
async def refreshTwitchGlobalEmotes(timestamp: datetime) -> None: if twitchLock.locked(): return dataCache: cache.CacheStore async with twitchLock, cache.get_cache() as dataCache: emoteSet: Set[int] = await dataCache.twitch_get_bot_emote_set() if emoteSet is None: return await dataCache.twitch_load_emotes(emoteSet, background=True)
async def set_channel_community(channel: str, communities: List[str], *, data: 'cache.CacheStore'=None ) -> Optional[List[str]]: if data is None: async with cache.get_cache() as data: return await set_channel_community(channel, communities, data=data) if not await data.twitch_load_id(channel): return None id: Optional[str] = await data.twitch_get_id(channel) if id is None: return None uri: str response: aiohttp.ClientResponse jsonData: Optional[Dict[str, Any]] if communities: communityIds: Set[str] = set() for communityName in communities: if not await data.twitch_load_community_name(communityName): return None communityId: Optional[str] communityId = await data.twitch_get_community_id(communityName) if communityId is None: continue communityIds.add(communityId) if not communityIds: return [] uri = f'/kraken/channels/{id}/communities' jsonData = {'community_ids': list(communityIds)} headers: Dict[str, str] = {'Content-Type': 'application/json'} with suppress(aiohttp.ClientConnectionError, aiohttp.ClientResponseError, asyncio.TimeoutError): response, jsonData = await put_call(channel, uri, headers=headers, data=json.dumps(jsonData)) return list(communityIds) if response.status == 204 else None else: uri = f'/kraken/channels/{id}/community' with suppress(aiohttp.ClientConnectionError, aiohttp.ClientResponseError, asyncio.TimeoutError): response, jsonData = await delete_call(channel, uri) return [] if response.status == 204 else None return None
async def checkOfflineChannels(timestamp: datetime) -> None: channels: Dict[str, data.Channel] = copy.copy(bot.globals.channels) if not channels: return channel: data.Channel for channel in channels.values(): if 'offlineLock' not in channel.sessionData: channel.sessionData['offlineLock'] = asyncio.Lock() cacheDuration: timedelta = timedelta(seconds=300) offlineChannels: List[data.Channel] offlineChannels = [ch for ch in channels.values() if (not ch.isStreaming and timestamp - ch.twitchCache >= cacheDuration and not ch.sessionData['offlineLock'].locked())] if not offlineChannels: return if 'offlineCheck' not in bot.globals.globalSessionData: bot.globals.globalSessionData['offlineCheck'] = [] bot.globals.globalSessionData['offlineCheck'] = [ t for t in bot.globals.globalSessionData['offlineCheck'] if t >= timestamp - timedelta(minutes=1)] if len(bot.globals.globalSessionData['offlineCheck']) >= 40: return chat: data.Channel = random.choice(offlineChannels) chat.twitchCache = timestamp dataCache: cache.CacheStore async with chat.sessionData['offlineLock'], cache.get_cache() as dataCache: current: Optional[twitch.TwitchStatus] current = await twitch.channel_properties(chat.channel) if current is None: chat.twitchCache = (timestamp - cacheDuration + timedelta(seconds=60)) return (chat.streamingSince, chat.twitchStatus, chat.twitchGame, shouldBeNone) = current communities: Optional[List[twitch.TwitchCommunity]] communities = await twitch.channel_community(chat.channel) if communities is not None: community: twitch.TwitchCommunity chat.community = [community.id for community in communities] await asyncio.gather( *[dataCache.twitch_save_community(comm.id, comm.name) for comm in communities] ) bot.globals.globalSessionData['offlineCheck'].append(timestamp)
async def num_followers(user: str, *, data: 'cache.CacheStore'=None) -> Optional[int]: if data is None: async with cache.get_cache() as data: return await num_followers(user, data=data) if not await data.twitch_load_id(user): return None id: Optional[str] = await data.twitch_get_id(user) if id is None: return 0 with suppress(aiohttp.ClientConnectionError, aiohttp.ClientResponseError, asyncio.TimeoutError): response: aiohttp.ClientResponse followerData: Dict[str, Any] uri: str = f'/kraken/users/{id}/follows/channels?limit=1' response, followerData = await get_call(None, uri) return int(followerData['_total']) return None
async def autoRepeatMessage(timestamp: datetime) -> None: dataCache: cache.CacheStore async with lock, cache.get_cache() as dataCache: tasks: List[Awaitable[Any]] = [] row: Tuple[str, str, str] async for row in dataCache.getAutoRepeatToSend(): broadcaster: str name: str message: str broadcaster, name, message = row if broadcaster in bot.globals.channels: tasks.append(dataCache.sentAutoRepeat(broadcaster, name)) channel: data.Channel = bot.globals.channels[broadcaster] channel.send(message) if channel.isMod: tasks.append( timeout.record_timeout(channel, None, message, None, 'autorepeat')) if tasks: await asyncio.gather(*tasks)
async def channel_properties(channel: str, *, data: 'cache.CacheStore'=None ) -> Optional[TwitchStatus]: if data is None: async with cache.get_cache() as data: return await channel_properties(channel, data=data) if not await data.twitch_load_id(channel): return None id: Optional[str] = await data.twitch_get_id(channel) if id is None: return None uri: str = f'/kraken/channels/{id}' with suppress(aiohttp.ClientConnectionError, aiohttp.ClientResponseError, asyncio.TimeoutError): response: aiohttp.ClientResponse channel_: Optional[Dict[str, str]] response, channel_ = await get_call(None, uri) if response.status != 200: return None return TwitchStatus(None, channel_['status'], channel_['game'], []) return None
async def check_domain_redirect(chat: 'data.Channel', nick: str, message: Message, timestamp: datetime) -> None: dataCache: cache.CacheStore async with cache.get_cache() as dataCache: if await dataCache.twitch_num_followers(nick): return # Record all urls with users of no follows utils.logIrcMessage(f'{chat.ircChannel}#blockurl.log', f'{nick}: {message}', timestamp) session: aiohttp.ClientSession async with aiohttp.ClientSession() as session: match: Match[str] for match in re.finditer(parser.twitchUrlRegex, str(message)): originalUrl: str = match.group(0) url: str = originalUrl if (not url.startswith('http://') and not url.startswith('https://')): url = 'http://' + url headers = {'User-Agent': 'BotGotsThis/' + bot.config.botnick} try: response: aiohttp.ClientSession async with session.get(url, headers=headers) as response: isBadRedirect: bool = compare_domains(url, str(response.url), chat=chat, nick=nick, timestamp=timestamp) if isBadRedirect: await handle_different_domains(chat, nick, message) return except aiohttp.ClientConnectorError: pass except Exception: utils.logException(str(message), timestamp)
async def handle_different_domains(chat: 'data.Channel', nick: str, message: Message) -> None: dataCache: cache.CacheStore async with cache.get_cache() as dataCache: await timeout.timeout_user(dataCache, chat, nick, 'redirectUrl', 1, str(message), 'Blocked Redirected URL')
async def refreshBttvGlobalEmotes(timestamp: datetime) -> None: if bttvGlobalLock.locked(): return dataCache: cache.CacheStore async with bttvGlobalLock, cache.get_cache() as dataCache: await dataCache.bttv_load_global_emotes(background=True)