async def trad(msg: Message) -> None: parola = utils.to_user_id(utils.remove_accents(msg.arg.lower())) if parola == "": await msg.reply("Cosa devo tradurre?") return results: List[Dict[str, str]] = [] for i in TRANSLATIONS: for j in TRANSLATIONS[i]: if utils.to_user_id(utils.remove_accents(j["en"].lower())) == parola: results.append({"trad": j["it"], "cat": i}) elif utils.to_user_id(utils.remove_accents(j["it"].lower())) == parola: results.append({"trad": j["en"], "cat": i}) if results: if len(results) == 1: await msg.reply(results[0]["trad"]) return resultstext = "" for k in results: if resultstext != "": resultstext += ", " resultstext += "{trad} ({cat})".format(trad=k["trad"], cat=k["cat"]) await msg.reply(resultstext) return await msg.reply("Non trovato")
async def chat(conn: Connection, roomid: str, *args: str) -> None: if len(args) < 2: return user = args[0] message = "|".join(args[1:]).strip() if utils.to_user_id(user) == utils.to_user_id(conn.username): return await parse_chat_message(conn, roomid, user, message)
async def timestampchat(conn: Connection, roomid: str, *args: str) -> None: if len(args) < 3: return timestamp = args[0] user = args[1] message = "|".join(args[2:]).strip() if utils.to_user_id(user) == utils.to_user_id(conn.username): return if int(timestamp) <= conn.timestamp: return await parse_chat_message(conn, roomid, user, message)
async def pm(conn: Connection, roomid: str, *args: str) -> None: if len(args) < 3: return sender = args[0] receiver = args[1] message = "|".join(args[2:]).strip() if utils.to_user_id(sender) == utils.to_user_id(conn.username): return if utils.to_user_id(receiver) != utils.to_user_id(conn.username): return await parse_chat_message(conn, None, sender, message)
async def wrapper( conn: Connection, room: Optional[str], user: str, arg: str ) -> None: if room: # (1) command used in a room: just add the roomid param to arg args = arg.split(",") if arg else [] args.insert(0, room) arg = ",".join(args) else: # (2) command used in PM: check perms if not arg: await conn.send_pm(user, "Specifica il nome della room.") return troom = utils.to_room_id(arg.split(",")[0], fallback="") # target room if not troom: await conn.send_pm(user, "Specifica il nome della room.") return if ( troom not in conn.rooms + conn.private_rooms # bot in room? or utils.to_user_id(user) not in Room.get(troom).users # user in room? ): # Send the same message for both errors to avoid leaking private rooms await conn.send_pm(user, "Sei nella room?") return await func(conn, room, user, arg)
def badges_route(userid: str) -> str: db = Database.open() with db.get_session() as session: userid = utils.to_user_id(userid) if request.method == "POST": if "labelnew" in request.form: image = request.form.get("imagenew") label = request.form.get("labelnew", "") if image: session.add(d.Badges(userid=userid, image=image, label=label)) for i in request.form: if i[:5] != "label" or i == "labelnew": continue row_id = i[5:] image = request.form.get(f"image{row_id}") label = request.form.get(f"label{row_id}", "") delete = request.form.get(f"delete{row_id}") if delete: session.query(d.Badges).filter_by(id=row_id).delete() elif image: session.query(d.Badges).filter_by(id=row_id).update( {"image": image, "label": label} ) user = session.query(d.Users).filter_by(userid=userid).first() badges = ( session.query(d.Badges).filter_by(userid=userid).order_by(d.Badges.id).all() ) return render_template("badges.html", user=user, badges=badges)
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: userdata = session.query(d.Users).filter_by(userid=userid).first() if userdata and userdata.userid and userdata.avatar: badges = ( session.query(d.Badges) .filter_by(userid=userdata.userid) .order_by(d.Badges.id) .all() ) if userdata.avatar[0] == "#": avatar_dir = "trainers-custom" avatar_name = userdata.avatar[1:] else: avatar_dir = "trainers" avatar_name = userdata.avatar html = utils.render_template( "commands/profile.html", avatar_dir=avatar_dir, avatar_name=avatar_name, username=userdata.username, badges=badges, description=userdata.description, ) await msg.reply_htmlbox(html)
async def pm(conn: Connection, room: Room, *args: str) -> None: if len(args) < 3: return if len(args) >= 4 and args[3] == "requestpage": return sender = User.get(conn, args[0]) receiver = User.get(conn, args[1]) message = "|".join(args[2:]).strip() if sender.userid == utils.to_user_id(conn.username): return if receiver.userid != utils.to_user_id(conn.username): return await parse_chat_message(conn, None, sender, message)
async def badge(msg: Message) -> None: admin_rank = msg.user.rank(msg.conn.main_room) rooms: Dict[str, str] = {} for room in msg.user.rooms: rooms[room.roomid] = msg.user.rank(room) or " " token_id = utils.create_token(rooms, 1, admin_rank) userid = utils.to_user_id(msg.arg or msg.user.userid) await msg.user.send(f"{msg.conn.domain}badges/{userid}?token={token_id}")
async def add_user( conn: Connection, roomid: str, user: str, skip_avatar_check: bool = False ) -> None: rank = user[0] username = user[1:].split("@")[0] userid = utils.to_user_id(username) idle = user[-2:] == "@!" room = Room.get(roomid) room.add_user(userid, rank, username, idle) if userid == utils.to_user_id(conn.username): room.roombot = rank == "*" db = Database() sql = "INSERT INTO users (userid, username) VALUES (?, ?) " sql += " ON CONFLICT (userid) DO UPDATE SET username = excluded.username" db.executenow(sql, [userid, username]) if not skip_avatar_check or rank != " ": await conn.send_message("", "/cmd userdetails {}".format(username), False)
async def chat(conn: Connection, room: Room, *args: str) -> None: if len(args) < 2: return user = User.get(conn, args[0]) message = "|".join(args[1:]).strip() room.dynamic_buffer.append(message) if user.userid == utils.to_user_id(conn.username): return await parse_chat_message(conn, room, user, message)
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. """ userid = utils.to_user_id(userstring) if userid in conn.users: return conn.users[userid] return cls(conn, userstring)
async def updateuser(conn: Connection, room: Room, *args: str) -> None: if len(args) < 4: return user = args[0] # named = args[1] avatar = args[2] # settings = args[3] username = user.split("@")[0] if utils.to_user_id(username) != utils.to_user_id(conn.username): return if conn.avatar and avatar != conn.avatar: await conn.send(f"|/avatar {conn.avatar}") if conn.statustext: await conn.send(f"|/status {conn.statustext}") for roomid in list(conn.rooms.keys()): if Room.get(conn, roomid).autojoin: await asyncio.sleep(0.15) await conn.send(f"|/join {roomid}")
async def updateuser(conn: Connection, roomid: str, *args: str) -> None: if len(args) < 4: return user = args[0] # named = args[1] avatar = args[2] # settings = args[3] username = user.split("@")[0] if utils.to_user_id(username) != utils.to_user_id(conn.username): return if conn.avatar and avatar != conn.avatar: await conn.send_message("", "/avatar {}".format(conn.avatar), False) if conn.statustext: await conn.send_message("", "/status {}".format(conn.statustext), False) for public_room in conn.rooms: await conn.send_message("", "/join {}".format(public_room), False) for private_room in conn.private_rooms: await conn.send_message("", "/join {}".format(private_room), False)
async def timestampchat(conn: Connection, room: Room, *args: str) -> None: if len(args) < 3: return timestamp = args[0] user = User.get(conn, args[1]) message = "|".join(args[2:]).strip() room.dynamic_buffer.append(message) if user.userid == utils.to_user_id(conn.username): return if int(timestamp) <= conn.timestamp: return await parse_chat_message(conn, room, user, message)
async def dashboard(conn: Connection, room: Optional[str], user: str, arg: str) -> None: userid = utils.to_user_id(user) for r in conn.rooms: users = Room.get(r).users if userid in users and utils.is_driver(users[userid]["rank"]): rank = users[userid]["rank"] break else: return private_rooms = [r for r in conn.private_rooms if userid in Room.get(r).users] token_id = utils.create_token(rank, private_rooms, 1) await conn.send_pm( user, "{url}?token={token}".format(url=conn.domain, token=token_id) )
async def setprofile(conn: Connection, room: Optional[str], user: str, arg: str) -> None: if len(arg) > 200: await conn.send_reply(room, user, "Errore: lunghezza massima 200 caratteri") return db = Database() sql = "INSERT INTO users (userid, description_pending) VALUES (?, ?) " sql += " ON CONFLICT (userid) DO UPDATE SET description_pending = excluded.description_pending" db.executenow(sql, [utils.to_user_id(user), arg]) await conn.send_reply(room, user, "Salvato") message = "Qualcuno ha aggiornato la sua frase del profilo. " message += ( 'Usa <button name="send" value="/pm ' + conn.username + ', .dashboard">.dashboard</button> per approvarla o rifiutarla') for room in conn.rooms: await conn.send_rankhtmlbox("%", room, message)
async def colorcompare(conn: Connection, room: Optional[str], user: str, arg: str) -> None: if arg == "": return cell = "<td>" cell += ' <div style="background:{color};text-align:center"><br><br>{username}<br><br><br></div>' cell += "</td>" html = '<table style="width:100%;table-layout:fixed">' html += "<tr>" for i in arg.split(","): html += cell.format( color=utils.username_color(utils.to_user_id(i)), username=utils.html_escape(i), ) html += "</tr>" html += "</table>" await conn.send_htmlbox(room, user, html)
def __init__( self, url: str, username: str, password: str, avatar: str, statustext: str, rooms: List[str], main_room: str, command_character: str, administrators: List[str], domain: str, unittesting: bool = False, ) -> None: self.url = url self.username = username self.password = password self.avatar = avatar self.statustext = statustext self.rooms: Dict[RoomId, Room] = {} # roomid, Room for roomname in rooms: roomid = utils.to_room_id(roomname) self.rooms[roomid] = Room(self, roomid, autojoin=True) self.main_room = Room.get(self, main_room) self.command_character = command_character self.administrators = [utils.to_user_id(user) for user in administrators] self.domain = domain self.unittesting = unittesting self.public_roomids: Set[str] = set() self.users: WeakValueDictionary[ # pylint: disable=unsubscriptable-object UserId, User ] = WeakValueDictionary() self.init_tasks = init_tasks self.handlers = handlers self.commands = commands self.timestamp: float = 0 self.lastmessage: float = 0 self.loop: Optional[asyncio.AbstractEventLoop] = None self.websocket: Optional[websockets.client.WebSocketClientProtocol] = None self.connection_start: Optional[float] = None self.tiers: List[TiersDict] = []
async def send_htmlbox( self, room: Optional[str], user: Optional[str], message: str, simple_message: str = "", ) -> None: message = message.replace("\n", "<br>") if room is not None: await self.send_message(room, "/addhtmlbox {}".format(message), False) elif user is not None: room = utils.can_pminfobox_to(self, utils.to_user_id(user)) if room is not None: await self.send_message( room, "/pminfobox {}, {}".format(user, message), False ) else: if simple_message == "": simple_message = "Questo comando è disponibile in PM " simple_message += "solo se sei online in una room dove sono Roombot" await self.send_pm(user, simple_message)
async def addquote(conn: Connection, room: Optional[str], user: str, arg: str) -> None: if room is None or not utils.is_driver(user): return if not arg: await conn.send_message(room, "Cosa devo salvare?") return maxlen = 250 # lower than the message limit to have space for metadata if len(arg) > maxlen: await conn.send_message( room, f"Quote troppo lunga, max {maxlen} caratteri.") return db = Database() sql = "INSERT OR IGNORE INTO quotes (message, roomid, author, date) " sql += "VALUES (?, ?, ?, DATE())" db.executenow(sql, [arg, room, utils.to_user_id(user)]) if db.connection.total_changes: await conn.send_message(room, "Quote salvata.") else: await conn.send_message(room, "Quote già esistente.")
def profilo() -> str: userid = utils.to_user_id(request.args.get("userid", "")) if request.method == "POST": if "description" in request.form: sql = "UPDATE users SET description = ? WHERE id = ? AND userid = ?" g.db.executenow( sql, [request.form["description"], request.form["id"], userid]) if "labelnew" in request.form: image = request.form.get("imagenew") label = request.form.get("labelnew") if label and image: sql = "INSERT INTO badges (userid, image, label) VALUES (?, ?, ?)" g.db.execute(sql, [userid, image, label]) for i in request.form: if i[:5] != "label" or i == "labelnew": continue row_id = i[5:] image = request.form.get(f"image{row_id}") label = request.form.get(f"label{row_id}") if label and image: sql = "UPDATE badges SET image = ?, label = ? WHERE id = ?" g.db.execute(sql, [image, label, row_id]) g.db.commit() sql = "SELECT * FROM users WHERE userid = ?" utente = g.db.execute(sql, [userid]).fetchone() sql = "SELECT * FROM badges WHERE userid = ? ORDER BY id" badges = g.db.execute(sql, [userid]).fetchall() return render_template("profilo.html", utente=utente, badges=badges)
async def learnset(msg: Message) -> None: if len(msg.args) < 2: return pokemon_id = utils.to_user_id(utils.remove_accents(msg.args[0].lower())) version = utils.to_user_id(utils.remove_accents(msg.args[1].lower())) db = Database.open("veekun") with db.get_session() as session: version_group_id: Optional[int] = ( session.query(v.VersionGroups.id) # type: ignore # sqlalchemy .filter_by(identifier=version) .scalar() ) if version_group_id is None: version_group_id = ( session.query(v.Versions.version_group_id) # type: ignore # sqlalchemy .filter_by(identifier=version) .scalar() ) if version_group_id is None: return class MovesDict(TypedDict): name: str level: Optional[int] machine: Optional[str] class ResultsDict(TypedDict): name: str moves: List[MovesDict] results: Dict[int, ResultsDict] = {} pokemon_species = ( session.query(v.PokemonSpecies) # type: ignore # sqlalchemy .options( joinedload(v.PokemonSpecies.pokemon) .joinedload(v.Pokemon.pokemon_moves) .joinedload(v.PokemonMoves.version_group) .raiseload("*") ) .filter_by(identifier=pokemon_id) .first() ) if pokemon_species: for pokemon in pokemon_species.pokemon: for pokemon_move in pokemon.pokemon_moves: version_group = pokemon_move.version_group if version_group.id != version_group_id: continue move = pokemon_move.move move_name = next( (i.name for i in move.move_names if i.local_language_id == 9), "", ) method = pokemon_move.pokemon_move_method method_name = next( ( i.name for i in method.pokemon_move_method_prose if i.local_language_id == 9 ), "", ) data: MovesDict = { "name": move_name, "level": None, "machine": None, } if method.id == 1: # level-up data["level"] = pokemon_move.level elif method.id == 4: # machine machine = next( ( i for i in move.machines if i.version_group_id == version_group.id ), None, ) if machine: machine_name = next( ( i.name for i in machine.item.item_names if i.local_language_id == 9 ), None, ) data["machine"] = machine_name if method.id not in results: results[method.id] = {"name": method_name, "moves": []} results[method.id]["moves"].append(data) for method_id in sorted(results.keys()): if method_id == 1: # level-up results[method_id]["moves"].sort(key=lambda x: (x["level"], x["name"])) elif method_id == 4: # machine results[method_id]["moves"].sort( key=lambda x: (x["machine"], x["name"]) ) else: results[method_id]["moves"].sort(key=lambda x: x["name"]) html = utils.render_template( "commands/learnsets.html", methods=sorted(results.keys()), results=results ) if not html: await msg.reply("Nessun dato") return await msg.reply_htmlbox('<div class="ladder">' + html + "</div>")
def userid(self) -> UserId: return utils.to_user_id(self.userstring)
async def send_pm(self, user: str, message: str, escape: bool = True) -> None: if escape and message[0] == "/": message = "/" + message await self.send("|/w {}, {}".format(utils.to_user_id(user), message))
async def profile(conn: Connection, room: Optional[str], user: str, arg: str) -> None: # pylint: disable=too-many-locals if arg.strip() == "": arg = user arg = utils.to_user_id(arg) db = Database() sql = "SELECT * FROM users WHERE userid = ?" body = db.execute(sql, [arg]).fetchone() if body: body = dict(body) sql = "SELECT image, label " sql += " FROM badges " sql += " WHERE userid = ? ORDER BY id" body["badges"] = db.execute(sql, [body["userid"]]).fetchall() html = "<div>" html += ' <div style="display: table-cell; width: 80px; vertical-align: top">' html += ' <img src="https://play.pokemonshowdown.com/sprites/{avatar_dir}/{avatar_name}.png"' html += ' width="80" height="80">' html += " </div>" html += ' <div style="display: table-cell; width: 100%; vertical-align: top">' html += ' <b style="color: {name_color}">{username}</b><br>{badges}' if body["description"] and body["description"].strip() != "": html += ' <hr style="margin: 4px 0">' html += ' <div style="text-align: justify">{description}</div>' html += " </div>" html += "</div>" if body["avatar"][0] == "#": avatar_dir = "trainers-custom" avatar_name = body["avatar"][1:] else: avatar_dir = "trainers" avatar_name = body["avatar"] username = body["username"] name_color = utils.username_color(utils.to_user_id(username)) badges = "" badge = '<img src="{image}" width="12" height="12" title="{title}"' badge += ' style="border: 1px solid; border-radius: 2px; margin: 2px 1px 0 0">' for i in body["badges"]: badges += badge.format(image=i["image"], title=utils.html_escape(i["label"])) description = utils.html_escape(body["description"]) await conn.send_htmlbox( room, user, html.format( avatar_dir=avatar_dir, avatar_name=avatar_name, name_color=name_color, username=username, badges=badges, description=description, ), )
async def learnset(conn: Connection, room: Optional[str], user: str, arg: str) -> None: args = arg.split(",") if len(args) < 2: return pokemon = utils.to_user_id(utils.remove_accents(args[0].lower())) version_group = utils.to_user_id(utils.remove_accents(args[1].lower())) db = Database("veekun") sql = "SELECT id FROM version_groups WHERE identifier = ?" version_group_id = db.execute(sql, [version_group]).fetchone() if version_group_id is None: sql = "SELECT version_group_id FROM versions WHERE identifier = ?" version_group_id = db.execute(sql, [version_group]).fetchone() if version_group_id is None: return version_group_id = version_group_id[0] sql = """SELECT pokemon_moves.version_group_id, pokemon_moves.pokemon_move_method_id, (SELECT GROUP_CONCAT(IFNULL(version_names.name, ''), '/') FROM versions LEFT JOIN version_names ON version_names.version_id = versions.id AND version_names.local_language_id = 9 WHERE versions.version_group_id = pokemon_moves.version_group_id ORDER BY versions.id) AS version_group, IFNULL(move_names.name, '') AS move_name, IFNULL(pokemon_move_method_prose.name, '') AS method_name, IFNULL(pokemon_moves.level, 0) AS level, IFNULL(item_names.name, '') AS machine FROM pokemon_species LEFT JOIN pokemon ON pokemon.species_id = pokemon_species.id LEFT JOIN pokemon_moves ON pokemon_moves.pokemon_id = pokemon.id JOIN version_groups ON version_groups.id = pokemon_moves.version_group_id JOIN moves ON moves.id = pokemon_moves.move_id LEFT JOIN move_names ON move_names.move_id = moves.id AND move_names.local_language_id = 9 JOIN pokemon_move_methods ON pokemon_move_methods.id = pokemon_moves.pokemon_move_method_id LEFT JOIN pokemon_move_method_prose ON pokemon_move_method_prose.pokemon_move_method_id = pokemon_move_methods.id AND pokemon_move_method_prose.local_language_id = 9 LEFT JOIN machines ON machines.move_id = moves.id AND pokemon_move_methods.id = 4 AND machines.version_group_id = version_groups.id LEFT JOIN item_names ON item_names.item_id = machines.item_id AND item_names.local_language_id = 9 WHERE pokemon_species.identifier = ? AND version_groups.id = ? ORDER BY pokemon_moves.pokemon_move_method_id, pokemon_moves.level, machines.machine_number, move_names.name""" html = "" current_move_method_id = 0 for row in db.execute(sql, [pokemon, version_group_id]): if current_move_method_id != row["pokemon_move_method_id"]: if current_move_method_id != 0: html += "</tbody></table>" html += "</details>" html += ( "<details><summary><b><big>" + utils.html_escape(row["method_name"]) + "</big></b></summary>" ) html += '<table style="margin: 5px 0"><tbody>' html += "<tr>" html += " <th>Move</th>" if row["pokemon_move_method_id"] == 1: # level-up html += " <th>Level</th>" elif row["pokemon_move_method_id"] == 2: # egg pass elif row["pokemon_move_method_id"] == 4: # machine html += " <th>Machine</th>" html += "</tr>" current_move_method_id = row["pokemon_move_method_id"] html += "<tr>" html += " <td>" + utils.html_escape(row["move_name"]) + "</td>" if current_move_method_id == 1: # level-up html += ( ' <td style="text-align: right">' + utils.html_escape(str(row["level"])) + "</td>" ) elif current_move_method_id == 2: # egg pass elif current_move_method_id == 4: # machine html += " <td>" + utils.html_escape(row["machine"]) + "</td>" html += "</tr>" if current_move_method_id != 0: html += "</tbody></table>" html += "</details>" if not html: await conn.send_reply(room, user, "Nessun dato") return await conn.send_htmlbox(room, user, '<div class="ladder">' + html + "</div>")
async def remove_user(conn: Connection, roomid: str, user: str) -> None: Room.get(roomid).remove_user(utils.to_user_id(user))
async def kill(conn: Connection, room: Optional[str], user: str, arg: str) -> None: if utils.to_user_id( user) in conn.administrators and conn.websocket is not None: await conn.websocket.close()