Example #1
0
class TestAuthentication(TestCase):
    def setUp(self):
        self.ws = WebSocket(sslopt={'check_hostname': False})
        self.ws.connect(url='wss://sim.smogon.com/showdown/websocket')
        msg = ''
        while not msg.startswith('|challstr|'):
            msg = self.ws.recv()
        self.challstr = msg[msg.find('|challstr|') + len('|challstr|'):]
        self.ws.settimeout(.5)

    def test_auth_temp_user(self):
        username = generate_username()
        assertion = auth_temp_user(self.challstr, username)
        login_cmd = f'|/trn {username},0,{assertion}'
        self.ws.send(login_cmd)
        self._username_in_msg(username)

    def test_login(self):
        with open('ts_auth.txt', 'r') as file:
            username, password = file.read().splitlines()
        assertion = login(self.challstr, username, password)
        login_cmd = f'|/trn {username},0,{assertion}'
        self.ws.send(login_cmd)
        self._username_in_msg(username)

    def _username_in_msg(self, username):
        msg = ''
        while not msg.startswith('|updateuser|'):
            msg = self.ws.recv()
        self.assertIn(username, msg)

    def tearDown(self):
        self.ws.close()
Example #2
0
 def create(cls, server=None):
     # type: (Optional[server.SDKServer]) -> USDKConnection
     server = server or get_instance()
     websocket = WebSocket()
     websocket.connect("ws://localhost:{}/eyes".format(server.port))
     websocket.settimeout(20 * 60)
     return cls(websocket, server.log_file_name)
Example #3
0
class Slack(object):
    """Simple class which just allows sending of messages via Slack"""
    base_url = "https://slack.com/api/"

    def __init__(self, token=irakli_is_bad):
        self.token = token

        response = requests.get(Slack.base_url + 'rtm.start', params={"token": token})
        body = response.json()

        self.channels = {c["name"]: c for c in chain(body["channels"], body["groups"])}

        url = body["url"]

        self.socket = WebSocket()
        self.socket.connect(url)
        self.socket.settimeout(5)

        self.message_id = 0

    def send_message(self, channel_name, text):
        """Send a message
        TODO:? Verify success
        """

        message = json.dumps({"id": self.message_id,
                              "type": "message",
                              "channel": self.channels[channel_name]["id"],
                              "text": text})

        self.message_id += 1
        self.socket.send(message)

    def close(self):
        self.socket.close()
