Beispiel #1
0
 def _get_next_song(db_session):
     next_id = SongRequestQueueManager.get_next_song()
     if not next_id:
         return None
     song = db_session.query(SongrequestQueue).filter_by(
         id=next_id).one_or_none()
     if not song:
         SongRequestQueueManager.remove_song_id(next_id)
         return SongrequestQueue._get_next_song(db_session)
     return song
Beispiel #2
0
 def _create(db_session,
             video_id,
             skip_after,
             requested_by_id,
             queue=None,
             backup=False):
     songrequestqueue = SongrequestQueue(video_id=video_id,
                                         date_added=utils.now(),
                                         skip_after=skip_after,
                                         requested_by_id=requested_by_id)
     db_session.add(songrequestqueue)
     db_session.commit()
     SongRequestQueueManager.inset_song(
         songrequestqueue.id,
         "backup-song-queue" if backup else "song-queue", queue)
     return songrequestqueue
Beispiel #3
0
    def songrequest():
        with DBManager.create_session_scope() as db_session:
            playing_in = 0
            track_number = 1
            songs_queue = []
            SongRequestQueueManager.force_reload()
            queue_ids = SongRequestQueueManager.get_next_songs(50)
            current_song = SongrequestQueue._get_current_song(db_session)
            queue = ([current_song]
                     if current_song else []) + SongrequestQueue.sort(
                         queue_ids,
                         SongrequestQueue._from_list_id(db_session, queue_ids))
            for song in queue:
                if song.song_info is None:
                    continue
                jsonify = song.webjsonify()
                m, s = divmod(playing_in, 60)
                m = int(m)
                s = int(s)
                jsonify["playing_in"] = (f"{m:02d}:{s:02d}"
                                         if playing_in != 0 else
                                         ("Currently playing" if current_song
                                          else "Song Request Closed"))
                jsonify["track_number"] = track_number
                playing_in += song.time_left
                track_number += 1
                songs_queue.append(jsonify)

            history = (db_session.query(SongrequestHistory).filter(
                SongrequestHistory.song_info.has(banned=False)).order_by(
                    SongrequestHistory.id.desc()).limit(50).all())
            track_number = 1
            songs_history = []
            for song in history:
                if song.song_info.banned:
                    continue
                jsonify = song.webjsonify()
                jsonify["track_number"] = track_number
                track_number += 1
                songs_history.append(jsonify)

            return render_template("songrequest.html",
                                   songs_queue=songs_queue,
                                   songs_history=songs_history,
                                   live=StreamManager.online)
Beispiel #4
0
 def _get_backup_playlist(db_session, limit=None):
     queued_song_ids = SongRequestQueueManager.get_next_songs(
         limit=limit, queue="backup-song-queue")
     queued_unordered_songs = SongrequestQueue._from_list_id(
         db_session, queued_song_ids)
     queued_songs = SongrequestQueue.sort(queued_song_ids,
                                          queued_unordered_songs)
     songs = []
     for song in queued_songs:
         songs.append(song.webjsonify())
     return songs
Beispiel #5
0
 def playing_in(self, db_session):
     all_song_ids_before_current = SongRequestQueueManager._songs_before(
         self.id, "song-queue")
     if SongRequestQueueManager.song_playing_id:
         all_song_ids_before_current.append(
             SongRequestQueueManager.song_playing_id)
     queued_unordered_songs = SongrequestQueue._from_list_id(
         db_session, all_song_ids_before_current)
     time = 0
     for song in queued_unordered_songs:
         if not song.playing:
             time += song.skip_after if song.skip_after else song.song_info.duration
         else:
             time += song.time_left
     return time
