コード例 #1
0
ファイル: profile.py プロジェクト: prnss/cerbottana
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)
コード例 #2
0
async def cleanup_table(conn: Connection) -> None:
    db = Database.open()
    with db.get_session() as session:
        stmt = (delete(d.Tokens).where(
            d.Tokens.expiry < str(datetime.utcnow())).execution_options(
                synchronize_session=False))
        session.execute(stmt)
コード例 #3
0
async def addquote(msg: Message) -> None:
    if not msg.arg:
        await msg.reply("Cosa devo salvare?")
        return

    db = Database.open()
    with db.get_session() as session:
        result = d.Quotes(
            message=msg.arg,
            roomid=msg.parametrized_room.roomid,
            author=msg.user.userid,
            date=str(date.today()),
        )
        session.add(result)
        session.commit()

        try:
            if result.id:
                await msg.reply("Quote salvata.")
                if msg.room is None:
                    await msg.parametrized_room.send_modnote(
                        "QUOTE ADDED", msg.user, msg.arg)
                return
        except ObjectDeletedError:
            pass
        await msg.reply("Quote già esistente.")
コード例 #4
0
ファイル: tempvoices.py プロジェクト: prnss/cerbottana
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)
コード例 #5
0
ファイル: usernames.py プロジェクト: prnss/cerbottana
async def antonio200509(msg: Message) -> None:
    db = Database.open("veekun")
    with db.get_session() as session:
        stmt = (select(v.PokemonSpeciesNames).filter_by(
            local_language_id=msg.language_id).order_by(func.random()))
        # TODO: remove annotation
        species: v.PokemonSpeciesNames = session.scalar(stmt)
        if not species:
            raise SQLAlchemyError("Missing PokemonSpeciesNames data")
        species_name = species.name
    numbers = str(random.randint(0, 999999)).zfill(6)
    await msg.reply(f'Antonio{numbers} guessed "{species_name}"!')
コード例 #6
0
ファイル: locations.py プロジェクト: prnss/cerbottana
async def encounters(msg: Message) -> None:
    if len(msg.args) < 1:
        return

    location_id = utils.to_id(utils.remove_diacritics(msg.args[0].lower()))

    language_id = msg.language_id
    if len(msg.args) >= 2:
        language_id = utils.get_language_id(msg.args[1], fallback=language_id)

    db = Database.open("veekun")

    with db.get_session(language_id) as session:

        stmt = (select(v.Locations).options(
            selectinload(v.Locations.location_areas).options(
                selectinload(v.LocationAreas.location_area_prose),
                selectinload(v.LocationAreas.encounters).options(
                    selectinload(v.Encounters.version).selectinload(
                        v.Versions.version_names),
                    selectinload(v.Encounters.pokemon).selectinload(
                        v.Pokemon.species).selectinload(
                            v.PokemonSpecies.pokemon_species_names),
                    selectinload(v.Encounters.encounter_slot).selectinload(
                        v.EncounterSlots.encounter_method).selectinload(
                            v.EncounterMethods.encounter_method_prose),
                    selectinload(v.Encounters.encounter_condition_value_map).
                    selectinload(v.EncounterConditionValueMap.
                                 encounter_condition_value).selectinload(
                                     v.EncounterConditionValues.
                                     encounter_condition_value_prose),
                ),
            )).filter_by(identifier=location_id))
        # TODO: remove annotation
        location: v.Locations | None = session.scalar(stmt)
        if location is None:
            await msg.reply("Location not found.")
            return

        results = Encounters()

        for area in location.location_areas:
            for encounter in area.encounters:
                results.add_encounter_slot(area, encounter)

        html = EncountersHTML(encounters_data=results)

        if not html:
            await msg.reply("No data available.")
            return

        await msg.reply_htmlbox(html.doc)
