def on_banphrase_update(self, data, conn): try: banphrase_id = int(data['id']) except (KeyError, ValueError): log.warn('No banphrase ID found in on_banphrase_update') return False updated_banphrase = find(lambda banphrase: banphrase.id == banphrase_id, self.banphrases) if updated_banphrase: with DBManager.create_session_scope(expire_on_commit=False) as db_session: db_session.add(updated_banphrase) db_session.refresh(updated_banphrase) db_session.expunge(updated_banphrase) else: with DBManager.create_session_scope(expire_on_commit=False) as db_session: updated_banphrase = db_session.query(Banphrase).filter_by(id=banphrase_id).one_or_none() db_session.expunge_all() if updated_banphrase is not None: self.db_session.add(updated_banphrase.data) if updated_banphrase: if updated_banphrase not in self.banphrases: self.banphrases.append(updated_banphrase) if updated_banphrase.enabled is True and updated_banphrase not in self.enabled_banphrases: self.enabled_banphrases.append(updated_banphrase) for banphrase in self.enabled_banphrases: if banphrase.enabled is False: self.enabled_banphrases.remove(banphrase)
def load_config(self, config): self.config = config pajbot.models.user.Config.se_sync_token = config['main'].get('se_sync_token', None) pajbot.models.user.Config.se_channel = config['main'].get('se_channel', None) self.domain = config['web'].get('domain', 'localhost') self.nickname = config['main'].get('nickname', 'pajbot') self.password = config['main'].get('password', 'abcdef') self.timezone = config['main'].get('timezone', 'UTC') os.environ['TZ'] = self.timezone if config['main'].getboolean('verified', False): TMI.promote_to_verified() self.trusted_mods = config.getboolean('main', 'trusted_mods') self.phrases = { 'welcome': ['{nickname} {version} running!'], 'quit': ['{nickname} {version} shutting down...'], } if 'phrases' in config: phrases = config['phrases'] if 'welcome' in phrases: self.phrases['welcome'] = phrases['welcome'].splitlines() if 'quit' in phrases: self.phrases['quit'] = phrases['quit'].splitlines() TimeManager.init_timezone(self.timezone) if 'streamer' in config['main']: self.streamer = config['main']['streamer'] self.channel = '#' + self.streamer elif 'target' in config['main']: self.channel = config['main']['target'] self.streamer = self.channel[1:] self.silent = False self.dev = False if 'flags' in config: self.silent = True if 'silent' in config['flags'] and config['flags']['silent'] == '1' else self.silent self.dev = True if 'dev' in config['flags'] and config['flags']['dev'] == '1' else self.dev DBManager.init(self.config['main']['db']) redis_options = {} if 'redis' in config: redis_options = config._sections['redis'] RedisManager.init(**redis_options)
def point_pos(bot, source, message, event, args): # XXX: This should be a module user = None # This phrase should be a module setting point_pos = '{username_w_verb} rank {point_pos} point-hoarder in this channel with {points} points.' if message: tmp_username = message.split(' ')[0].strip().lower() user = bot.users.find(tmp_username) if not user: user = source phrase_data = { 'points': user.points } if user == source: phrase_data['username_w_verb'] = 'You are' else: phrase_data['username_w_verb'] = '{0} is'.format(user.username_raw) if user.points > 0: with DBManager.create_session_scope() as db_session: query_data = db_session.query(func.count(User.id)).filter(User.points > user.points).one() phrase_data['point_pos'] = int(query_data[0]) + 1 bot.whisper(source.username, point_pos.format(**phrase_data))
def nl_pos(bot, source, message, event, args): # XXX: This should be a module # These phrases should be module settings nl_0 = '{username} has not typed any messages in this channel BibleThump' nl_pos = '{username} is rank {nl_pos} line-farmer in this channel!' if message: tmp_username = message.split(' ')[0].strip().lower() user = bot.users.find(tmp_username) if user: username = user.username_raw num_lines = user.num_lines else: username = tmp_username num_lines = 0 else: username = source.username_raw num_lines = source.num_lines phrase_data = { 'username': username, 'num_lines': num_lines } if num_lines <= 0: bot.say(nl_0.format(**phrase_data)) else: with DBManager.create_session_scope() as db_session: query_data = db_session.query(func.count(User.id)).filter(User.num_lines > num_lines).one() phrase_data['nl_pos'] = int(query_data[0]) + 1 bot.say(nl_pos.format(**phrase_data))
def pleblist_history_stream(stream_id): with DBManager.create_session_scope() as session: stream = session.query(Stream).filter_by(id=stream_id).one_or_none() if stream is None: return render_template('pleblist_history_404.html'), 404 previous_stream = session.query(Stream).filter_by(id=stream_id - 1).one_or_none() next_stream = session.query(Stream).filter_by(id=stream_id + 1).one_or_none() q = session.query(PleblistSong, User).outerjoin(User, PleblistSong.user_id == User.id).filter(PleblistSong.stream_id == stream.id).order_by(PleblistSong.id.asc(), PleblistSong.id.asc()) songs = [] for song, user in q: song.user = user songs.append(song) total_length_left = sum([song.skip_after or song.song_info.duration if song.date_played is None and song.song_info is not None else 0 for song in songs]) first_unplayed_song = find(lambda song: song.date_played is None, songs) stream_chunks = session.query(StreamChunk).filter(StreamChunk.stream_id == stream.id).all() return render_template('pleblist_history.html', stream=stream, previous_stream=previous_stream, next_stream=next_stream, songs=songs, total_length_left=total_length_left, first_unplayed_song=first_unplayed_song, stream_chunks=stream_chunks)
def __init__(self, bot): self.bot = bot self.current_stream_chunk = None # should this even exist? self.num_offlines = 0 self.first_offline = None self.num_viewers = 0 self.bot.execute_every(self.STATUS_CHECK_INTERVAL, self.bot.action_queue.add, (self.refresh_stream_status_stage1, )) self.bot.execute_every(self.VIDEO_URL_CHECK_INTERVAL, self.bot.action_queue.add, (self.refresh_video_url_stage1, )) """ This will load the latest stream so we can post an accurate "time since last online" figure. """ with DBManager.create_session_scope(expire_on_commit=False) as db_session: self.current_stream = db_session.query(Stream).filter_by(ended=False).order_by(Stream.stream_start.desc()).first() self.last_stream = db_session.query(Stream).filter_by(ended=True).order_by(Stream.stream_end.desc()).first() if self.current_stream: self.current_stream_chunk = db_session.query(StreamChunk).filter_by(stream_id=self.current_stream.id).order_by(StreamChunk.chunk_start.desc()).first() log.info('Set current stream chunk here to {0}'.format(self.current_stream_chunk)) db_session.expunge_all()
def enable(self, bot): self.bot = bot pajbot.managers.handler.HandlerManager.add_handler('on_message', self.on_message, priority=100) pajbot.managers.handler.HandlerManager.add_handler('on_commit', self.on_commit) if bot: self.run_later = bot.execute_delayed if 'safebrowsingapi' in bot.config['main']: # XXX: This should be loaded as a setting instead. # There needs to be a setting for settings to have them as "passwords" # so they're not displayed openly self.safeBrowsingAPI = SafeBrowsingAPI(bot.config['main']['safebrowsingapi'], bot.nickname, bot.version) else: self.safeBrowsingAPI = None if self.db_session is not None: self.db_session.commit() self.db_session.close() self.db_session = None self.db_session = DBManager.create_session() self.blacklisted_links = [] for link in self.db_session.query(BlacklistedLink): self.blacklisted_links.append(link) self.whitelisted_links = [] for link in self.db_session.query(WhitelistedLink): self.whitelisted_links.append(link)
def commands(**options): from pajbot.models.module import ModuleManager bot_commands = pajbot.managers.command.CommandManager( socket_manager=None, module_manager=ModuleManager(None).load(), bot=None).load(enabled=None) bot_commands_list = bot_commands.parse_for_web() custom_commands = [] point_commands = [] moderator_commands = [] for command in bot_commands_list: if command.id is None: continue if command.level > 100 or command.mod_only: moderator_commands.append(command) elif command.cost > 0: point_commands.append(command) else: custom_commands.append(command) with DBManager.create_session_scope() as db_session: commands_data = db_session.query(CommandData).options(joinedload(CommandData.user), joinedload(CommandData.user2)).all() return render_template( 'admin/commands.html', commands_data=commands_data, custom_commands=sorted(custom_commands, key=lambda f: f.command), point_commands=sorted(point_commands, key=lambda a: (a.cost, a.command)), moderator_commands=sorted(moderator_commands, key=lambda c: (c.level if c.mod_only is False else 500, c.command)), created=session.pop('command_created_id', None), edited=session.pop('command_edited_id', None))
def create_stream(self, status): log.info('Attempting to create a stream!') with DBManager.create_session_scope(expire_on_commit=False) as db_session: stream_chunk = db_session.query(StreamChunk).filter_by(broadcast_id=status['broadcast_id']).one_or_none() new_stream = False if stream_chunk is not None: stream = stream_chunk.stream else: log.info('checking if there is an active stream already') stream = db_session.query(Stream).filter_by(ended=False).order_by(Stream.stream_start.desc()).first() new_stream = stream is None if new_stream: log.info('No active stream, create new!') stream = Stream(status['created_at'], title=status['title']) db_session.add(stream) db_session.commit() log.info('Successfully added stream!') stream_chunk = StreamChunk(stream, status['broadcast_id'], status['created_at']) db_session.add(stream_chunk) db_session.commit() stream.stream_chunks.append(stream_chunk) log.info('Created stream chunk') self.current_stream = stream self.current_stream_chunk = stream_chunk db_session.expunge_all() if new_stream: HandlerManager.trigger('on_stream_start', stop_on_false=False) log.info('Successfully created a stream')
def decks_warrior(): session = DBManager.create_session() decks = session.query(Deck).filter_by(deck_class='warrior').order_by(Deck.last_used.desc(), Deck.first_used.desc()).all() session.close() return render_template('decks/by_class.html', decks=decks, deck_class='Warrior')
def post(self): args = self.post_parser.parse_args() try: pajbot.web.utils.pleblist_login(args['password'], app.bot_config) except pajbot.exc.InvalidLogin as e: return {'error': str(e)}, 401 with DBManager.create_session_scope() as session: youtube_id = args['youtube_id'] current_stream = session.query(Stream).filter_by(ended=False).order_by(Stream.stream_start).first() if current_stream is None: return { 'error': 'Stream offline' }, 400 skip_after = args['skip_after'] log.info('Request song youtube ID: {}'.format(youtube_id)) song_requested = PleblistSong(current_stream.id, youtube_id, skip_after=skip_after) session.add(song_requested) song_info = session.query(PleblistSongInfo).filter_by(pleblist_song_youtube_id=youtube_id).first() if song_info is None and song_requested.song_info is None: PleblistManager.init(app.bot_config['youtube']['developer_key']) song_info = PleblistManager.create_pleblist_song_info(song_requested.youtube_id) if song_info is not False: session.add(song_info) session.commit() return { 'success': 'got em!' }, 200
def post(self): args = self.post_parser.parse_args() try: pajbot.web.utils.pleblist_login(args['password'], app.bot_config) except pajbot.exc.InvalidLogin as e: return {'error': str(e)}, 401 with DBManager.create_session_scope() as session: try: current_song = session.query(PleblistSong).filter(PleblistSong.id == args['song_id']).order_by(PleblistSong.date_added.asc()).first() except ValueError: return { 'error': 'Invalid data song_id' }, 400 if current_song is None: return { 'error': 'No song active in the pleblist' }, 404 current_song.date_played = datetime.datetime.now() session.commit() # TODO: Add more data. # Was this song forcefully skipped? Or did it end naturally. return { 'success': 'got em!' }, 200
def post(self, row_id, **options): args = self.post_parser.parse_args() try: new_state = int(args['new_state']) except (ValueError, KeyError): return {'error': 'Invalid `new_state` parameter.'}, 400 with DBManager.create_session_scope() as db_session: row = db_session.query(Module).filter_by(id=row_id).one_or_none() if not row: return { 'error': 'Module with this ID not found' }, 404 if validate_module(row_id) is False: return {'error': 'cannot modify module'}, 400 row.enabled = True if new_state == 1 else False db_session.commit() payload = { 'id': row.id, 'new_state': row.enabled, } AdminLogManager.post('Module toggled', options['user'], 'Enabled' if row.enabled else 'Disabled', row.id) SocketClientManager.send('module.update', payload) return {'success': 'successful toggle', 'new_state': new_state}
def points(): with DBManager.create_session_scope() as db_session: custom_web_content = db_session.query(WebContent).filter_by(page='points').first() custom_content = '' if custom_web_content and custom_web_content.content: try: custom_content = Markup(markdown.markdown(custom_web_content.content)) except: log.exception('Unhandled exception in def index') rank = 1 index = 1 last_user_points = -13333337 rankings = [] for user in db_session.query(User).order_by(User.points.desc()).limit(30): if user.points != last_user_points: rank = index rankings.append((rank, user)) index += 1 last_user_points = user.points return render_template('points.html', top_30_users=rankings, custom_content=custom_content)
def modules_edit(module_id, **options): module_manager = ModuleManager(None).load(do_reload=False) current_module = find(lambda m: m.ID == module_id, module_manager.all_modules) if current_module is None: return render_template('admin/module_404.html'), 404 sub_modules = [] for module in module_manager.all_modules: module.db_module = None with DBManager.create_session_scope() as db_session: for db_module in db_session.query(Module): module = find(lambda m: m.ID == db_module.id, module_manager.all_modules) if module: module.db_module = db_module if module.PARENT_MODULE == current_module.__class__: sub_modules.append(module) if current_module.db_module is None: return render_template('admin/module_404.html'), 404 if request.method == 'POST': form_values = {key: value for key, value in request.form.items()} res = current_module.parse_settings(**form_values) if res is False: return render_template('admin/module_404.html'), 404 current_module.db_module.settings = json.dumps(res) db_session.commit() settings = None try: settings = json.loads(current_module.db_module.settings) except (TypeError, ValueError): pass current_module.load(settings=settings) payload = { 'id': current_module.db_module.id, } SocketClientManager.send('module.update', payload) AdminLogManager.post('Module edited', options['user'], current_module.NAME) return render_template('admin/configure_module.html', module=current_module, sub_modules=sub_modules) else: settings = None try: settings = json.loads(current_module.db_module.settings) except (TypeError, ValueError): pass current_module.load(settings=settings) return render_template('admin/configure_module.html', module=current_module, sub_modules=sub_modules)
def get_current_song(stream_id): with DBManager.create_session_scope() as session: cur_song = session.query(PleblistSong).filter(PleblistSong.stream_id == stream_id, PleblistSong.date_played.is_(None)).order_by(PleblistSong.date_added.asc(), PleblistSong.id.asc()).first() if cur_song is None: return None session.expunge(cur_song) return cur_song
def stats(): bot_commands_list = pajbot.web.utils.get_cached_commands() top_5_commands = sorted(bot_commands_list, key=lambda c: c['data']['num_uses'] if c['data'] is not None else -1, reverse=True)[:5] redis = RedisManager.get() # TODO: Make this hideable through some magic setting (NOT config.ini @_@) with DBManager.create_session_scope() as db_session: top_5_line_farmers = [] for redis_user in redis.zrevrangebyscore( '{streamer}:users:num_lines'.format(streamer=StreamHelper.get_streamer()), '+inf', '-inf', start=0, num=5, withscores=True, score_cast_func=int): user = UserManager.get_static(redis_user[0], db_session=db_session) user.save_to_redis = False user.num_lines = redis_user[1] top_5_line_farmers.append(user) return render_template('stats.html', top_5_commands=top_5_commands, top_5_line_farmers=top_5_line_farmers)
def __init__(self, bot): self.bot = bot self.banphrases = [] self.enabled_banphrases = [] self.db_session = DBManager.create_session(expire_on_commit=False) if self.bot: self.bot.socket_manager.add_handler('banphrase.update', self.on_banphrase_update) self.bot.socket_manager.add_handler('banphrase.remove', self.on_banphrase_remove)
def points_rank(self): if self.shared_db_session: query_data = self.shared_db_session.query(sqlalchemy.func.count(User.id)).filter(User.points > self.points).one() else: with DBManager.create_session_scope(expire_on_commit=False) as db_session: query_data = db_session.query(sqlalchemy.func.count(User.id)).filter(User.points > self.points).one() rank = int(query_data[0]) + 1 return rank
def sql_save(self, save_to_db=True): if not self.model_loaded: return if save_to_db and not self.shared_db_session: with DBManager.create_session_scope(expire_on_commit=False) as db_session: db_session.add(self.user_model) UserSQLCache.save(self.user_model)
def remove_deck(self, deck): self.data.remove(deck) with DBManager.create_session_scope_nc(expire_on_commit=False) as db_session: db_session.delete(deck) db_session.commit() if deck == self.current_deck: log.info('refreshing current deck') self.refresh_current_deck()
def banphrases_edit(banphrase_id, **options): with DBManager.create_session_scope() as db_session: banphrase = db_session.query(Banphrase).filter_by(id=banphrase_id).one_or_none() if banphrase is None: return render_template('admin/banphrase_404.html'), 404 return render_template('admin/create_banphrase.html', banphrase=banphrase)
def remove_command(self, command): self.remove_command_aliases(command) with DBManager.create_session_scope() as db_session: self.db_session.expunge(command.data) db_session.delete(command.data) db_session.delete(command) self.rebuild()
def remove_highlight(self, id): """ Returns True if a highlight was removed, otherwise return False """ with DBManager.create_session_scope() as db_session: num_rows = db_session.query(StreamChunkHighlight).filter(StreamChunkHighlight.id == id).delete() return (num_rows == 1)
def get(self, stream_id): with DBManager.create_session_scope() as session: songs = session.query(PleblistSong).filter_by(stream_id=stream_id) return pajbot.web.utils.jsonify_list( 'songs', songs, base_url=url_for(self.endpoint, stream_id=stream_id, _external=True), )
def decks(): session = DBManager.create_session() top_decks = [] for deck in session.query(Deck).order_by(Deck.last_used.desc(), Deck.first_used.desc())[:25]: top_decks.append(deck) session.close() return render_template('decks/all.html', top_decks=top_decks, deck_class=None)
def shared_close_predict(self, bot, source, type): with DBManager.create_session_scope() as db_session: # Check if there is a non-ended, but closed prediction run we can end current_prediction_run = db_session.query(PredictionRun).filter_by(ended=None, open=True, type=type).one_or_none() if current_prediction_run is None: bot.say('{}, There is no open prediction runs we can close right now.'.format(source.username_raw)) return True current_prediction_run.open = False bot.say('{}, Predictions are no longer accepted for prediction run {}'.format(source.username_raw, current_prediction_run.id))
def moderators(**options): with DBManager.create_session_scope() as db_session: moderator_users = db_session.query(User).filter(User.level > 100).order_by(User.level.desc()).all() userlists = collections.OrderedDict() userlists['Admins'] = list(filter(lambda user: user.level >= 2000, moderator_users)) userlists['Super Moderators/Broadcaster'] = list(filter(lambda user: user.level >= 1000 and user.level < 2000, moderator_users)) userlists['Moderators'] = list(filter(lambda user: user.level >= 500 and user.level < 1000, moderator_users)) userlists['Notables/Helpers'] = list(filter(lambda user: user.level >= 101 and user.level < 500, moderator_users)) return render_template('admin/moderators.html', userlists=userlists)
def reload(self): self.data = [] with DBManager.create_session_scope_nc(expire_on_commit=False) as db_session: for deck in db_session.query(Deck).order_by(Deck.last_used.desc()): if self.current_deck is None: self.current_deck = deck self.data.append(deck) db_session.expunge(deck) db_session.expunge_all()
def load_config(self, config): self.config = config self.domain = config['web'].get('domain', 'localhost') self.nickname = config['main'].get('nickname', 'pajbot') self.password = config['main'].get('password', 'abcdef') self.timezone = config['main'].get('timezone', 'UTC') self.trusted_mods = config.getboolean('main', 'trusted_mods') TimeManager.init_timezone(self.timezone) if 'streamer' in config['main']: self.streamer = config['main']['streamer'] self.channel = '#' + self.streamer elif 'target' in config['main']: self.channel = config['main']['target'] self.streamer = self.channel[1:] self.wolfram = None if 'wolfram' in config['main']: try: import wolframalpha self.wolfram = wolframalpha.Client(config['main']['wolfram']) except ImportError: pass self.silent = False self.dev = False if 'flags' in config: self.silent = True if 'silent' in config['flags'] and config['flags']['silent'] == '1' else self.silent self.dev = True if 'dev' in config['flags'] and config['flags']['dev'] == '1' else self.dev DBManager.init(self.config['main']['db']) redis_options = {} if 'redis' in config: redis_options = config._sections['redis'] RedisManager.init(**redis_options)
def enable(self, bot): if not bot: return HandlerManager.add_handler("on_message", self.on_message, priority=100) HandlerManager.add_handler("on_commit", self.on_commit) if self.db_session is not None: self.db_session.commit() self.db_session.close() self.db_session = None self.db_session = DBManager.create_session() self.blacklisted_links = [] for link in self.db_session.query(BlacklistedLink): self.blacklisted_links.append(link) self.whitelisted_links = [] for link in self.db_session.query(WhitelistedLink): self.whitelisted_links.append(link)
def __init__(self, reactor, bot, target, message_limit, time_interval, num_of_conns=30): self.db_session = DBManager.create_session() self.reactor = reactor self.bot = bot self.message_limit = message_limit self.time_interval = time_interval self.num_of_conns = num_of_conns self.whisper_thread = None self.connlist = [] self.whispers = Queue() self.maintenance_lock = False
def on_web_playsound(self, data): # on playsound test triggered by the Web UI # this works even if the module is not enabled. playsound_name = data["name"] with DBManager.create_session_scope() as session: playsound = session.query(Playsound).filter(Playsound.name == playsound_name).one_or_none() if playsound is None: log.warning("Web UI tried to play invalid playsound. Ignoring.") return payload = { "link": playsound.link, "volume": int(round(playsound.volume * self.settings["global_volume"] / 100)), } log.debug("Playsound module is emitting payload: {}".format(json.dumps(payload))) self.bot.websocket_manager.emit("play_sound", payload)
def get(self, **options): args = self.get_parser.parse_args() offset = max(0, args["offset"]) limit = max(1, args["limit"]) if args["direction"] == "desc": direction = TwitterUser.id.desc() else: direction = TwitterUser.id.asc() with DBManager.create_session_scope() as db_session: return ( { "_total": db_session.query(TwitterUser).count(), "follows": [ t.jsonify() for t in db_session.query(TwitterUser).order_by(direction)[offset : offset + limit] ], }, 200, )
def remove_playsound_command(bot, source, message, **rest): """Method for removing playsounds. Usage: !edit playsound PLAYSOUNDNAME """ playsound_name = message.split(" ")[0].lower() # check for empty string if not playsound_name: bot.whisper(source, "Invalid usage. Correct syntax: !remove playsound <name>") return with DBManager.create_session_scope() as session: playsound = session.query(Playsound).filter(Playsound.name == playsound_name).one_or_none() if playsound is None: bot.whisper(source, "No playsound with that name exists.") return session.delete(playsound) bot.whisper(source, "Successfully deleted your playsound.")
def get(self): with DBManager.create_session_scope() as session: # songs = session.query(PleblistSong, func.count(PleblistSong.song_info).label('total')).group_by(PleblistSong.youtube_id).order_by('total DESC') songs = (session.query( PleblistSong, func.count(PleblistSong.youtube_id).label("total")).group_by( PleblistSong.youtube_id).order_by("total DESC")) log.info(songs) log.info(songs.all()) return pajbot.web.utils.jsonify_list( "songs", songs, default_limit=50, max_limit=500, base_url=url_for(self.endpoint, _external=True), jsonify_method=jsonify_query, )
def edit_playsound_command(self, bot, source, message, **rest): """Method for editing playsounds. Usage: !edit playsound PLAYSOUNDNAME [LINK] [options] Multiple options available: --volume VOLUME --cooldown COOLDOWN --enabled/--disabled """ options, name, link = self.parse_playsound_arguments(message) if options is False or name is False or link is False: bot.whisper( source, "Invalid usage. Correct syntax: !edit playsound <name> [link] " + "[--volume 0-100] [--cooldown 60/none] [--enabled/--disabled]", ) return with DBManager.create_session_scope() as session: playsound = session.query(Playsound).filter(Playsound.name == name).one_or_none() if playsound is None: bot.whisper( source, "No playsound with that name exists. You can create playsounds with " "!add playsound <name> <link> [options].", ) return if not self.update_link(bot, source, playsound, link): return if not self.update_volume(bot, source, playsound, options): return if not self.update_cooldown(bot, source, playsound, options): return if not self.update_enabled(bot, source, playsound, options): return session.add(playsound) bot.whisper(source, "Successfully edited your playsound.")
def get(self, song_id): with DBManager.create_session_scope() as session: current_stream = session.query(Stream).filter_by( ended=False).order_by(Stream.stream_start).first() if current_stream is None: return {'error': 'Stream offline'}, 400 songs = session.query(PleblistSong).filter( and_(PleblistSong.stream_id == current_stream.id, PleblistSong.date_played.is_(None), PleblistSong.id > song_id)) return pajbot.web.utils.jsonify_list( 'songs', songs, base_url=url_for(self.endpoint, song_id=song_id, _external=True), )
def cancel_duel(self, bot, source, **rest): """ Cancel any duel requests you've sent. How to use: !cancelduel """ if source.id not in self.duel_requests: bot.whisper(source, "You have not sent any duel requests") return with DBManager.create_session_scope() as db_session: challenged = User.find_by_id(db_session, self.duel_requests[source.id]) bot.whisper(source, f"You have cancelled the duel vs {challenged}") del self.duel_targets[challenged.id] del self.duel_request_price[source.id] del self.duel_begin_time[source.id] del self.duel_requests[source.id]
def go_offline(self): with DBManager.create_session_scope( expire_on_commit=False) as db_session: self.current_stream.ended = True self.current_stream.stream_end = self.first_offline self.current_stream_chunk.chunk_end = self.first_offline db_session.add(self.current_stream) db_session.add(self.current_stream_chunk) db_session.commit() db_session.expunge_all() self.last_stream = self.current_stream self.current_stream = None self.current_stream_chunk = None HandlerManager.trigger("on_stream_stop", stop_on_false=False)
def user_profile_connections(): with DBManager.create_session_scope() as db_session: if "user" not in session: return redirect(f"/login?n=/connections/") user = User.find_by_id(db_session, session["user"]["id"]) if user is None: return render_template("no_user.html"), 404 user_connection = UserConnections._from_twitch_id( db_session, user.id) discord = None steam = None if ("discord_id" in session and "discord_username" in session and session["discord_id"] is not None and session["discord_username"] is not None): discord = { "id": session["discord_id"], "username": session["discord_username"] } if "steam_id" in session and session["steam_id"] is not None: steam = {"id": session["steam_id"]} data = { "steam": steam, "discord": discord, "twitch": session["user"], "offcd": user.offcd } user_connection = UserConnections._from_twitch_id( db_session, user.id) if user_connection: return render_template( "connections_unlink.html", user=user, data=user_connection.jsonify(), twitch_user=session["user"], returnUrl=f"/connections", ) return render_template("connections.html", user=user, data=data, returnUrl=f"/connections", pair_failed=False)
def bg_pleblist_add_song(self, stream_id, youtube_id, force, **options): bot = options['bot'] source = options['source'] with DBManager.create_session_scope() as db_session: song_info = PleblistManager.get_song_info(youtube_id, db_session) if song_info is None or force: try: # XXX: Should this be a setting in the module? idk PleblistManager.init(bot.config['youtube']['developer_key']) except: log.error('No youtube key set up.') bot.whisper(source.username, 'No youtube key set up') return False song_info = PleblistManager.create_pleblist_song_info(youtube_id) if song_info is False: bot.whisper(source.username, 'Invalid song given (or the YouTube API is down)') return False db_session.merge(song_info) db_session.commit() # See if the user has already submitted X songs num_unplayed_songs_requested = int(db_session.query(func.count(PleblistSong.id)).filter_by(stream_id=stream_id, user_id=source.id, date_played=None).one()[0]) if num_unplayed_songs_requested >= self.settings['max_songs_per_user'] and not force: bot.whisper(source.username, 'You can only request {} songs at the same time!'.format(num_unplayed_songs_requested)) return False # Add the song request song_request = PleblistSong(bot.stream_manager.current_stream.id, youtube_id, user_id=source.id) # See if the song is too long # If it is, make it autoskip after that time if song_info.duration > self.settings['max_song_length']: song_request.skip_after = self.settings['max_song_length'] db_session.add(song_request) bot.say('{} just requested the song "{}" to be played KKona'.format(source.username_raw, song_info.title))
def command_close(self, bot, source, message, **rest): with DBManager.create_session_scope() as db_session: current_game = db_session.query(BetGame).filter(BetGame.is_running).one_or_none() if not current_game: bot.say(f"{source}, no bet currently exists") return False if current_game.betting_open: if self.lock_schedule: try: self.lock_schedule.remove() except: pass self.lock_schedule = None count_down = 15 if message and message.isdigit(): count_down = 15 if int(message) < 0 else int(message) if count_down > 0: bot.me(f"Betting will be locked in {count_down} seconds! Place your bets people monkaS") self.lock_schedule = ScheduleManager.execute_delayed(count_down, self.lock_bets) elif message: split_message = message.split(" ") outcome = None if len(split_message) > 0: for item in split_message: outcome = "l" if "l" in item.lower() or "dire" in item.lower() else None if not outcome: outcome = "w" if "w" in item.lower() or "radi" in item.lower() else None if outcome: break if outcome: if outcome == "l": bot.execute_now(self.spread_points, BetGameOutcome.loss) else: bot.execute_now(self.spread_points, BetGameOutcome.win) else: bot.say(f"Are you pretending {source}?") return False self.spectating = False else: bot.say("WTFF")
def post(self, playsound_name, **options): # require JSON so the cooldown can be null post_parser = RequestParser() post_parser.add_argument("link", required=True) post_parser.add_argument("volume", type=int, required=True) post_parser.add_argument("cooldown", type=int, required=False) post_parser.add_argument("enabled", type=bool, required=False) args = post_parser.parse_args() link = args["link"] if not PlaysoundModule.validate_link(link): return "Empty or bad link, links must start with https:// and must not contain spaces", 400 volume = args["volume"] if not PlaysoundModule.validate_volume(volume): return "Bad volume argument", 400 # cooldown is allowed to be null/None cooldown = args.get("cooldown", None) if not PlaysoundModule.validate_cooldown(cooldown): return "Bad cooldown argument", 400 enabled = args["enabled"] if enabled is None: return "Bad enabled argument", 400 with DBManager.create_session_scope() as db_session: playsound = db_session.query(Playsound).filter( Playsound.name == playsound_name).one_or_none() if playsound is None: return "Playsound does not exist", 404 # TODO admin audit logs playsound.link = link playsound.volume = volume playsound.cooldown = cooldown playsound.enabled = enabled db_session.add(playsound) return "OK", 200
def points(): with DBManager.create_session_scope() as db_session: custom_web_content = db_session.query(WebContent).filter_by( page="points").first() custom_content = "" if custom_web_content and custom_web_content.content: try: custom_content = Markup( markdown.markdown(custom_web_content.content)) except: log.exception("Unhandled exception in def index") # rankings is a list of (User, int) tuples (user with their rank) # note on the efficiency of this query: takes approx. 0.3-0.4 milliseconds on a 5 million user DB # # pajbot=# EXPLAIN ANALYZE SELECT * FROM (SELECT *, rank() OVER (ORDER BY points DESC) AS rank FROM "user") AS subquery LIMIT 30; # QUERY PLAN # ---------------------------------------------------------------------------------------------------------------------------------------------------------- # Limit (cost=0.43..2.03 rows=30 width=49) (actual time=0.020..0.069 rows=30 loops=1) # -> WindowAgg (cost=0.43..181912.19 rows=4197554 width=49) (actual time=0.020..0.065 rows=30 loops=1) # -> Index Scan Backward using user_points_idx on "user" (cost=0.43..118948.88 rows=4197554 width=41) (actual time=0.012..0.037 rows=31 loops=1) # Planning Time: 0.080 ms # Execution Time: 0.089 ms # # (see also the extensive comment on migration revision ID 2, 0002_create_index_on_user_points.py) rankings = db_session.query(User, column("rank")).from_statement( text( 'SELECT * FROM (SELECT *, rank() OVER (ORDER BY points DESC) AS rank FROM "user") AS subquery LIMIT 30' )) chatters_refresh_enabled = ChattersRefreshModule.is_enabled() chatters_refresh_settings = ChattersRefreshModule.module_settings() chatters_refresh_interval = ChattersRefreshModule.UPDATE_INTERVAL return render_template( "points.html", top_30_users=rankings, custom_content=custom_content, chatters_refresh_enabled=chatters_refresh_enabled, chatters_refresh_settings=chatters_refresh_settings, chatters_refresh_interval=chatters_refresh_interval, )
def permaban_command(bot, source, message, **rest): if not message: return username = message.split(" ")[0] with DBManager.create_session_scope() as db_session: user = User.find_by_user_input(db_session, username) if not user: bot.whisper(source, "No user with that name found.") return False if user.banned: bot.whisper(source, "User is already permabanned.") return False user.banned = True log_msg = f"{user} has been permabanned" bot.whisper(source, log_msg) AdminLogManager.add_entry("Permaban added", source, log_msg)
def get(login): # add ?user_input=true to query user more fuzzily query_by_user_input = request.args.get("user_input") == "true" with DBManager.create_session_scope() as db_session: if query_by_user_input: user = User.find_by_user_input(db_session, login) else: user = User.find_by_login(db_session, login) if user is None: return {"error": "Not found"}, 404 # these are provided for legacy purposes - so we don't break the API interface. json = user.jsonify() json["username_raw"] = json["name"] json["username"] = json["login"] json["nl_rank"] = json["num_lines_rank"] json["minutes_in_chat_online"] = int(json["time_in_chat_online"] / 60) json["minutes_in_chat_offline"] = int(json["time_in_chat_offline"] / 60) return json
def highlights(): session = DBManager.create_session() dates_with_highlights = [] highlights = session.query(StreamChunkHighlight).options( joinedload('*')).filter(StreamChunkHighlight.created_at >= ( datetime.datetime.utcnow() - datetime.timedelta(days=60))).order_by( StreamChunkHighlight.created_at_with_offset.desc()).all() for highlight in highlights: dates_with_highlights.append( datetime.datetime(year=highlight.created_at.year, month=highlight.created_at.month, day=highlight.created_at.day)) try: return render_template( 'highlights.html', highlights=highlights[:10], dates_with_highlights=set(dates_with_highlights)) finally: session.close()
def post(self, row_id, **options): args = self.post_parser.parse_args() try: new_state = int(args["new_state"]) except (ValueError, KeyError): return {"error": "Invalid `new_state` parameter."}, 400 with DBManager.create_session_scope() as db_session: row = db_session.query(Timer).filter_by(id=row_id).one_or_none() if not row: return {"error": "Timer with this ID not found"}, 404 row.enabled = True if new_state == 1 else False db_session.commit() payload = {"id": row.id, "new_state": row.enabled} AdminLogManager.post("Timer toggled", options["user"], "Enabled" if row.enabled else "Disabled", row.name) SocketClientManager.send("timer.update", payload) return {"success": "successful toggle", "new_state": new_state}
def debug_user(bot, source, message, **options): if not message or len(message) <= 0: bot.whisper(source, "Usage: !debug user USERNAME") return False username = message.split(" ")[0] with DBManager.create_session_scope() as db_session: user = User.find_by_user_input(db_session, username) if user is None: bot.whisper(source, "No user with this username found.") return False # TODO the time_in_chat_ properties could be displayed in a more user-friendly way # current output format is time_in_chat_online=673800.0, time_in_chat_offline=7651200.0 data = user.jsonify() bot.whisper( source, ", ".join([f"{key}={value}" for (key, value) in data.items()]))
def get_duel_stats(self, **options): """ Whispers the users duel winratio to the user """ bot = options['bot'] source = options['source'] with DBManager.create_session_scope( expire_on_commit=False) as db_session: db_session.add(source.user_model) if source.duel_stats is None: bot.whisper(source.username, 'You have no recorded duels.') return True bot.whisper( source.username, 'duels: {ds.duels_total} winrate: {ds.winrate:.2f}% streak: {ds.current_streak} profit: {ds.profit}' .format(ds=source.duel_stats))
def create_command(self, alias_str, **options): aliases = alias_str.lower().replace('!', '').split('|') for alias in aliases: if alias in self.data: return self.data[alias], False, alias command = Command(command=alias_str, **options) command.data = CommandData(command.id, **options) self.add_db_command_aliases(command) with DBManager.create_session_scope(expire_on_commit=False) as db_session: db_session.add(command) db_session.add(command.data) db_session.commit() db_session.expunge(command) db_session.expunge(command.data) self.db_session.add(command.data) self.commit() self.rebuild() return command, True, ''
def put(self, playsound_name, **options): post_parser = RequestParser() post_parser.add_argument("link", required=True) args = post_parser.parse_args() try: link = args["link"] except (ValueError, KeyError): return {"error": "Invalid `link` parameter."}, 400 with DBManager.create_session_scope() as db_session: count = db_session.query(Playsound).filter(Playsound.name == playsound_name).count() if count >= 1: return "Playsound already exists", 400 # the rest of the parameters are initialized with defaults playsound = Playsound(name=playsound_name, link=link) db_session.add(playsound) return "OK", 200
def sql_load(self): if self.model_loaded: return self.model_loaded = True # log.debug('[UserSQL] Loading user model for {}'.format(self.username)) # from pajbot.utils import print_traceback # print_traceback() if self.shared_db_session: user = UserSQL.select_or_create(self.shared_db_session, self.username) else: with DBManager.create_session_scope( expire_on_commit=False) as db_session: user = UserSQL.select_or_create(db_session, self.username) db_session.expunge(user) self.user_model = user
def get(self, command_id, **options): with DBManager.create_session_scope() as db_session: command = db_session.query(Command).filter_by( id=command_id).one_or_none() if command is None: return {"error": "Invalid command ID"}, 404 if command.level > options["user"].level: return {"error": "Unauthorized"}, 403 log_msg = "The !{} command has been removed".format( command.command.split("|")[0]) AdminLogManager.add_entry("Command removed", options["user"], log_msg) db_session.delete(command.data) db_session.delete(command) if SocketClientManager.send("command.remove", {"command_id": command_id}) is True: return {"success": "good job"}, 200 else: return {"error": "could not push update"}, 500
def authorized(): try: resp = twitch.authorized_response() except OAuthException: log.exception("An exception was caught while authorizing") next_url = get_next_url(request, "state") return redirect(next_url) except: log.exception("Unhandled exception while authorizing") return render_template("login_error.html") if resp is None: if "error" in request.args and "error_description" in request.args: log.warning( "Access denied: reason={}, error={}".format( request.args["error"], request.args["error_description"] ) ) next_url = get_next_url(request, "state") return redirect(next_url) elif type(resp) is OAuthException: log.warning(resp.message) log.warning(resp.data) log.warning(resp.type) next_url = get_next_url(request, "state") return redirect(next_url) session["twitch_token"] = (resp["access_token"],) me = twitch.get("user") level = 100 with DBManager.create_session_scope() as db_session: db_user = db_session.query(User).filter_by(username=me.data["name"].lower()).one_or_none() if db_user: level = db_user.level session["user"] = {"username": me.data["name"], "username_raw": me.data["display_name"], "level": level} if me.data["name"].lower() == app.bot_config["main"]["nickname"].lower(): redis = RedisManager.get() redis.set("{}:token".format(app.bot_config["main"]["nickname"]), json.dumps(resp)) next_url = get_next_url(request, "state") return redirect(next_url)
def commands(**options) -> ResponseReturnValue: from pajbot.models.module import ModuleManager bot_commands = pajbot.managers.command.CommandManager( socket_manager=None, module_manager=ModuleManager(None).load(), bot=None).load(enabled=None) bot_commands_list = bot_commands.parse_for_web() custom_commands = [] point_commands = [] moderator_commands = [] for command in bot_commands_list: if command.id is None: continue if command.level > 100 or command.mod_only: moderator_commands.append(command) elif command.cost > 0: point_commands.append(command) else: custom_commands.append(command) with DBManager.create_session_scope() as db_session: commands_data = (db_session.query(CommandData).options( joinedload(CommandData.user), joinedload(CommandData.user2)).all()) return render_template( "admin/commands.html", commands_data=commands_data, custom_commands=sorted(custom_commands, key=lambda f: f.command), point_commands=sorted(point_commands, key=lambda a: (a.cost, a.command)), moderator_commands=sorted( moderator_commands, key=lambda c: (c.level if c.mod_only is False else 500, c.command)), created=session.pop("command_created_id", None), edited=session.pop("command_edited_id", None), )
def on_message(self, message): msg = json.loads(message) if msg["type"].lower() == "pong": self.sent_ping = False return elif msg["type"].lower() == "reconnect": self.reset() return elif msg["type"].lower() == "message": if msg["data"][ "topic"] == "channel-bits-events-v2." + self.bot.streamer_user_id: messageR = json.loads(msg["data"]["message"]) user_id_of_cheer = str(messageR["data"]["user_id"]) bits_cheered = str(messageR["data"]["bits_used"]) with DBManager.create_session_scope() as db_session: user = User.find_by_id(db_session, user_id_of_cheer) if user is not None: HandlerManager.trigger("on_cheer", True, user=user, bits_cheered=bits_cheered) return try: message_message = json.loads(msg["data"]["message"]) if message_message["type"] == "reward-redeemed": userDict = message_message["data"]["redemption"]["user"] HandlerManager.trigger( "on_redeem", redeemer=UserBasics(userDict["id"], userDict["login"], userDict["display_name"]), redeemed_id=message_message["data"]["redemption"] ["reward"]["id"], user_input=message_message["data"]["redemption"].get( "user_input", ""), ) except Exception as e: log.error(e) elif msg["type"].lower() == "response": if not msg["error"]: return log.warning(msg)
def check_retimeout(self): with DBManager.create_session_scope() as session: timeoutList = session.query(LongTimeout).all() timeNow = utils.now() for timeoutItem in timeoutList: timeoutEnd = timeoutItem.timeout_recent_end overallStart = timeoutItem.timeout_start overallEnd = timeoutItem.timeout_end if timeNow > overallEnd: self.bot.whisper( timeoutItem.timeout_author, "{}'s timeout of {} hours has ended.".format( timeoutItem.username, round((overallEnd - overallStart).seconds / 3600, 2)), ) session.delete(timeoutItem) continue if timeoutEnd < timeNow: timeoutDuration = 1209600 if (overallEnd - timeNow).days < 14: timeoutDuration = (overallEnd - timeNow).seconds timeoutHours = round(float(timeoutDuration / 3600), 2) timeoutItem.timeout_recent_end = ( timeNow + timedelta(seconds=timeoutDuration)) self.bot.whisper( timeoutItem.timeout_author, "Timing out {} for an additional {} hours".format( timeoutItem.username, timeoutHours), ) self.bot._timeout( timeoutItem.username, timeoutDuration, "Timed out {} for an additional {} hours, per {}'s !longtimeout" .format(timeoutItem.username, timeoutHours, timeoutItem.timeout_author), ) session.add(timeoutItem)
def load(self, do_reload=True): """ Load module classes """ from pajbot.modules import available_modules self.all_modules = [module(self.bot) for module in available_modules] with DBManager.create_session_scope() as db_session: # Make sure there's a row in the DB for each module that's available db_modules = db_session.query(Module).all() for module in self.all_modules: mod = find(lambda db_module, registered_module=module: db_module.id == registered_module.ID, db_modules) if mod is None: log.info("Creating row in DB for module {}".format(module.ID)) mod = Module(module.ID, enabled=module.ENABLED_DEFAULT) db_session.add(mod) if do_reload is True: self.reload() return self