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()
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)
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()
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')
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
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")