Beispiel #6
0
def init(args):
    import subprocess
    import sys

    from flask import request
    from flask import session
    from flask import g
    from flask_scrypt import generate_random_salt

    import pajbot.utils
    import pajbot.web.common
    import pajbot.web.routes

    from pajbot.managers.db import DBManager
    from pajbot.managers.redis import RedisManager
    from pajbot.managers.schedule import ScheduleManager
    from pajbot.managers.songrequest_queue_manager import SongRequestQueueManager
    from pajbot.models.module import ModuleManager
    from pajbot.models.sock import SocketClientManager
    from pajbot.streamhelper import StreamHelper
    from pajbot.utils import load_config
    from pajbot.web.models import errors
    from pajbot.web.utils import download_logo
    from pajbot.web.utils import download_sub_badge

    ScheduleManager.init()

    config = load_config(args.config)
    # ScheduleManager.init()
    api_client_credentials = ClientCredentials(
        config["twitchapi"]["client_id"], config["twitchapi"]["client_secret"], config["twitchapi"]["redirect_uri"]
    )

    redis_options = {}
    if "redis" in config:
        redis_options = dict(config["redis"])

    RedisManager.init(**redis_options)

    id_api = TwitchIDAPI(api_client_credentials)
    app_token_manager = AppAccessTokenManager(id_api, RedisManager.get())
    twitch_helix_api = TwitchHelixAPI(RedisManager.get(), app_token_manager)
    twitch_badges_api = TwitchBadgesAPI(RedisManager.get())

    if "web" not in config:
        log.error("Missing [web] section in config.ini")
        sys.exit(1)

    if "secret_key" not in config["web"]:
        salt = generate_random_salt()
        config.set("web", "secret_key", salt.decode("utf-8"))

        with open(args.config, "w") as configfile:
            config.write(configfile)

    streamer = config["main"]["streamer"]
    SongRequestQueueManager.init(streamer)
    streamer_user_id = twitch_helix_api.get_user_id(streamer)
    if streamer_user_id is None:
        raise ValueError("The streamer login name you entered under [main] does not exist on twitch.")
    StreamHelper.init_streamer(streamer, streamer_user_id)

    try:
        download_logo(twitch_helix_api, streamer, streamer_user_id)
    except:
        log.exception("Error downloading the streamers profile picture")

    subscriber_badge_version = config["web"].get("subscriber_badge_version", "0")

    # Specifying a value of -1 in the config will disable sub badge downloading. Useful if you want to keep a custom version of a sub badge for a streamer
    if subscriber_badge_version != "-1":
        try:
            download_sub_badge(twitch_badges_api, streamer, streamer_user_id, subscriber_badge_version)
        except:
            log.exception("Error downloading the streamers subscriber badge")

    SocketClientManager.init(streamer)

    app.bot_modules = config["web"].get("modules", "").split()
    app.bot_commands_list = []
    app.bot_config = config
    app.secret_key = config["web"]["secret_key"]
    app.bot_dev = "flags" in config and "dev" in config["flags"] and config["flags"]["dev"] == "1"

    DBManager.init(config["main"]["db"])

    app.module_manager = ModuleManager(None).load()

    pajbot.web.routes.admin.init(app)
    pajbot.web.routes.api.init(app)
    pajbot.web.routes.base.init(app)

    pajbot.web.common.filters.init(app)
    pajbot.web.common.assets.init(app)
    pajbot.web.common.menu.init(app)

    app.register_blueprint(pajbot.web.routes.clr.page)

    errors.init(app, config)
    pajbot.web.routes.clr.config = config

    version = VERSION
    last_commit = None

    if app.bot_dev:
        version = extend_version_if_possible(VERSION)

        try:
            last_commit = subprocess.check_output(["git", "log", "-1", "--format=%cd"]).decode("utf8").strip()
        except:
            log.exception("Failed to get last_commit, will not show last commit")

    default_variables = {
        "version": version,
        "last_commit": last_commit,
        "bot": {"name": config["main"]["nickname"]},
        "site": {
            "domain": config["web"]["domain"],
            "deck_tab_images": config.getboolean("web", "deck_tab_images"),
            "websocket": {"host": config["websocket"].get("host", f"wss://{config['web']['domain']}/clrsocket")},
            "songrequestWS": {
                "host": config["songrequest-websocket"].get(
                    "host", f"wss://{config['web']['domain']}/songrequest_websocket"
                )
            },
        },
        "streamer": {"name": config["web"]["streamer_name"], "full_name": config["main"]["streamer"]},
        "modules": app.bot_modules,
        "request": request,
        "session": session,
        "google_analytics": config["web"].get("google_analytics", None),
    }

    @app.context_processor
    def current_time():
        current_time = {}
        current_time["current_time"] = pajbot.utils.now()
        return current_time

    @app.context_processor
    def inject_default_variables():
        return default_variables
