def run_enter(self, message: base.Message) -> Optional[str]: if self.data.exists('_ended'): if self.error_cooldown.fire(): raise base.UserError(f"Sorry @{message.user}, it's too late to enter! NotLikeThis") else: return None if self.data.exists(message.user.name): raise base.UserError(f"@{message.user} Don't worry, you're already entered.") self.data.set(message.user.name, cast(twitch.TwitchUser, message.user).display_name) return f"@{message.user} You've entered the giveaway, good luck!"
def run_aliascom(self, message: base.Message, name: str, target: str): name = normalize(name) target = normalize(target) if self.data.exists(name): raise base.UserError(f'!{name} already exists.') if hasattr(self, 'run_' + name): raise base.UserError(f"Can't use !{name} for a command.") lookup = self._lookup(target) if not lookup: raise base.UserError(f"!{target} isn't a custom command.") target, _ = lookup self.data.set(name, {'alias': target}) if self.discord: msg = f'**{message.user}** created **!{name}** as an alias to **!{target}**.' self.discord.embed(EMBED_COLOR, msg) return f'Added !{name} as an alias to !{target}.'
def finish_authorization(self, code: str) -> None: try: access_token, refresh_token = self._fetch({ 'grant_type': 'authorization_code', 'code': code, 'redirect_uri': secret.TWITCH_REDIRECT_URI, }) except base.ServerError: logger.exception("Couldn't exchange code for token") raise response = requests.get('https://api.twitch.tv/helix/users', headers={ 'Client-ID': secret.TWITCH_CLIENT_ID, 'Authorization': f'Bearer {access_token}' }) if response.status_code != 200: logger.error( "Couldn't fetch user info with new bearer token: %d %s", response.status_code, response.text) raise base.ServerError(f'{response.status_code} {response.text}') body = response.json() login = body['data'][0]['login'] if login != self.streamer_username.lower(): display_name = body['data'][0]['display_name'] raise base.UserError( f"You're logged into Twitch as {display_name}. Please log in " f"as {self.streamer_username} to authorize the bot.") self.data.set('access_token', access_token) self.data.set('refresh_token', refresh_token) self.auth_finished.set()
def run_roulette(self, message: base.Message, points: int) -> str: starting_points = int(self.data.get(message.user.name, default='0')) if starting_points < points: if not starting_points: raise base.UserError("You don't have any points!") elif starting_points == 1: raise base.UserError('You only have 1 point.') raise base.UserError(f'You only have {starting_points} points.') if random.randint(0, 1): new_points = starting_points + points self.data.set(message.user.name, str(new_points)) return f'{message.user} won {points} points and now has {new_points} points!' else: new_points = starting_points - points self.data.set(message.user.name, str(new_points)) return f'{message.user} lost {points} points and now has {new_points} points.'
def run_resetcount(self, name: str, count: Optional[int]) -> str: if count is None: count = 0 name = normalize(name) lookup = self._lookup(name) if not lookup: raise base.UserError(f"!{name} doesn't exist") name, _ = lookup self.data.set_subkey(name, 'count', str(count)) return f'Reset !{name} counter to {count}.'
def run_addcom(self, message: base.Message, name: str, text: str) -> str: name = normalize(name) if self.data.exists(name): raise base.UserError(f'!{name} already exists.') if hasattr(self, 'run_' + name): raise base.UserError(f"Can't use !{name} for a command.") self.data.set( name, { 'response': text, 'count': '0', 'cooldowns': repr( cooldown.GlobalAndUserCooldowns( datetime.timedelta(seconds=5), None)), }) if self.discord: msg = f'**{message.user}** created the command **!{name}**:\n\n{text}' self.discord.embed(EMBED_COLOR, msg) return f'Added !{name}.'
def run( self, event: impbot.connections.twitch_eventsub.PointsRewardRedemption ) -> Optional[str]: if datetime.now() > END_TIME: if self.error_cooldown.fire(): raise base.UserError( f"Sorry @{event.user}, it's too late to enter! NotLikeThis" ) else: return None entries = int(self.data.get(event.user.name, default='0')) if entries == MAX_ENTRIES: raise base.UserError( f'Sorry @{event.user}, you already have the max {MAX_ENTRIES} entries.' ) entries += 1 self.data.set(event.user.name, str(entries)) if entries == MAX_ENTRIES: return ( f"@{event.user} You've entered {entries} times now -- that's the maximum, " "good luck!") else: return None
def run_followage(self, message: base.Message, who: Optional[str]): if not who: who = message.user.name if who.startswith('@'): who = who[1:] try: from_id = self.twitch_util.get_channel_id(who) except KeyError: raise base.UserError(f"@{message.user} {who} isn't a Twitch user.") to_id = self.twitch_util.get_channel_id(self.streamer_username) body = self.twitch_util.helix_get('users/follows', { 'from_id': from_id, 'to_id': to_id }) if not body['data']: if who.lower() == message.user.name.lower(): return f"@{message.user} You aren't following {self.streamer_username}." else: return f"{who} isn't following {self.streamer_username}." data = body['data'][0] if who.lower() == message.user.name.lower(): name_has = f"@{message.user} You've" else: name_has = f'{data["from_name"]} has' since_str = data['followed_at'] since = date.fromisoformat(since_str[:len('YYYY-MM-DD')]) if (date.today() - since) < timedelta(days=365): since_str = since.strftime('%B %d').replace(' 0', ' ') else: since_str = since.strftime('%B %d, %Y').replace(' 0', ' ') if since == date.today(): duration = 'today' elif since == date.today() - timedelta(days=1): duration = 'yesterday' elif since < date.today(): days = (date.today() - since).days duration = f'{days:,} days ago' else: duration = 'the future??' return ( f'{name_has} been following {self.streamer_username} since {since_str} ({duration}).' )
def run(self, message: base.Message) -> Optional[str]: # If CommandHandler's check() passes, this is a built-in like !addcom, so let # CommandHandler's run() dispatch to it. if super().check(message): # As it happens, all the builtins are for mods only, so we'll do that check here. # TODO: Real per-command ACLs. if not (message.user.moderator or message.user.admin): raise base.UserError("You can't do that.") return super().run(message) # Otherwise, it's a custom command so we do our own thing. # self.lookup is guaranteed non-None by check(). name, comm = cast(Tuple[str, CommandDict], self.lookup) if 'cooldowns' in comm: cooldowns = eval(comm['cooldowns']) if not cooldowns.fire(message.user): return None self.data.set_subkey(name, 'cooldowns', repr(cooldowns)) count = int(comm['count']) + 1 self.data.set_subkey(name, 'count', str(count)) return comm['response'].replace('(count)', f'{count:,}')
def run_delcom(self, message: base.Message, name: str) -> str: name = normalize(name) try: comm = self.data.get_dict(name) except KeyError: raise base.UserError(f"!{name} doesn't exist.") self.data.unset(name) if 'alias' in comm: target = comm['alias'] if self.discord: self.discord.embed( EMBED_COLOR, f'**{message.user}** deleted the command **!{name}** (alias to **!{target}**).' ) return f'Deleted !{name}. (It was an alias to !{target}.)' else: if self.discord: msg = f'**{message.user}** deleted the command **!{name}**.' fields = {'Old response': comm['response']} if '(count)' in comm['response']: fields['Count'] = comm['count'] self.discord.embed(EMBED_COLOR, msg, fields) return f'Deleted !{name}.'
def game_id(self, game_name: str) -> str: body = self.helix_get('games', {'name': game_name}) if not body['data']: raise base.UserError(f'No such game "{game_name}"') return body['data'][0]['id']