コード例 #7
0
ファイル: profile.py プロジェクト: prnss/cerbottana
async def clearprofile(msg: Message) -> None:
    db = Database.open()
    with db.get_session() as session:
        userid = msg.user.userid
        session.add(d.Users(userid=userid))
        stmt = (
            update(d.Users)
            .filter_by(userid=userid)
            .values(description="", description_pending="")
        )
        session.execute(stmt)

    await msg.reply("Frase rimossa")
コード例 #8
0
ファイル: profile.py プロジェクト: prnss/cerbottana
async def rifiutaprofilo(msg: Message) -> None:
    db = Database.open()

    with db.get_session() as session:
        parts = msg.arg.split(",")
        stmt = (
            update(d.Users)
            .filter_by(id=parts[0], description_pending=",".join(parts[1:]))
            .values(description_pending="")
        )
        session.execute(stmt)

    await msg.user.send_htmlpage("pendingdescriptions", msg.conn.main_room)
コード例 #9
0
ファイル: tempvoices.py プロジェクト: prnss/cerbottana
async def tempvoice(msg: Message) -> None:
    if msg.parametrized_room.roombot and msg.user.rank(
            msg.parametrized_room) == " ":
        await msg.parametrized_room.send(f"/roomvoice {msg.user.userid}",
                                         False)
        db = Database.open()
        with db.get_session() as session:
            session.add(
                d.TemporaryVoices(
                    roomid=msg.parametrized_room.roomid,
                    userid=msg.user.userid,
                    date=str(datetime.utcnow()),
                ))
コード例 #10
0
ファイル: eightball.py プロジェクト: prnss/cerbottana
async def removeeightballanswerid(msg: Message) -> None:
    if len(msg.args) != 2:
        return

    db = Database.open()
    with db.get_session() as session:
        stmt = select(d.EightBall).filter_by(
            id=msg.args[0], roomid=msg.parametrized_room.roomid)
        answer: d.EightBall  # TODO: remove annotation
        if answer := session.scalar(stmt):
            if msg.room is None:
                await msg.parametrized_room.send_modnote(
                    "EIGHTBALL ANSWER REMOVED", msg.user, answer.answer)
            session.delete(answer)
コード例 #11
0
async def eightball(msg: Message) -> None:
    db = Database.open()

    with db.get_session() as session:
        language_name = msg.language
        if language_name not in DEFAULT_ANSWERS:
            language_name = "English"
        answers = DEFAULT_ANSWERS[language_name]

        if msg.room:
            stmt = select(d.EightBall.answer).filter_by(roomid=msg.room.roomid)
            answers.extend(session.execute(stmt).scalars())

        await msg.reply(random.choice(answers))
コード例 #12
0
async def removequoteid(msg: Message) -> None:
    room = msg.parametrized_room

    if len(msg.args) != 2:
        return

    db = Database.open()
    with db.get_session() as session:
        stmt = select(d.Quotes).filter_by(id=msg.args[0], roomid=room.roomid)
        quote: d.Quotes  # TODO: remove annotation
        if quote := session.scalar(stmt):
            await msg.parametrized_room.send_modnote("QUOTE REMOVED", msg.user,
                                                     quote.message)
            session.delete(quote)
コード例 #13
0
ファイル: eightball.py プロジェクト: prnss/cerbottana
async def removeeightballanswer(msg: Message) -> None:
    if not msg.arg:
        await msg.reply("Che risposta devo cancellare?")
        return

    db = Database.open()
    with db.get_session() as session:
        stmt = delete(d.EightBall).filter_by(
            answer=msg.arg, roomid=msg.parametrized_room.roomid)
        if session.execute(stmt).rowcount:  # type: ignore[attr-defined]
            await msg.reply("Risposta cancellata.")
            if msg.room is None:
                await msg.parametrized_room.send_modnote(
                    "EIGHTBALL ANSWER REMOVED", msg.user, msg.arg)
        else:
            await msg.reply("Risposta inesistente.")