Beispiel #7
0
    def __init__(self, config, args):
        self.config = config
        self.args = args

        self.last_ping = utils.now()
        self.last_pong = utils.now()

        ScheduleManager.init()

        DBManager.init(self.config["main"]["db"])

        # redis
        redis_options = {}
        if "redis" in config:
            redis_options = dict(config.items("redis"))
        RedisManager.init(**redis_options)
        wait_for_redis_data_loaded(RedisManager.get())

        self.nickname = config["main"].get("nickname", "pajbot")

        if config["main"].getboolean("verified", False):
            TMI.promote_to_verified()

        # phrases
        self.phrases = {
            "welcome": ["{nickname} {version} running! HeyGuys"],
            "quit": ["{nickname} {version} shutting down... BibleThump"],
        }
        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()

        # streamer
        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.bot_domain = self.config["web"]["domain"]
        log.debug("Loaded config")

        # do this earlier since schema upgrade can depend on the helix api
        self.api_client_credentials = ClientCredentials(
            self.config["twitchapi"]["client_id"],
            self.config["twitchapi"]["client_secret"],
            self.config["twitchapi"]["redirect_uri"],
        )

        HandlerManager.init_handlers()

        self.twitch_id_api = TwitchIDAPI(self.api_client_credentials)
        self.twitch_tmi_api = TwitchTMIAPI()
        self.app_token_manager = AppAccessTokenManager(self.twitch_id_api,
                                                       RedisManager.get())

        SongRequestQueueManager.init(self.streamer)

        self.twitch_helix_api = TwitchHelixAPI(RedisManager.get(),
                                               self.app_token_manager)
        self.twitch_v5_api = TwitchKrakenV5API(self.api_client_credentials,
                                               RedisManager.get())
        self.spotify_api = None
        self.spotify_token_manager = None
        if ("client_id" in config["spotify"]
                and "client_secret" in config["spotify"]
                and "redirect_uri" in config["spotify"]
                and "user_id" in config["spotify"]):
            if (config["spotify"]["client_id"] != ""
                    and config["spotify"]["client_secret"] != ""
                    and config["spotify"]["redirect_uri"] != ""
                    and config["spotify"]["user_id"] != ""):
                self.spotify_api = SpotifyApi(
                    RedisManager.get(),
                    config["spotify"]["client_id"],
                    config["spotify"]["client_secret"],
                    config["spotify"]["redirect_uri"],
                )
                self.spotify_token_manager = SpotifyAccessTokenManager(
                    self.spotify_api, RedisManager.get(),
                    config["spotify"]["user_id"])
                log.info("Spotify Loaded")

        self.streamlabs_api = None
        if "socket_access_token" in config["streamlabs"]:
            socket_access_token = config["streamlabs"]["socket_access_token"]
            if socket_access_token is not None and socket_access_token != "":
                self.streamlabs_api = StreamLabsAPI(socket_access_token)

        self.bot_user_id = self.twitch_helix_api.get_user_id(self.nickname)
        if self.bot_user_id is None:
            raise ValueError(
                "The bot login name you entered under [main] does not exist on twitch."
            )

        self.streamer_user_id = self.twitch_helix_api.get_user_id(
            self.streamer)
        if self.streamer_user_id is None:
            raise ValueError(
                "The streamer login name you entered under [main] does not exist on twitch."
            )

        self.streamer_access_token_manager = UserAccessTokenManager(
            api=self.twitch_id_api,
            redis=RedisManager.get(),
            username=self.streamer,
            user_id=self.streamer_user_id)

        self.pubsub_api = PubSubAPI(
            self,
            self.streamer_access_token_manager,
        )

        StreamHelper.init_streamer(self.streamer, self.streamer_user_id)

        # SQL migrations
        with DBManager.create_dbapi_connection_scope() as sql_conn:
            sql_migratable = DatabaseMigratable(sql_conn)
            sql_migration = Migration(sql_migratable,
                                      pajbot.migration_revisions.db, self)
            sql_migration.run()

        # Redis migrations
        redis_migratable = RedisMigratable(redis_options=redis_options,
                                           namespace=self.streamer)
        redis_migration = Migration(redis_migratable,
                                    pajbot.migration_revisions.redis, self)
        redis_migration.run()

        # Thread pool executor for async actions
        self.action_queue = ActionQueue()

        # refresh points_rank and num_lines_rank regularly
        UserRanksRefreshManager.start(self.action_queue)

        self.reactor = irc.client.Reactor(self.on_connect)
        # SafeDefaultScheduler makes the bot not exit on exception in the main thread
        # e.g. on actions via bot.execute_now, etc.
        self.reactor.scheduler_class = SafeDefaultScheduler
        self.reactor.scheduler = SafeDefaultScheduler()

        self.start_time = utils.now()
        ActionParser.bot = self

        self.socket_manager = SocketManager(self.streamer, self.execute_now)
        self.stream_manager = StreamManager(self)
        StreamHelper.init_stream_manager(self.stream_manager)

        self.decks = DeckManager()
        self.banphrase_manager = BanphraseManager(self).load()
        self.timer_manager = TimerManager(self).load()
        self.kvi = KVIManager()

        self.websocket_manager = WebSocketManager(self)

        # bot access token
        if "password" in self.config["main"]:
            log.warning(
                "DEPRECATED - Using bot password/oauth token from file. "
                "You should authenticate in web gui using route /bot_login "
                "and remove password from config file")

            access_token = self.config["main"]["password"]

            if access_token.startswith("oauth:"):
                access_token = access_token[6:]

            self.bot_token_manager = UserAccessTokenManager(
                api=None,
                redis=None,
                username=self.nickname,
                user_id=self.bot_user_id,
                token=UserAccessToken.from_implicit_auth_flow_token(
                    access_token),
            )
        else:
            self.bot_token_manager = UserAccessTokenManager(
                api=self.twitch_id_api,
                redis=RedisManager.get(),
                username=self.nickname,
                user_id=self.bot_user_id)

        self.songrequest_manager = SongrequestManager(self)
        self.songrequest_websocket_manager = SongRequestWebSocketManager(self)
        self.emote_manager = EmoteManager(self.twitch_v5_api,
                                          self.action_queue)
        self.epm_manager = EpmManager()
        self.ecount_manager = EcountManager()
        if "twitter" in self.config and self.config["twitter"].get(
                "streaming_type", "twitter") == "tweet-provider":
            self.twitter_manager = PBTwitterManager(self)
        else:
            self.twitter_manager = TwitterManager(self)
        self.module_manager = ModuleManager(self.socket_manager,
                                            bot=self).load()
        self.commands = CommandManager(socket_manager=self.socket_manager,
                                       module_manager=self.module_manager,
                                       bot=self).load()
        self.spotify_streamlabs_manager = SpotifyStreamLabsManager(self)

        HandlerManager.trigger("on_managers_loaded")

        # Commitable managers
        self.commitable = {
            "commands": self.commands,
            "banphrases": self.banphrase_manager
        }

        self.execute_every(60, self.commit_all)
        self.execute_every(1, self.do_tick)

        # promote the admin to level 2000
        self.admin = self.config["main"].get("admin", None)
        if self.admin is None:
            log.warning(
                "No admin user specified. See the [main] section in the example config for its usage."
            )
        else:
            with DBManager.create_session_scope() as db_session:
                admin_user = User.find_or_create_from_login(
                    db_session, self.twitch_helix_api, self.admin)
                if admin_user is None:
                    log.warning(
                        "The login name you entered for the admin user does not exist on twitch. "
                        "No admin user has been created.")
                else:
                    admin_user.level = 2000

        # silent mode
        self.silent = ("flags" in config and "silent" in config["flags"]
                       and config["flags"]["silent"] == "1") or args.silent
        if self.silent:
            log.info("Silent mode enabled")

        # dev mode
        self.dev = "flags" in config and "dev" in config["flags"] and config[
            "flags"]["dev"] == "1"
        if self.dev:
            self.version_long = extend_version_if_possible(VERSION)
        else:
            self.version_long = VERSION

        self.irc = IRCManager(self)

        relay_host = self.config["main"].get("relay_host", None)
        relay_password = self.config["main"].get("relay_password", None)
        if relay_host is not None or relay_password is not None:
            log.warning(
                "DEPRECATED - Relaybroker support is no longer implemented. relay_host and relay_password are ignored"
            )

        self.reactor.add_global_handler("all_events", self.irc._dispatcher,
                                        -10)

        self.data = {
            "broadcaster": self.streamer,
            "version": self.version_long,
            "version_brief": VERSION,
            "bot_name": self.nickname,
            "bot_domain": self.bot_domain,
        }

        self.data_cb = {
            "status_length": self.c_status_length,
            "stream_status": self.c_stream_status,
            "bot_uptime": self.c_uptime,
            "current_time": self.c_current_time,
            "molly_age_in_years": self.c_molly_age_in_years,
        }
        self.user_agent = f"pajbot1/{VERSION} ({self.nickname})"