Example #4
0
class ShowdownSimulator(BattleSimulator):
    """A :class:`pokebattle_rl_env.battle_simulator.BattleSimulator` using
    `Pokemon Showdown <https://pokemonshowdown.com>`_ as backend.

    View ongoing battles at https://play.pokemonshowdown.com/:attr:`room_id` if :attr:`local` is False or at
    http://localhost:8000/:attr:`room_id` if otherwise.

    Attributes:
        state (:class:`pokebattle_rl_env.game_state.GameState`): The current state of the battle.
        auth (str): The authentication method to use to log into https://pokemonshowdown.com. Options:

            * empty string: Log into a temporary account.
            * `'register'`: Generate a username and password to register an account. The credentials will be output on the
              console.
            * path to authentication file: Logs into an account specified in a text file, where the first line specifies
              the username and the second line specifies the password.


        self_play (bool): Whether to use self play. Note that this is a naive self play-implementation. In fact, agents
            simply play against other agents - a temporary text file keeps track of the battles. Thus, self play only
            works if `number of agents % 2 == 0`. If :attr:`self_play` is false, the agent will battle against random
            human opponents. Keep in mind that this self-play implementation is redundant if multiple agents are
            deployed on a local Pokemon Showdown instance (see :attr:`connection`) without human players. If
            https://github.com/Zarel/Pokemon-Showdown/blob/master/ladders.js#L470 and
            https://github.com/Zarel/Pokemon-Showdown/blob/master/ladders.js#L470 is removed, they will battle against
            each other automatically.
        connection (:class:`pokebattle_rl_env.showdown_simulator.ShowdownConnection`): Details which Pokemon Showdown
            connection to use. The default connection is to the local instance at https://localhost:8000. Use a local
            instance of Pokemon Showdown whenever possible. See https://github.com/Zarel/Pokemon-Showdown for
            installation instructions. Obviously, if self play is not desired, using a local/custom instance is only
            recommended if there are human players on it. Otherwise, set :attr:`connection` to
            :const:`DEFAULT_PUBLIC_CONNECTION` to use the public connection at https://play.pokemonshowdown.com.
        logging_file (bool): Specify the path to a file to log debug output.
        room_id (str): The string used to identify the current battle (room).
    """
    def __init__(self,
                 auth='',
                 self_play=False,
                 connection=DEFAULT_LOCAL_CONNECTION,
                 logging_file=None):
        info('Using Showdown backend')
        self.state = GameState()
        self.auth = auth
        self.self_play = self_play
        self.connection = connection
        if logging_file is not None:
            logger = getLogger()
            logger.removeHandler(logger.handlers[0])
            logger.setLevel(DEBUG)
            handler = FileHandler(filename=logging_file,
                                  mode='w',
                                  encoding='utf-8')
            logger.addHandler(handler)
        self.room_id = None
        self.ws = None
        if self_play:
            self.self_play_opponent = None
        super().__init__()

    def _connect(self, auth):
        self.ws = WebSocket(sslopt={'check_hostname': False})
        self.ws.connect(url=self.connection.ws_url)
        debug('Connected to Showdown socket')
        msg = ''
        while not msg.startswith('|challstr|'):
            msg = self.ws.recv()
        challstr = msg[msg.find('|challstr|') + len('|challstr|'):]
        if auth == 'register':
            self.username = generate_username()
            self.password = generate_token(16)
            assertion = register(challstr=challstr,
                                 username=self.username,
                                 password=self.password)
        elif isfile(auth):
            with open(auth, 'r') as file:
                self.username, password = file.read().splitlines()
                self.password = None
            assertion = login(challstr=challstr,
                              username=self.username,
                              password=password)
        else:
            self.username = generate_username()
            self.password = None
            assertion = auth_temp_user(challstr=challstr,
                                       username=self.username)
        login_cmd = f'|/trn {self.username},0,{assertion}'
        self.ws.send(login_cmd)
        msg = ''
        while not msg.startswith('|updateuser|') and self.username not in msg:
            msg = self.ws.recv()
            debug(msg)

    def _attack(self, move, mega=False, z=False):
        cmd = f'{self.room_id}|/move {move}'
        cmd += ' mega' if mega else ''
        cmd += ' zmove' if z else ''
        debug(cmd)
        self.ws.send(cmd)

    def _switch(self, pokemon):
        cmd = f'{self.room_id}|/switch {pokemon}'
        debug(cmd)
        self.ws.send(cmd)
        pokemon_list = self.state.player.pokemon
        pokemon_list[0], pokemon_list[pokemon -
                                      1] = pokemon_list[pokemon -
                                                        1], pokemon_list[0]

    counter = 0

    def _update_state(self):
        self.counter += 1
        debug('%s, %s, %s', self.username, self.state.player.name,
              self.counter)
        end = False
        while not end:
            msg = self.ws.recv()
            end = self._parse_message(msg)

    def _parse_message(self, msg):
        if self.room_id is None and '|init|battle' in msg:
            self.room_id = msg.split('\n')[0][1:]
        end = False
        if not msg.startswith(f'>{self.room_id}'):
            return False
        debug(msg)
        msgs = msg.split('\n')
        for msg in msgs:
            info = msg.split('|')
            if len(info) < 2:
                continue
            if info[1] == 'player':
                if info[3] == self.username:
                    self.player_short = info[2]
                    self.state.player.name = info[3]
                else:
                    self.opponent = info[3]
                    self.state.opponent.name = self.opponent
                    self.opponent_short = info[2]
            elif info[1] == 'win':
                winner = msg[len('|win|'):]
                self.state.state = 'win' if winner == self.state.player.name else 'loss'
                end = True
            elif info[1] == 'tie':
                self.state.state = 'tie'
                end = True
            elif info[1] == 'turn':
                self.state.turn = int(info[2])
                if self.state.turn == 1:
                    self.state.state = 'ongoing'
                end = True
            elif info[1] == 'html':
                if info[2] == "<div class=\"broadcast-red\"><b>The battle crashed</b><br />Don't worry, we're working on fixing it.</div>":
                    self.state.state = 'tie'
                    end = True
            elif info[1] == 'request':
                if info[2].startswith(
                        '{"wait":true'
                ) and False:  # ToDo: Start battle on first action?
                    end = True
                elif info[2] != '' and not info[2].startswith('{"wait":true'):
                    read_state_json(info[2], self.state)
                    end = self.state.player.force_switch
            elif info[1] == 'replace':
                parse_replace(info, self.state, self.opponent_short)
            elif info[1] == 'move':
                parse_move(info, self.state, self.opponent_short)
            elif info[1] == 'upkeep':
                for effect in self.state.field_effects + self.state.player_conditions + self.state.opponent_conditions:
                    effect.turn += 1
                for pokemon in self.state.player.pokemon + self.state.opponent.pokemon:
                    for status in pokemon.statuses:
                        status.turn += 1
                pass
            elif info[1] == 'error':
                warning(msg)
            elif info[1] == 'switch' or info[1] == 'drag':
                parse_switch(info, self.state, self.opponent_short)
            elif info[1] == '-boost':
                parse_boost(info, self.state, self.opponent_short)
            elif info[1] == '-unboost':
                parse_boost(info,
                            self.state,
                            self.opponent_short,
                            unboost=True)
            elif info[1] == '-damage' or info[1] == '-heal':
                parse_damage_heal(info, self.state, self.opponent_short)
            elif info[1] == '-status':
                parse_status(info, self.state, self.opponent_short)
            elif info[1] == '-curestatus':
                parse_status(info, self.state, self.opponent_short, cure=True)
            elif info[1] == '-message':
                if 'lost due to inactivity.' in info[
                        2] or 'forfeited.' in info[2]:
                    self.state.forfeited = True
            elif info[1] == '-start':
                parse_start_end(info, self.state, self.opponent_short)
            elif info[1] == '-end':
                parse_start_end(info,
                                self.state,
                                self.opponent_short,
                                start=False)
            elif info[1] == '-sidestart':
                parse_sideeffect(info, self.state, self.opponent_short)
            elif info[1] == '-sideend':
                parse_sideeffect(info,
                                 self.state,
                                 self.opponent_short,
                                 start=False)
            elif info[1] == '-weather':
                if info[2] == 'none':
                    self.state.weather = None
                else:
                    if self.state.weather is not None and info[2] == self.state.weather.name and len(info) > 3 and\
                       info[3] == '[upkeep]':
                        self.state.weather.turn += 1
                    else:
                        self.state.weather = BattleEffect(info[2])
            elif info[1] == '-fieldstart':
                parse_field(info, self.state)
            elif info[1] == '-fieldend':
                parse_field(info, self.state, start=False)
            elif info[1] == '-ability':
                pokemon = ident_to_pokemon(info[2], self.state,
                                           self.opponent_short)
                ability = ability_name_to_id(info[3])
                pokemon.ability = ability
            elif info[1] == 'endability':
                pokemon = ident_to_pokemon(info[2], self.state,
                                           self.opponent_short)
                pokemon.ability = None
            elif info[1] == 'detailschange':
                parse_specieschange(info, self.state, self.opponent_short)
            elif info[1] == '-formechange':
                parse_specieschange(info,
                                    self.state,
                                    self.opponent_short,
                                    details=True)
            elif info[1] == '-transform':
                pokemon = ident_to_pokemon(info[2], self.state,
                                           self.opponent_short)
                to_pokemon = ident_to_pokemon(info[3], self.state,
                                              self.opponent_short)
                pokemon.change_species(to_pokemon.species)
                pokemon.transformed = True
            elif info[1] == '-mega':
                parse_mega(info, self.state, self.opponent_short)
            elif info[1] == '-item':
                parse_item(info, self.state, self.opponent_short)
            elif info[1] == '-enditem':
                parse_item(info, self.state, self.opponent_short, start=False)
            elif info[1] == '-zpower':
                if self.opponent_short in msg:
                    self.state.opponent.z_used = True
                else:
                    self.state.player.z_used = True
            # ToDo: |-zpower|POKEMON |move|POKEMON|MOVE|TARGET|[zeffect]
            if '[of]' in msg:
                parse_auxiliary_info(info, self.state, self.opponent_short)
        return end

    def render(self, mode='human'):
        """Renders the ongoing battle, if there is any.

        Args:
            mode (str): Details the rendering mode. Currently, only mode `human` is supported. `human` will simply open
                the ongoing battle in a web browser (if one exists). Therefore, it is advised to call :meth:`render`
                only once per battle.
        """
        if mode == 'human' and self.room_id is not None:
            browser_url = f'{self.connection.web_url}/{self.room_id}'
            webbrowser.open(browser_url)

    def reset(self):
        """Resets the simulator to its initial state. Call this function prior to calling :meth:`act`. It automatically
        sets up a new battle, even if there exists an ongoing battle.
        """
        debug('Reset %s', self.state.player.name)
        if self.state.state == 'ongoing':
            cmd = f'{self.room_id}|/forfeit'
            self.ws.send(cmd)
            debug(cmd)
        if self.room_id is not None:
            cmd = f'|/leave {self.room_id}'
            self.ws.send(cmd)
            debug(cmd)
            self.room_id = None
            self.state = GameState()
            msg = ''
            while 'deinit' not in msg:
                msg = self.ws.recv()
                debug(msg)
        if self.ws is None:
            self._connect(self.auth)
            info('Using username %s with password %s', self.username,
                 self.password)
        self.ws.send('|/utm null')  # Team

        if self.self_play:
            self.ws.settimeout(None)
            # Naive self play
            with open('usernames', 'a') as file:
                file.write(self.username + '\n')
            lines = []
            while len(lines) < 2:
                with open('usernames', 'r') as file:
                    lines = file.readlines()
            usernames = [line[:-1] for line in lines]
            print(self.counter, self.username, lines, usernames)
            username_index = usernames.index(self.username)
            if username_index % 2 == 0:
                opponent = usernames[username_index + 1]
                cmd = f'|/challenge {opponent}, gen7unratedrandombattle'
                self.ws.send(cmd)
                debug(cmd)
            else:
                while True:
                    msg = self.ws.recv()
                    debug(msg)
                    if msg.startswith('|updatechallenges|'):
                        json = loads(msg.split('|')[2])
                        if 'challengesFrom' in json and json['challengesFrom']:
                            opponent = next(iter(json['challengesFrom']))
                            cmd = f'|/accept {opponent}'
                            self.ws.send(cmd)
                            debug(cmd)
                            del lines[username_index - 1]
                            del lines[username_index - 1]
                            with open('usernames', 'w') as file:
                                file.writelines(lines)
                            break

            # if self.self_play_opponent is None:
            #     with open('usernames', 'a') as file:
            #         file.write(self.username + '\n')
            #     sleep(1 + random())
            #     with open('usernames', 'r') as file:
            #         lines = file.readlines()
            #         usernames = [line[:-1] for line in lines]
            #     username_index = usernames.index(self.username)
            #     if username_index % 2 == 0:
            #         self.self_play_opponent = usernames[username_index + 1]
            #         self.ws.send(f'|/challenge {self.self_play_opponent}, gen7randombattle')
            #         print(f'|/challenge {self.self_play_opponent}, gen7randombattle')
            #     else:
            #         self.self_play_opponent = self.username
            #         while True:
            #             msg = self.ws.recv()
            #             if self.debug_output:
            #                 print(msg)
            #             if msg.startswith('|updatechallenges|'):
            #                 json = loads(msg.split('|')[2])
            #                 if 'challengesFrom' in json and json['challengesFrom']:
            #                     self.self_play_opponent = next(iter(json['challengesFrom']))
            #                     self.ws.send(f'|/accept {self.self_play_opponent}')
            #                     if self.debug_output:
            #                         print(f'|/accept {self.self_play_opponent}')
            #                     del lines[username_index - 1]
            #                     del lines[username_index - 1]
            #                     with open('usernames', 'w') as file:
            #                         file.writelines(lines)
            #                     break
            # elif self.self_play_opponent == self.username:
            #     while True:
            #         msg = self.ws.recv()
            #         if self.debug_output:
            #             print(msg)
            #         if msg.startswith('|updatechallenges|'):
            #             json = loads(msg.split('|')[2])
            #             if 'challengesFrom' in json and json['challengesFrom']:
            #                 opponent = next(iter(json['challengesFrom']))
            #                 self.ws.send(f'|/accept {opponent}')
            #                 if self.debug_output:
            #                     print(f'|/accept {opponent}')
            #                 break
            # else:
            #     self.ws.send(f'|/challenge {self.self_play_opponent}, gen7randombattle')
            #     print(f'|/challenge {self.self_play_opponent}, gen7randombattle')

            # p >> |/challenge [OPPONENT], gen7randombattle
            # p << |updatechallenges|{"challengesFrom":{},"challengeTo":{"to":"[OPPONENT]","format":"gen7randombattle"}}
            # o << |updatechallenges|{"challengesFrom":{"[PLAYER]":"gen7randombattle"},"challengeTo":null}
            # o >> |/accept [PLAYER]
            # - << |updatechallenges|{"challengesFrom":{},"challengeTo":null}
            # - << |updatesearch|{"searching":[],"games":null}
            # - << |updatesearch|{"searching":[],"games":{"battle-gen7randombattle-706502869":"[Gen 7] Random Battle"}}
        else:
            # Against human players or other agents
            self.ws.send('|/search gen7unratedrandombattle')  # Tier

        self._update_state()
        if not self.self_play:
            self.ws.send(f'{self.room_id}|/timer on')
        debug('Playing against %s', self.opponent)

    def close(self):
        """Closes the connection to the WebSocket endpoint."""
        self.ws.close()
        info('Connection to Showdown Socket closed')