コード例 #14
0
async def removequote(msg: Message) -> None:
    if not msg.arg:
        await msg.reply("Che quote devo cancellare?")
        return

    db = Database.open()
    with db.get_session() as session:
        stmt = delete(d.Quotes).filter_by(message=msg.arg,
                                          roomid=msg.parametrized_room.roomid)
        if session.execute(stmt).rowcount:  # type: ignore[attr-defined]
            await msg.reply("Quote cancellata.")
            if msg.room is None:
                await msg.parametrized_room.send_modnote(
                    "QUOTE REMOVED", msg.user, msg.arg)
        else:
            await msg.reply("Quote inesistente.")
コード例 #15
0
async def randquote(msg: Message) -> None:
    db = Database.open()
    with db.get_session() as session:
        stmt = (select(d.Quotes).filter_by(
            roomid=msg.parametrized_room.roomid).order_by(func.random()))
        if msg.arg:
            # LIKE wildcards are supported and "*" is considered an alias for "%".
            keyword = msg.arg.replace("*", "%")
            stmt = stmt.where(d.Quotes.message.ilike(f"%{keyword}%"))

        quote: d.Quotes = session.scalar(stmt)  # TODO: remove annotation
        if not quote:
            await msg.reply("Nessuna quote trovata.")
            return

        html = e.RawTextNode(to_html_quotebox(quote.message))
        await msg.reply_htmlbox(html)
コード例 #16
0
ファイル: translations.py プロジェクト: prnss/cerbottana
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
コード例 #17
0
ファイル: icons.py プロジェクト: prnss/cerbottana
async def icon(msg: Message) -> None:
    if len(msg.args) < 1:
        await msg.reply(
            "You can set your Pokémon using "
            f"``{msg.conn.command_character}icon <pokemon>``, "
            "or install the userstyle (https://git.io/JuoFg) "
            "using Stylus (https://add0n.com/stylus.html)."
        )
        return

    search_query = utils.to_id(msg.args[0])
    dex_entry = utils.get_ps_dex_entry(search_query)
    if dex_entry is None or dex_entry["num"] <= 0:
        await msg.reply("Pokémon not found.")
        return

    db = Database.open()
    with db.get_session() as session:
        userid = msg.user.userid
        session.add(d.Users(userid=userid))
        stmt = (
            update(d.Users)
            .filter_by(userid=userid)
            .values(icon=utils.to_id(dex_entry["dex_name"]))
        )
        session.execute(stmt)

        # Update the CSV file
        stmt_csv = (
            select(d.Users.userid, d.Users.icon)
            .where(d.Users.icon.is_not(None))
            .order_by(d.Users.userid)
        )
        with utils.get_config_file("userlist_icons.csv").open(
            "w", encoding="utf-8"
        ) as f:
            f.writelines(
                [f"{userid},{icon}\n" for userid, icon in session.execute(stmt_csv)]
            )

    await msg.reply(
        "Done. Your Pokémon might take up to 24 hours to appear on the userstyle."
    )
コード例 #18
0
ファイル: html_utils.py プロジェクト: prnss/cerbottana
    def load_page(self, page: int) -> None:
        db = Database.open()
        with db.get_session() as session:
            stmt_last_page = self._stmt.with_only_columns(func.count())
            last_page = math.ceil(session.scalar(stmt_last_page) / 100)
            page = min(page, last_page)
            stmt_rs = self._stmt.limit(100).offset(100 * (page - 1))

            rs = session.execute(stmt_rs).all()

            with self.doc, e.Div(class_="pad"):
                e.H2(self._title)
                if not rs:
                    e.TextNode("No results found")
                    return

                with e.Div(class_="ladder"), e.Table():
                    with e.Tr():
                        for field_header, _ in self._fields:
                            e.Th(field_header)
                        e.Th(self._actions_header)

                    for row in rs:
                        with e.Tr():
                            for _, field in self._fields:
                                e.Td(
                                    str(getattr(row[0], field) or "")
                                    if isinstance(field, str) else field(row))

                            with e.Td(style=self._get_css("one_pixel_width")):
                                self._add_action_buttons(row)

                page_cmd = (f"/pm {self._botname}, {self._cmd_char}changepage "
                            f"{self._command}, {self._room.roomid}, ")
                for p in range(last_page):
                    with e.Button(p + 1, class_="option") as btn:
                        if page == p + 1:
                            btn.add_class("sel")
                            btn["disabled"] = True
                        else:
                            btn["name"] = "send"
                            btn["value"] = f"{page_cmd}{p + 1}"