Beispiel #8
0
    def load_song(self, skipped_by_id=None):
        if not self.module_state["enabled"]:
            return False
        if self.current_song_id:
            with DBManager.create_session_scope() as db_session:
                current_song = SongrequestQueue._from_id(
                    db_session, self.current_song_id)
                if current_song:
                    if current_song.current_song_time > 5:
                        self.previous_queue = 0
                        histroy = current_song._to_histroy(
                            db_session, skipped_by_id)
                        if not histroy:
                            log.info(
                                "History not added because stream is offline!")
                    else:
                        current_song._remove(db_session)
                self._stop_video()
                self._hide()
                db_session.commit()
            self._playlist_history()

        self.current_song_id = None
        self.schedule_job_id = None
        self.module_state["paused"] = False
        self._module_state()
        self.remove_schedule()

        if not self.module_state["requests_open"]:
            if self.previously_playing_spotify:
                self.bot.spotify_api.play(self.bot.spotify_token_manager)
                log.info("Resumed Spotify")
                self.previously_playing_spotify = False
            return False

        with DBManager.create_session_scope() as db_session:
            current_song = SongrequestQueue._get_current_song(db_session)
            if not current_song:
                current_song = SongrequestQueue._pop_next_song(db_session)
            if current_song:
                SongRequestQueueManager.update_song_playing_id(current_song.id)
                current_song.played_for = 0
                current_song.date_resumed = utils.now()
                self.current_song_id = current_song.id
                self._volume()
                self._play(current_song.video_id, current_song.webjsonify())
                self.schedule_job_id = random.randint(1, 100000)
                self.current_song_schedule = ScheduleManager.execute_delayed(
                    current_song.time_left + 10,
                    self.load_song_schedule,
                    args=[self.schedule_job_id])
                if self.settings["use_spotify"]:
                    is_playing, song_name, artistsArr = self.bot.spotify_api.state(
                        self.bot.spotify_token_manager)
                    if is_playing:
                        self.bot.spotify_api.pause(
                            self.bot.spotify_token_manager)
                        self.previously_playing_spotify = True
                if not current_song.requested_by_id:
                    SongrequestQueue._create(db_session,
                                             current_song.video_id,
                                             current_song.skip_after,
                                             None,
                                             backup=True)
                db_session.commit()
                if current_song.requested_by_id:
                    self._playlist()
                else:
                    self._backup_playlist()

                return True
            if not current_song:
                SongRequestQueueManager.update_song_playing_id("")
            if self.settings["use_spotify"]:
                if self.previously_playing_spotify:
                    self.bot.spotify_api.play(self.bot.spotify_token_manager)
                    log.info("Resumed Spotify")
                    self.previously_playing_spotify = False
            if self.is_video_showing:
                self._hide()
        return False
Beispiel #9
0
 def queue(self):
     return SongRequestQueueManager.get_id_index(self.id)
Beispiel #10
0
 def _clear_backup_songs(db_session):
     SongRequestQueueManager.delete_backup_songs()
     return db_session.query(SongrequestQueue).filter_by(
         requested_by=None).delete(synchronize_session="evaluate")
Beispiel #11
0
 def _pruge_videos(db_session, _video_id):
     all_songs = SongrequestQueue._all_by_video_id(db_session, _video_id)
     for song in all_songs:
         SongRequestQueueManager.remove_song_id(song.id)
         song._remove(db_session)
Beispiel #12
0
 def _pop_next_song(db_session):
     next_id = SongRequestQueueManager.get_next_song()
     SongRequestQueueManager.remove_song_id(next_id)
     return db_session.query(SongrequestQueue).filter_by(
         id=next_id).one_or_none()
Beispiel #13
0
    def _move_song(self, to_id):
        if not self.requested_by:
            return

        SongRequestQueueManager.move_song(self.id, to_id)