Example #5
0
def downloadLiveVideo(r: Session, data: dict, threadMap: dict, se: dict,
                      ip: dict, dirName: str, filen: str, imgs: int,
                      imgf: str):
    """下载视频
    - data 数据字典
    - threadMap 线程Map
    - se 设置字典
    - ip 命令行字典
    - dirName 下载的目录
    - filen 最终视频文件名
    - imgs 图片下载状态
    - imgf 图片名称
    -1 建立WebSocket失败
    -2 发送startWatch失败
    -3 找不到ffmpeg
    -4 下载出现问题"""
    logg: Logger = ip['logg'] if 'logg' in ip else None
    oll: autoopenfilelist = ip['oll'] if 'oll' in ip else None
    nte = not ip['te'] if 'te' in ip else True if getset(
        se, 'te') is False else False
    useInternalDownloader = ip['imn'] if 'imn' in ip else True if getset(
        se, 'imn') is True else False
    low_latency = ip['lp'] if 'lp' in ip else False
    ff = system(f"ffmpeg -h > {devnull} 2>&1") == 0
    speed = ip['nsp'] if 'nsp' in ip else 1
    if not ff and not useInternalDownloader:
        return -3
    websocket = WebSocket(enable_multithread=True)
    try:
        pro = None
        if not nte:
            if 'https_proxy' in environ:
                pro = environ['https_proxy']
        if 'httpsproxy' in ip:
            pro = ip['httpsproxy']
        op = getProxyDict(pro)
        headers = {
            "origin": "https://live.nicovideo.jp",
            "User-Agent":
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36",
            "Accept-Language": "ja-JP"
        }
        websocket.connect(data['site']['relive']['webSocketUrl'],
                          header=headers,
                          **op)
    except:
        if logg:
            logg.write(format_exc(), currentframe(),
                       "NicoNico Live Video Create WebSocket Failed")
        return -1
    lock = Lock()
    chasePlay = True if 'nlt' in ip and data['program'][
        'status'] == 'ON_AIR' else False
    if not sendMsg(
            websocket, lock,
            genStartWatching(data['program']['stream']['maxQuality'],
                             low_latency=low_latency,
                             chase_play=chasePlay), logg):
        return -2
    Ok = False
    keepThread: KeepSeatThread = None
    dl = []
    dmf = None
    dmdl = []
    dp: DownloadProcess = None
    dpc = 0
    dmc = 0
    lvid = data['program']['nicoliveProgramId'][2:]
    websocket.settimeout(5)
    keyboardInt = False
    while not Ok:
        try:
            try:
                message = websocket.recv()
            except WebSocketTimeoutException:
                message = None
                if data['program']['status'] == 'ENDED':
                    if makeSureAllClosed(dl):
                        Ok = True
            except WebSocketConnectionClosedException:
                break
            if message is not None and logg:
                logg.write(f"String msg:\n{message}", currentframe(),
                           "NicoNico Live Video WebSocket Get Message")
            if message is not None and message != '':
                msg = loads(message, strict=False)
                if msg["type"] == "ping":
                    sendMsg(websocket, lock, {"type": "pong"}, logg)
                elif msg["type"] == "seat":
                    if keepThread is None:
                        keepThread = KeepSeatThread(
                            f"lv{lvid}", msg["data"]["keepIntervalSec"],
                            websocket, lock, logg)
                        threadMap[f"lv{lvid}_{round(time())}"] = keepThread
                        keepThread.start()
                    else:
                        keepThread._keepIntervalSec = msg["data"][
                            "keepIntervalSec"]
                elif msg["type"] == "statistics":
                    pass
                elif msg["type"] == "stream":
                    if dpc == 0:
                        startpos = max(ip['nlt'], 0) if 'nlt' in ip else max(
                            data['program']['beginTime'] -
                            data['program']['vposBaseTime'] - 5, 0
                        ) if data['program']['status'] == 'ENDED' else None
                    else:
                        startpos = None
                    if useInternalDownloader:
                        if dp is None:
                            dp = DownloadProcess()
                        dt = NicoLiveDownloaderThread(f"lv{lvid},{dpc}", data,
                                                      msg["data"], dp, logg, r,
                                                      dirName)
                        threadMap[f"lv{lvid},{dpc}_{round(time())}"] = dt
                        dl.append(dt)
                        dt.start()
                    else:
                        fn = filen if dpc == 0 else f"{splitext(filen)[0]}_{dpc}{splitext(filen)[1]}"
                        while exists(fn):  # 如果有重复的名字,自动修改名字
                            dpc += 1
                            fn = filen if dpc == 0 else f"{splitext(filen)[0]}_{dpc}{splitext(filen)[1]}"
                        dt2 = FfmpegM3UDownloader(f"lv{lvid},{dpc}", fn, data,
                                                  msg["data"], logg, imgs,
                                                  imgf, oll, startpos)
                        threadMap[f"lv{lvid},{dpc}_{round(time())}"] = dt2
                        dl.append(dt2)
                        dt2.start()
                        if speed != 1:
                            sct = SpeedChangeThread(f"lv{lvid},{dpc}", r,
                                                    msg["data"]["syncUri"],
                                                    dt2, speed, logg)
                            sct.start()
                elif msg["type"] == "disconnect" and msg["data"][
                        "reason"] == "END_PROGRAM":
                    Ok = True
                    break
                elif msg["type"] == "room":
                    if dmc == 0:
                        startpos = max(ip['nlt'], 0) if 'nlt' in ip else max(
                            data['program']['beginTime'] -
                            data['program']['vposBaseTime'] - 5, 0
                        ) if data['program']['status'] == 'ENDED' else None
                    else:
                        startpos = None
                    filen2 = f"{splitext(filen)[0]}.xml"
                    fn = filen2 if dmc == 0 else f"{splitext(filen2)[0]}_{dmc}{splitext(filen2)[1]}"
                    while exists(fn):  # 如果有重复的名字,自动修改名字
                        dmc += 1
                        fn = filen if dmc == 0 else f"{splitext(filen2)[0]}_{dmc}{splitext(filen2)[1]}"
                    if dmf is None:
                        dmf = NicoDanmuFile(fn, data, msg["data"], logg)
                    dmdt = NicoLiveDanmuThread(f"lv{lvid},dm{dmc}", dmf, data,
                                               msg["data"], logg, speed,
                                               headers, op, startpos)
                    threadMap[f"lv{lvid},dm{dmc}_{round(time())}"] = dmdt
                    dmdl.append(dmdt)
                    dmdt.start()
                else:
                    print(msg)
        except KeyboardInterrupt:
            if logg:
                logg.write("Get Keyboard Interrupt", currentframe(),
                           "NicoNico Live Video WebSocket Get KILL")
            keyboardInt = True
            Ok = True
        except:
            if logg:
                logg.write(format_exc(), currentframe(),
                           "NicoNico Live Video WebSocket Error")
    if data['program']['status'] != 'ENDED' or keyboardInt:
        makeSureSendKill(dmdl)
    while not makeSureAllClosed(dmdl):
        sleep(1)
    if dmf is not None:
        dmf.close()
    if keepThread is not None:
        keepThread.kill()
    return 0 if Ok else -4