コード例 #19
0
    def get_required_rank(self, roomid: RoomId | None, is_pm: bool) -> Role:
        req_rank = self.required_rank

        if self.required_rank_editable is not False and roomid:
            command = f".{self.name}"
            if isinstance(self.required_rank_editable, str):
                command = self.required_rank_editable

            db = Database.open()
            with db.get_session() as session:
                stmt = select(d.CustomPermissions.required_rank).filter_by(
                    roomid=roomid, command=command
                )
                custom_rank: Role | None = session.scalar(stmt)
                if custom_rank:
                    req_rank = custom_rank

        if is_pm and isinstance(self.allow_pm, str):
            req_rank = self.allow_pm

        return req_rank
コード例 #20
0
async def setpermission(msg: Message) -> None:
    room = msg.parametrized_room

    if len(msg.args) != 3:
        return

    command = msg.args[0]
    if command not in Command.get_rank_editable_commands():
        return

    rank = msg.args[2]
    if rank not in PERMISSION_ROLES:
        return
    rank = cast(Role | Literal["default"], rank)

    db = Database.open()
    with db.get_session() as session:
        if rank == "default":
            stmt = delete(d.CustomPermissions).filter_by(roomid=room.roomid,
                                                         command=command)
            session.execute(stmt)
        else:
            session.add(
                d.CustomPermissions(roomid=room.roomid,
                                    command=command,
                                    required_rank=rank))

    await room.send_modnote("PERMISSIONS", msg.user,
                            f"set the required rank for {command} to {rank}")

    try:
        page = int(msg.args[1])
    except ValueError:
        page = 1

    await msg.user.send_htmlpage("permissions",
                                 room,
                                 page,
                                 scroll_to_top=False)
コード例 #21
0
async def addeightballanswer(msg: Message) -> None:
    if not msg.arg:
        await msg.reply("Cosa devo salvare?")
        return

    db = Database.open()
    with db.get_session() as session:
        result = d.EightBall(answer=msg.arg, roomid=msg.parametrized_room.roomid)
        session.add(result)
        session.commit()

        try:
            if result.id:
                await msg.reply("Risposta salvata.")
                if msg.room is None:
                    await msg.parametrized_room.send_modnote(
                        "EIGHTBALL ANSWER ADDED", msg.user, msg.arg
                    )
                return
        except ObjectDeletedError:
            pass
        await msg.reply("Risposta già esistente.")
コード例 #22
0
ファイル: profile.py プロジェクト: prnss/cerbottana
async def setprofile(msg: Message) -> None:
    if not msg.arg:
        await msg.reply("Specifica una frase da inserire nel tuo profilo")
        return

    if len(msg.arg) > 250:
        await msg.reply("Errore: lunghezza massima 250 caratteri")
        return

    # authorized: True if msg.user can approve new descriptions.
    authorized = msg.user.has_role("driver", msg.conn.main_room)

    db = Database.open()
    with db.get_session() as session:
        userid = msg.user.userid
        session.add(d.Users(userid=userid))
        stmt = update(d.Users).filter_by(userid=userid)
        if authorized:
            # Authorized users skip the validation process.
            stmt = stmt.values(description=msg.arg, description_pending="")
        else:
            stmt = stmt.values(description_pending=msg.arg)
        session.execute(stmt)

    await msg.reply("Salvato")

    if not authorized:
        username = msg.user.username
        botname = msg.conn.username
        cmd = f"{msg.conn.command_character}pendingdescriptions"

        html = f"{username} ha aggiornato la sua frase del profilo." + e.Br()
        html += (
            "Usa "
            + e.Button(cmd, name="send", value=f"/pm {botname}, {cmd}")
            + " per approvarla o rifiutarla"
        )
        await msg.conn.main_room.send_rankhtmlbox("%", html)
