async def pm(msg: ProtocolMessage) -> None: if len(msg.params) >= 4 and msg.params[3] == "requestpage": return sender = User.get(msg.conn, msg.params[0]) receiver = User.get(msg.conn, msg.params[1]) message = "|".join(msg.params[2:]).strip() if sender.userid == utils.to_user_id(msg.conn.username): return if receiver.userid != utils.to_user_id(msg.conn.username): return await parse_chat_message(msg.conn, None, sender, message)
async def translate(msg: Message) -> None: if len(msg.args) > 3: return word = utils.to_user_id(utils.remove_diacritics(msg.args[0])) if word == "": await msg.reply("Cosa devo tradurre?") return languages_list: list[int] = [] for lang in msg.args[1:]: # Get language ids from the command parameters languages_list.append(utils.get_language_id(lang)) languages_list.append(msg.language_id) # Add the room language languages_list.extend([9, 8]) # Hardcode english and italian as fallbacks # Get the first two unique languages languages = tuple(dict.fromkeys(languages_list))[:2] languages = cast(tuple[int, int], languages) results = _get_translations(word, languages) if results: if len(results) == 1: await msg.reply(" / ".join(sorted(list(results.values())[0]))) return resultstext = "" for key, val in results.items(): if resultstext != "": resultstext += ", " resultstext += f"{' / '.join(sorted(val))} ({key[0]})" await msg.reply(resultstext) return await msg.reply("Non trovato")
async def add_queryresponse_userdetails( self, user: str, *, group: str = " ", rooms: dict[str, str] | None = None, ) -> None: userid = utils.to_user_id(user) if rooms is None: rooms = {} data = { "id": user, "userid": userid, "name": user, "avatar": "1", "group": group, "autoconfirmed": True, "status": "", "rooms": {rooms[room].strip() + room: {} for room in rooms}, } await self.add_messages([ f"|queryresponse|userdetails|{json.dumps(data)}", ])
async def profile(msg: Message) -> None: userid = utils.to_user_id(msg.arg.strip()) if userid == "": userid = msg.user.userid db = Database.open() with db.get_session() as session: stmt = select(d.Users).filter_by(userid=userid) # TODO: remove annotation userdata: d.Users = session.scalar(stmt) if userdata and userdata.userid and userdata.avatar: stmt = ( select(d.Badges).filter_by(userid=userdata.userid).order_by(d.Badges.id) ) badges: list[d.Badges] = session.execute(stmt).scalars().all() if userdata.avatar[0] == "#": avatar_dir = "trainers-custom" avatar_name = userdata.avatar[1:] else: avatar_dir = "trainers" avatar_name = userdata.avatar html = ProfileHTML( avatar_dir=avatar_dir, avatar_name=avatar_name, username=userdata.username or userdata.userid, badges=badges, description=userdata.description, pokemon_icon=userdata.icon, ) await msg.reply_htmlbox(html.doc)
async def join_leave_name(msg: ProtocolMessage) -> None: db = Database.open() with db.get_session() as session: for user in msg.params: stmt = (update(d.TemporaryVoices).filter_by( roomid=msg.room.roomid, userid=utils.to_user_id(user)).values( date=str(datetime.utcnow()))) session.execute(stmt)
async def add_user_leave(self, room: str, user: str) -> None: roomid = utils.to_room_id(room) userid = utils.to_user_id(user) await self.add_messages([ f">{roomid}", f"|l|{userid}", ])
async def chat(msg: ProtocolMessage) -> None: user = User.get(msg.conn, msg.params[0]) message = "|".join(msg.params[1:]).strip() msg.room.dynamic_buffer.append(message) if user.userid == utils.to_user_id(msg.conn.username): return await parse_chat_message(msg.conn, msg.room, user, message)
async def add_user_namechange( self, room: str, user: str, olduser: str, rank: str = " ", *, group: str = " ", ) -> None: roomid = utils.to_room_id(room) userid = utils.to_user_id(user) olduserid = utils.to_user_id(olduser) await self.add_messages([ f">{roomid}", f"|n|{rank}{userid}|{olduserid}", ]) await self.add_queryresponse_userdetails(user, group=group, rooms={roomid: rank})
async def timestampchat(msg: ProtocolMessage) -> None: timestamp = msg.params[0] user = User.get(msg.conn, msg.params[1]) message = "|".join(msg.params[2:]).strip() msg.room.dynamic_buffer.append(message) if user.userid == utils.to_user_id(msg.conn.username): return if int(timestamp) < msg.conn.timestamp: return await parse_chat_message(msg.conn, msg.room, user, message)
def _get_translations( word: str, languages: tuple[int, int]) -> dict[tuple[str, str], set[str]]: word = utils.to_user_id(utils.remove_diacritics(utils.get_alias(word))) results: dict[tuple[str, str], set[str]] = {} db = Database.open("veekun") with db.get_session() as session: tables: dict[str, tuple[type[v.TranslatableMixin], type[TranslatableTableNames]]] = { "ability": (v.Abilities, v.AbilityNames), "item": (v.Items, v.ItemNames), "move": (v.Moves, v.MoveNames), "nature": (v.Natures, v.NatureNames), } for category_name, t in tables.items(): stmt = (select(t[0], t[1].local_language_id).select_from( t[0]).join(t[1]).where( t[1].local_language_id.in_(languages), t[1].name_normalized == word, )) # TODO: remove annotations row: v.TranslatableMixin language_id: int for row, language_id in session.execute(stmt): translation = row.get_translation( f"{category_name}_names", language_id=list(set(languages) - {language_id})[0], fallback_english=False, ) if translation is not None: res = ( category_name, utils.to_id(utils.remove_diacritics(translation)), ) if res not in results: results[res] = set() results[res].add(translation) return results
def get(cls, conn: Connection, userstring: str) -> User: """Safely retrieves a User instance, if it exists, or creates a new one. Args: conn (Connection): Used to access the websocket. userstring (str): User string of the room to retrieve. Returns: User: Existing instance associated with userstring or newly created one. """ if conn not in cls._instances: cls._instances[conn] = WeakValueDictionary() userid = utils.to_user_id(userstring) try: instance = cls._instances[conn][userid] except KeyError: instance = cls._instances[conn][userid] = cls(conn, userstring) return instance
def userid(self) -> UserId: return utils.to_user_id(self.userstring)
async def csv_to_sqlite(conn: Connection) -> None: latest_veekun_commit = "" try: latest_veekun_commit = (subprocess.run( [ "git", "rev-list", "-1", "HEAD", "--", "data/veekun", "databases/veekun.py", "tasks/veekun.py", ], cwd=join(dirname(__file__), ".."), capture_output=True, check=True, ).stdout.decode().strip()) db = Database.open("veekun") with db.get_session() as session: stmt = select(v.LatestCommit.commit_id) if session.scalar(stmt) == latest_veekun_commit: return # database is already up-to-date, skip rebuild except ( subprocess.SubprocessError, # generic subprocess error FileNotFoundError, # git is not available OperationalError, # table does not exist ): pass # always rebuild on error print("Rebuilding veekun database...") with open(utils.get_config_file("veekun.sqlite"), "wb"): # truncate database pass db = Database.open("veekun") v.Base.metadata.create_all(db.engine) tables_classes = { obj.__tablename__: obj for name, obj in inspect.getmembers(v) if inspect.isclass(obj) and obj.__module__ == v.__name__ and hasattr(obj, "__tablename__") } with db.get_session() as session: if latest_veekun_commit: session.add(v.LatestCommit(commit_id=latest_veekun_commit)) for table in v.Base.metadata.sorted_tables: tname = table.key file_name = utils.get_data_file("veekun", f"{tname}.csv") if isfile(file_name): with open(file_name, encoding="utf-8") as f: csv_data = csv.DictReader(f) csv_keys = csv_data.fieldnames if csv_keys is not None: data = [dict(i) for i in csv_data] if hasattr(table.columns, "name_normalized"): for row in data: row["name_normalized"] = utils.to_user_id( utils.remove_diacritics(row["name"])) if tname == "locations": for row in data: if num := re.search(r"route-(\d+)", row["identifier"]): row["route_number"] = num[1] bulk_insert_stmt = insert(tables_classes[tname]) session.execute(bulk_insert_stmt, data) if "identifier" in csv_keys: bulk_update_stmt = (update( tables_classes[tname]).values( identifier=func.replace( tables_classes[tname].identifier, "-", "")).execution_options( synchronize_session=False)) session.execute(bulk_update_stmt)
async def make_connection( enable_commands: bool = False, *, username: str | None = None, password: str = "", avatar: str = "", statustext: str = "", rooms: list[str] | None = None, main_room: str = "lobby", command_character: str = ".", administrators: list[str] | None = None, webhooks: dict[str, str] | None = None, ) -> AsyncGenerator[Any, None]: if username is None: # Create and yield two default connections if no username is passed bot_username = env.str("USERNAME") bot_password = env.str("PASSWORD") mod_username = env.str("TESTS_MOD_USERNAME") mod_password = env.str("TESTS_MOD_PASSWORD") bot_userid = utils.to_user_id(bot_username) mod_userid = utils.to_user_id(mod_username) async with make_connection( True, username=bot_username, password=bot_password ) as bot, make_connection( username=mod_username, password=mod_password ) as mod: await bot.await_message( f'|queryresponse|userdetails|{{"id":"{mod_userid}"', startswith=True ) await mod.await_message( f'|queryresponse|userdetails|{{"id":"{bot_userid}"', startswith=True ) yield bot, mod return url = f"ws://localhost:{showdown_server}/showdown/websocket" if rooms is None: rooms = ["lobby"] if administrators is None: administrators = ["parnassius"] if webhooks is None: webhooks = {"room1": "https://discord.com/api/webhooks/00000/aaaaa"} conn = TestConnection( url=url, username=username, password=password, avatar=avatar, statustext=statustext, rooms=rooms, main_room=main_room, command_character=command_character, administrators=administrators, webhooks=webhooks, unittesting=True, ) if not enable_commands: conn.commands = {} asyncio.create_task(conn._start_websocket()) rooms_to_join = {utils.to_room_id(room) for room in rooms} while rooms_to_join: msg = await conn.recv_queue.get() msg_parts = msg.split("\n") roomname = "" if msg_parts[0][0] == ">": roomname = msg_parts[0] roomid = utils.to_room_id(roomname) if roomid not in rooms_to_join: continue if any(x.startswith("|init|") for x in msg_parts): rooms_to_join.remove(roomid) try: yield conn finally: if conn.websocket is not None: for task in asyncio.all_tasks(): if not task.get_coro().__name__.startswith("test_"): # type: ignore task.cancel() await conn.websocket.close()