Example #6
0
class NicoLiveDanmuThread(Thread):
    def __init__(self, name: str, f: NicoDanmuFile, data: dict, room: dict,
                 logg: Logger, speed: Union[int, float], headers: dict,
                 op: dict, startpos: Union[int, float]):
        Thread.__init__(self, name=f"DanmuThread:{name}")
        self._tname = name
        self._file = f
        self._data = data
        self._room = room
        self._logg = logg
        self._speed = speed
        self._headers = headers
        self._op = op
        self._hb = None
        self._sr = None
        self._startpos = startpos
        self._mystop = False
        self._hasNoLastRes = False

    def kill(self):
        self._mystop = True
        if self._logg:
            self._logg.write(f"{self.name}: Get Kill Signial", currentframe(),
                             "NicoNico Live Danmu Thread Get Kill")

    def killSubModule(self):
        if self._hb:
            self._hb.kill()
        if self._sr:
            self._sr.kill()
        while True:
            a = True if self._hb is None or not self._hb.is_alive() else False
            b = True if self._sr is None or not self._sr.is_alive() else False
            if a and b:
                break
            sleep(1)

    def run(self):
        try:
            self.openws()
            self._hb = NicoLiveDanmuHeartBeatThread(self._tname, self._logg,
                                                    self._ws)
            self._hb.start()
            self._sr = NicoLiveDanmuRequestThread(self._tname, self._logg,
                                                  self._ws, self._room,
                                                  self._data, self._startpos,
                                                  self._speed)
            self._sr.start()
            self.loop()
            self.killSubModule()
        except:
            if self._logg:
                self._logg.write(f"{self.name}:\n{format_exc()}",
                                 currentframe(),
                                 "NicoNico Live Danmu Thread Error")
            self.killSubModule()

    def openws(self):
        self._ws = WebSocket(enable_multithread=True)
        self._ws.connect(self._room["messageServer"]["uri"],
                         headers=self._headers,
                         **self._op)
        self._ws.settimeout(5)

    def loop(self):
        while True:
            try:
                if self._mystop:
                    break
                m = self._ws.recv()
                if isinstance(m, str):
                    if self._logg:
                        self._logg.write(
                            f"{self.name}: Get Msg: {m}", currentframe(),
                            "NicoNico Live Danmu Thread Get Message")
                    if m != '':
                        msg = loads(m)
                        if 'thread' in msg:
                            thread = msg['thread']
                            if 'last_res' in thread:
                                self._sr.addData(thread)
                            else:
                                self._hasNoLastRes = True
                        elif 'chat' in msg:
                            startpos = 0 if self._startpos is None else self._startpos
                            self._file.write(msg['chat'], startpos,
                                             self._file.no)
            except WebSocketConnectionClosedException:
                break
            except WebSocketTimeoutException:
                if self._mystop:
                    break
                elif self._data['program'][
                        'status'] == 'ENDED' and self._hasNoLastRes:
                    break
            except KeyboardInterrupt:
                break
            except:
                if self._logg:
                    self._logg.write(
                        f"{self.name}:\n{format_exc()}", currentframe(),
                        "NicoNico Live Danmu Thread Recive Data Error")