def command_check_suggestion(msg: twitchirc.ChannelMessage): cd_state = main.do_cooldown('check_suggestion', msg, global_cooldown=0, local_cooldown=30) if cd_state: return t = main.delete_spammer_chrs(msg.text).split(' ') if len(t) == 1: return f'@{msg.user}, Usage: check_suggestion <ID> or check_suggestion.' target = t[1] if target.isnumeric(): target = int(target) with main.session_scope() as session: suggestion = session.query(Suggestion).filter(Suggestion.id == target).first() if suggestion is None: return f'@{msg.user} Suggestion id {target!r} not found.' else: return (f'@{msg.user} ' f'{suggestion.humanize(suggestion.author.last_known_username == msg.user)}') else: with main.session_scope() as session: user = main.User.get_by_message(msg, no_create=True) if user is None: return f'@{msg.user}, You are a new user, you don\'t have any suggestions.' suggestions = (session.query(Suggestion) .filter(Suggestion.author == user) .filter(Suggestion.state.notin_([Suggestion.SuggestionState.done, Suggestion.SuggestionState.rejected, Suggestion.SuggestionState.not_a_suggestion]))) return (f'@{msg.user} Your suggestions: ' f'{", ".join([f"{s.id} ({s.nice_state()})" for s in suggestions])}')
def c_cookie_optin(self, msg: twitchirc.ChannelMessage): if msg.user.lower() in self.cookie_optin: self.cookie_optin.remove(msg.user.lower()) plugin_manager.channel_settings[plugin_manager.SettingScope.GLOBAL.name].update() with main.session_scope() as session: session.add(plugin_manager.channel_settings[plugin_manager.SettingScope.GLOBAL.name]) return f'@{msg.user} You have been removed from the cookie opt-in list.' else: self.cookie_optin.append(msg.user.lower()) plugin_manager.channel_settings[plugin_manager.SettingScope.GLOBAL.name].update() with main.session_scope() as session: session.add(plugin_manager.channel_settings[plugin_manager.SettingScope.GLOBAL.name]) return f'@{msg.user} You have been added to the cookie opt-in list.'
def command_resolve_suggestion(msg: twitchirc.ChannelMessage): t = main.delete_spammer_chrs(msg.text).split(' ', 3) if len(t) < 3: return f'@{msg.user}, Usage: resolve_suggestion <ID> <state> [notes...]' if not t[1].isnumeric(): return f'@{msg.user}, Unknown suggestion {t[1]!r}.' target = int(t[1]) state_names = [i.name for i in Suggestion.SuggestionState] if t[2] in state_names: state = Suggestion.SuggestionState[t[2]] else: main.bot.send( msg.reply( f'@{msg.user}, Invalid state: {t[2]!r}. Choose between ' f'{", ".join([repr(i.name) for i in Suggestion.SuggestionState])}' )) return if len(t) == 4: notes = t[3] else: notes = None with main.session_scope() as session: suggestion = session.query(Suggestion).filter( Suggestion.id == target).first() suggestion.state = state if notes is not None: suggestion.notes = notes session.add(suggestion) main.bot.send( msg.reply(f'@{msg.user} Modified suggestion id {target!r}, ' f'new state {state}, ' f'new notes {notes}.'))
def _post_init(self): global blacklists # load all entries with main.session_scope() as dank_circle: blacklists = BlacklistEntry.load_all(dank_circle) # initialize middleware main.bot.middleware.append(BlacklistMiddleware())
def command_manage_blacklists(self, msg: twitchirc.ChannelMessage): argv = main.delete_spammer_chrs(msg.text).rstrip(' ').split(' ', 1) if len(argv) == 1: return f'@{msg.user}, {plugin_help.all_help[plugin_help.SECTION_COMMANDS]["plonk"]}' text = argv[1] kw = self._parse_blacklist_args(text, msg) if isinstance(kw, str): return kw if kw['command'] is None: return f'@{msg.user}, No `command:...` provided.' if kw['user'] is None: return f'@{msg.user}, No `user:...` provided.' with main.session_scope() as session: if kw['scope'] == 'global': targets = main.User.get_by_name( kw['user'], session) if kw['user'] is not True else None if targets is None or len(targets) == 1: obj = BlacklistEntry( target=targets[0] if targets is not None else targets, command=kw['command'] if kw['command'] is not True else None, channel=None, expires_on=kw['expires'], is_active=True) blacklists.append(obj) session.add(obj) elif len(targets) == 0: return f'@{msg.user} Failed to find user: {kw["user"]}' else: return f'@{msg.user} Found multiple users possible with name {kw["user"]}' else: for ch in kw['scope']: targets = main.User.get_by_name( kw['user'], session) if kw['user'] is not True else None channels = main.User.get_by_name(ch, session) if len(channels) == 1: if targets is None or len(targets) == 1: obj = BlacklistEntry( target=targets[0] if targets is not None else targets, command=kw['command'] if kw['command'] is not True else None, channel=channels[0], expires_on=kw['expires'], is_active=True) blacklists.append(obj) session.add(obj) elif len(targets) == 0: return f'@{msg.user} Failed to find user: {kw["user"]}' else: return f'@{msg.user} Found multiple users possible with name {kw["user"]}' elif len(channels) == 0: return f'@{msg.user} Failed to find channel: {ch}' elif len(channels) > 1: return f'@{msg.user} Found multiple channels possible with name {ch}' return f'@{msg.user}, Added blacklist for command {kw["command"]} with scope {kw["scope"]} for {kw["user"]}'
def _mailbox_whatif(self, argv, msg): # "!mailbox" "whatif" "ID SCORE SCORE SCORE" # [2:] args = argv[-1].split(' ', 1) if len(args) != 2: return f'@{msg.user}, Usage: {argv[0]} {argv[1]} ID SCORE SCORE SCORE' id_ = args[0] matches = args[1] if not id_.isnumeric(): return f'@{msg.user}, Game ID must be a number.' with main.session_scope() as session: try: game = session.query(self.MailboxGame).filter( self.MailboxGame.id == int(id_)).one() except sqlalchemy.exc.NoResultFound: return f'@{msg.user}, Unable to fetch game ID {int(id_)!r}' match = GUESS_PATTERN.match(matches) if not match: return ( f'@{msg.user}, Could not extract scores from your message. ' f'Usage: {argv[0]} {argv[1]} ID SCORE SCORE SCORE') good_value = [int(i.rstrip(', -')) for i in match.groups()] settings = game.settings msgs = [] for i in game.guesses: platform, channel, user, guess = i.split(' ', 3) msg = main.StandardizedMessage(guess, user.strip('<>'), channel, main.Platform[platform.strip('[]')]) msgs.append(msg) possible_guesses = msgs print('possible', possible_guesses) good_guesses = self._find_good_guesses(possible_guesses, good_value, settings) print('good', good_guesses) best = self._best_guess(settings, good_guesses) print('best', best) print(settings) if len(best) == 0: return f'No matching guesses.' else: message = f'Best guesses would be {self._nice_best_guesses(best)}.' if len(message) > 500: msgs = [] for i in range(1, math.ceil(len(message) / 500) + 1): msgs.append(message[(i - 1) * 500:i * 500]) return msgs return message
def command_suggest(msg: twitchirc.ChannelMessage): cd_state = main.do_cooldown('suggest', msg, global_cooldown=0, local_cooldown=30) if cd_state: return t = main.delete_spammer_chrs(msg.text).rstrip(' ').split(' ', 1) if len(t) == 1: return f'@{msg.user}, Usage: suggest <text...>' s = Suggestion(author=main.User.get_by_message(msg), text=t[1], state=Suggestion.SuggestionState.new, notes='<no notes>') with main.session_scope() as session: session.add(s) return f'@{msg.user} Suggestion saved, hopefully. ID: {s.id}'
def _periodically_expire(self): while 1: time.sleep(30) # don't need to flush this right away. to_expire = [] while 1: try: o = expire_queue.get_nowait() to_expire.append(o) except queue.Empty: break if to_expire: with main.session_scope() as session, self.expire_lock: for e in to_expire: e: BlacklistEntry session.delete(e)
def _fetch_suggestions(rwq): print(f'fetch {user_id} p {page_num}') with main.session_scope() as session: suggestions = (session.query(Suggestion).filter( Suggestion.author_alias == user_id).offset( page_num * PAGE_SIZE).limit(PAGE_SIZE).all()) rwq.put( plugin_ipc.format_json({ 'type': 'suggestion_list', 'page': page_num, 'page_size': PAGE_SIZE, 'data': [{ 'text': suggestion.text, 'notes': suggestion.notes, 'state': suggestion.state.name, 'id': suggestion.id } for suggestion in suggestions] }))
def _fetch_suggestions(rwq): print(f'fetch suggestion {suggestion_id}') with main.session_scope() as session: suggestion = (session.query(Suggestion).filter( Suggestion.id == suggestion_id).first()) rwq.put( plugin_ipc.format_json({ 'type': 'suggestion_list', 'page': 0, 'page_size': 1, 'data': ([{ 'text': suggestion.text, 'notes': suggestion.notes, 'state': suggestion.state.name, 'id': suggestion.id, 'author': { 'name': suggestion.author.last_known_username, 'alias': suggestion.author.id } }] if suggestion is not None else []) }))
def _init_settings(): global channel_settings_session channel_settings_session = main.Session() channel_settings_session.flush = lambda *a, **kw: print( 'CS: Flushing a readonly session.') print('Load channel settings.') with main.session_scope() as write_session: print('Loading existing channel settings...') for i in ChannelSettings.load_all(write_session): i.fill_defaults(forced=False) i.update() if i.channel_alias == -1: # global settings. channel_settings[SettingScope.GLOBAL.name] = i else: channel_settings[i.channel.last_known_username] = i print('OK') print('Creating missing channel settings...') for j in main.bot.channels_connected + [SettingScope.GLOBAL.name]: if j in channel_settings: continue cs = ChannelSettings() if j == SettingScope.GLOBAL.name: cs.channel_alias = -1 write_session.add(cs) continue channels = main.User.get_by_name(j.lower(), write_session) if len(channels) != 1: continue cs.channel = channels[0] write_session.add(cs) channel_settings[channels[0].last_known_username] = cs print('OK') print('Commit.') print(f'Done. Loaded {len(channel_settings)} channel settings entries.')
def _mailbox_draw(self, argv, msg): if msg.channel not in self.mailbox_games: return f'@{msg.user}, Cannot draw a winner from mailbox minigame, there is none running. FeelsBadMan' settings = self.mailbox_games[msg.channel] if settings.get('timeout_reason'): return ( f'@{msg.user}, There is no minigame running, however there are automatic timeouts set up. ' f'Use "{argv[0]} cancel" to stop timing out.') action_argv = ' '.join(argv[2:]) show_closed = False if 'end_time' not in settings: settings['end_time'] = time.time() show_closed = True match = GUESS_PATTERN.match(action_argv) if not match: return ( f'@{msg.user}, Bad winning value, it should be formatted like "30 32 29" ' f'{"The game has been automatically closed and is waiting for valid scores." if show_closed else ""}' ) good_value = [int(i.rstrip(', -')) for i in match.groups()] msgs = plugin_chat_cache.find_messages( msg.channel, min_timestamp=settings['start_time'], max_timestamp=settings['end_time']) possible_guesses = list( filter(lambda m: bool(GUESS_PATTERN.match(m.text)), msgs)) print('possible', possible_guesses) good_guesses = self._find_good_guesses(possible_guesses, good_value, settings) print('good', good_guesses) best = self._best_guess(settings, good_guesses) print('best', best) print(settings) failed_save = False # noinspection PyBroadException try: with main.session_scope() as session: game_obj = self.MailboxGame( main.User.get_by_twitch_id(int(msg.flags['room-id']), session), settings, best.copy(), good_value, possible_guesses) session.add(game_obj) except Exception: failed_save = True traceback.print_exc() del self.mailbox_games[msg.channel] db_notif = ( f'{"Failed to save to database. " if failed_save else ""}ID: {game_obj.id if not failed_save else "n/a"}' ) if len(best) == 0: return ( f'{"(automatically closed the game)" if show_closed else ""}' f'No one guessed {"even remotely " if settings["find_best"] else ""}right. {db_notif}' ) else: message = ( f'{"(automatically closed the game)" if show_closed else ""}' f'Best guesses are {self._nice_best_guesses(best)}. {db_notif}' ) if len(message) > 500: msgs = [] for i in range(1, math.ceil(len(message) / 500) + 1): msgs.append(message[(i - 1) * 500:i * 500]) return msgs return message
def reload_blacklist(self): global blacklists with self.expire_lock: # don't expire black lists while reloading with main.session_scope() as dank_circle: blacklists = BlacklistEntry.load_all(dank_circle)
def command_manage_blacklists(self, msg: twitchirc.ChannelMessage): argv = main.delete_spammer_chrs(msg.text).rstrip(' ').split(' ', 1) if len(argv) == 1: return f'@{msg.user}, {plugin_help.all_help[plugin_help.SECTION_COMMANDS]["plonk"]}' text = argv[1] try: kw = arg_parser.parse_args(text, { 'scope': str, 'user': str, 'command': str, 'expires': datetime.timedelta }, defaults={ 'scope': None, 'user': None, 'command': None, 'expires': None }) except arg_parser.ParserError as e: return e.message if kw['command'] is None: return f'@{msg.user}, No `command:...` provided.' if kw['user'] is None: return f'@{msg.user}, No `user:...` provided.' kw['user'] = kw['user'].lower() if kw['user'] == 'everyone': kw['user'] = True kw['scope'] = kw['scope'].lower().strip('#') if kw['scope'] != 'global': sc = [] for ch in kw['scope'].split(','): ch = ch.lstrip('#').lower() if ch not in main.bot.channels_connected: return f'@{msg.user}, Invalid `scope`: {kw["scope"]!r}, no such channel.' sc.append(ch) kw['scope'] = sc if kw['command'].lower() == 'all': kw['command'] = True else: cmd = None for i in main.bot.commands: i: main.Command if i.chat_command.lower( ) == kw['command'] or kw['command'] in i.aliases: cmd = i.chat_command if cmd is None: return f'@{msg.user}, Invalid `command`: {kw["command"]!r}. No such command exists.' else: kw['command'] = cmd del cmd if kw['expires']: kw['expires'] = datetime.datetime.now() + kw['expires'] with main.session_scope() as session: if kw['scope'] == 'global': targets = main.User.get_by_name( kw['user'], session) if kw['user'] is not True else None if targets is None or len(targets) == 1: obj = BlacklistEntry( target=targets[0] if targets is not None else targets, command=kw['command'] if kw['command'] is not True else None, channel=None, expires_on=kw['expires'], is_active=True) blacklists.append(obj) session.add(obj) elif len(targets) == 0: return f'@{msg.user} Failed to find user: {kw["user"]}' else: return f'@{msg.user} Found multiple users possible with name {kw["user"]}' else: for ch in kw['scope']: targets = main.User.get_by_name( kw['user'], session) if kw['user'] is not True else None channels = main.User.get_by_name(ch, session) if len(channels) == 1: if targets is None or len(targets) == 1: obj = BlacklistEntry( target=targets[0] if targets is not None else targets, command=kw['command'] if kw['command'] is not True else None, channel=channels[0], expires_on=kw['expires'], is_active=True) blacklists.append(obj) session.add(obj) elif len(targets) == 0: return f'@{msg.user} Failed to find user: {kw["user"]}' else: return f'@{msg.user} Found multiple users possible with name {kw["user"]}' elif len(channels) == 0: return f'@{msg.user} Failed to find channel: {ch}' elif len(channels) > 1: return f'@{msg.user} Found multiple channels possible with name {ch}' return f'@{msg.user}, Added blacklist for command {kw["command"]} with scope {kw["scope"]} for {kw["user"]}'
def _init(): log('debug', 'Load ban phrases.') with main.session_scope() as s: for i in BanPhrase.load_all(s): ban_phrases.append(i) log('debug', f'Done. Loaded {len(ban_phrases)} ban phrases.')