コード例 #23
0
ファイル: tempvoices.py プロジェクト: prnss/cerbottana
async def demote_old_temporary_voices(conn: Connection) -> None:
    await asyncio.sleep(3 * 60 * 60)

    db = Database.open()
    while True:

        with db.get_session() as session:
            stmt = select(d.TemporaryVoices).filter(
                d.TemporaryVoices.date < datetime.utcnow() -
                timedelta(days=30))
            user: d.TemporaryVoices | None = session.scalar(stmt)

            if user:
                room = Room.get(conn, user.roomid)
                if room.roombot:
                    await room.send(f"/roomdeauth {user.userid}", False)
                session.delete(user)
                # sleep for a minute, then try to deauth another user
                wait_time = 60
            else:
                # sleep for a day if there are no more users to deauth
                wait_time = 24 * 60 * 60

        await asyncio.sleep(wait_time)
コード例 #24
0
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)
コード例 #25
0
ファイル: learnsets.py プロジェクト: prnss/cerbottana
async def learnset(msg: Message) -> None:
    if len(msg.args) < 2:
        return

    pokemon_id = utils.to_id(utils.remove_diacritics(msg.args[0].lower()))
    version_id = utils.to_id(utils.remove_diacritics(msg.args[1].lower()))

    language_id = msg.language_id
    if len(msg.args) >= 3:
        language_id = utils.get_language_id(msg.args[2], fallback=language_id)

    db = Database.open("veekun")

    with db.get_session(language_id) as session:

        stmt = select(v.VersionGroups).filter_by(identifier=version_id)
        # TODO: remove annotation
        version_group: v.VersionGroups | None = session.scalar(stmt)

        if version_group is None:
            stmt = select(v.Versions).filter_by(identifier=version_id)
            # TODO: remove annotation
            version: v.Versions | None = session.scalar(stmt)
            if version is None:
                await msg.reply("Game version not found.")
                return
            version_group = version.version_group

        stmt = (select(v.PokemonSpecies).options(
            selectinload(v.PokemonSpecies.pokemon).selectinload(
                v.Pokemon.pokemon_moves.and_(
                    v.PokemonMoves.version_group_id == version_group.id)).
            options(
                selectinload(v.PokemonMoves.move).options(
                    selectinload(v.Moves.move_names),
                    selectinload(
                        v.Moves.machines.and_(
                            v.Machines.version_group_id ==
                            version_group.id)).selectinload(
                                v.Machines.item).selectinload(
                                    v.Items.item_names),
                ),
                selectinload(v.PokemonMoves.pokemon_move_method).selectinload(
                    v.PokemonMoveMethods.pokemon_move_method_prose),
            )).filter_by(identifier=pokemon_id))
        # TODO: remove annotation
        pokemon_species: v.PokemonSpecies | None = session.scalar(stmt)
        if pokemon_species is None:
            await msg.reply("Pokémon not found.")
            return

        results = Learnset()

        all_forms = set(pokemon_species.pokemon)

        for pokemon in pokemon_species.pokemon:
            for pokemon_move in pokemon.pokemon_moves:
                results.add_move(pokemon, pokemon_move)

        for method_data in results.methods.values():
            for move_data in method_data.moves.values():
                if move_data.forms == all_forms:
                    move_data.forms = set()
                else:
                    method_data.form_column = True

        html = LearnsetHTML(learnset_data=results)

        if not html:
            await msg.reply("No data available.")
            return

        await msg.reply_htmlbox(html.doc)
コード例 #26
0
ファイル: database.py プロジェクト: prnss/cerbottana
async def create_or_upgrade_database(conn: Connection) -> None:
    db = Database.open()
    d.Base.metadata.create_all(db.engine)