Giter Site home page Giter Site logo

apricot-s / majsoulrpa Goto Github PK

View Code? Open in Web Editor NEW
19.0 4.0 5.0 2.36 MB

A Robotic Process Automation (RPA) framework for Mahjong Soul (雀魂)

License: Other

Python 100.00%
mahjong mahjong-soul mahjongsoul majsoul python rpa automation automation-framework robotic-process-automation

majsoulrpa's Issues

`HomePresentation.join_room` で指定した部屋への join が失敗した原因を識別したい

HomePresentation.join_room の戻り値の型は bool ですが,これだと False が返ってきたときに

  • 指定した ID の部屋が存在しない
  • 指定した ID の部屋が満員である
  • 指定した ID の部屋の対戦がすでに始まっている

のどれが原因で部屋への join が失敗したかが分からないです.上記3つはゲーム画面上のエラーメッセージとしては区別されているため, API を調べれば区別できる可能性が高いと思います(要調査).これが区別できるように join_room を拡張してほしいです.

メールアドレスの入力エラーを処理する

概要

メールアドレスの形式が間違っていても
正しいときと同様のボタンの表示待ち・タイムアウトが発生する。
また、メールアドレスの再入力ができないため、
メールアドレスの形式は正しいがtypoがあった場合に再入力できない。
(参考: メールアドレス欄の最大入力可能文字数は半角全角合わせて50文字までである。)

再現手順

  1. AuthPresentation.enter_email_address()にメールアドレスとして不正な文字列を渡す。
  2. 「コードを受け取る」がクリックされると「使用不可」の文字が出る(アドレスが長い場合「使」の下に重なる)。

To Be

  1. メールアドレスが最大入力文字数より多い(51文字以上)の場合は入力せずにValueErrorを投げる。
  2. メールアドレスの形式が間違っている(「使用不可」が出る)場合はValueErrorを投げる。
  3. メールアドレスの形式が正しいかに関わらず再入力できるようにする。

`majsoulrpa_remote_browser` コマンドを実行すると `ModuleNotFoundError` 例外が送出される

概要

majsoulrpa_remote_browser コマンドを実行すると ModuleNotFoundError 例外が送出される

再現手順

python3 -m pip . でインストール後, majsoulrpa_remote_browser コマンドを実行する.

As Is

以下の例外が送出されてコマンドの実行が終了する.

Traceback (most recent call last):
  File "C:\Users\Cryolite\Documents\work\majsoulrpa\.venv\Scripts\\majsoulrpa_remote_browser", line 3, in <module>
    from src.majsoulrpa.remote_browser._remote_browser import main
ModuleNotFoundError: No module named 'src'

To Be

リモートブラウザが正常に起動する.

Presentationが_implではなくRPAに依存するようにする

概要

各Presentationは現在_impl
BrowserBase, MessageQueueClientBase, PresentationCreatorBase
をメンバーに持っているが、できればRPAをメンバーに持つようにする
ただし、RPAのプライベートメンバーへのアクセスを避けつつ外部には非公開にする必要がある(要設計)

なぜ必要か

  • 現在の実装だと利用者はRPA.waitからしかPresentationBaseを受け取れず、自分で生成できない
  • RPA.close()にてBrowserBase, MessageQueueClientBaseを終了してもPresentationからの参照が残っているため
    _implのクラスの__del__が呼ばれない

To Be

Presentationクラスの__init___implのクラスではなくRPAクラスを与える
RPAはPresentationに各メンバーを公開する手段を持つ(フレームワーク利用者には非公開にする)

開発方針・注意事項のREADMEへの追記

概要

開発方針と注意事項をREADMEに明記する

開発方針

許可のとれた身内での大会戦・友人戦に麻雀AIを参加させる等を想定している
段位戦参加機能は開発しない(プルリクが来てもrejectする)

注意事項

利用して運営からBANされるリスクなどの責任はとれないことを明記する

TODO

  • 他のRPAライブラリでの記載事項の調査
  • 記載内容原文の作成
  • 記載内容の英訳
  • README.mdへの追記

イベントアイテム入手の際、「確認」が出ない状態で1回クリックが必要となる

概要

イベント期間中は対局後イベントアイテムを獲得することがある。
獲得後次の画面に進むためには画面をクリックする必要があるが、現在対応していないので対処する。

イベントアイテムを獲得したとき

  • 画面にはアイテムのアイコンのみ表示され、「確認」ボタンが表示されない。
  • 画面をクリックしない限り次の画面に進まない。クリック位置は画面のどこでもよい。
  • 一回画面をクリックをすれば「確認」ボタンが表示される。

(2024/01/31アップデート後のイベントで確認)
match_event_item

As Is

イベント期間中の対局後にイベントアイテムを獲得したとき、
アイテムを獲得した後の画面から次の画面(友人戦部屋など)に遷移しない。

To Be

イベント期間中の対局後にイベントアイテムを獲得した場合も正常に次の画面に遷移する。

ruffのnoqaを設定する

概要

コードにnoqaが散らばっているので設定頻度が多いものに関しては
pyproject.tomlに一括で設定する

・C901 → リファクタリング箇所の参考にするため一旦保留とする
・PLRの一部
init.pyのF401 → 代わりに__all__を設定する

友人戦ホストの検出に失敗することがある

概要

友人戦部屋を作成し部屋に入った際、画面の検出に失敗する
画面解像度が小さいほど失敗率が上がる傾向がある

原因

部屋に入ってから「装飾品変更」、「開始」ボタンが表示されるまでにラグがあるため、画面の検出に使用している「開始」ボタンが表示される前に検出処理が実行されるため
2024-07-14-14-54-17-PresentationNotDetectedError
2024-07-14-14-49-27-PresentationNotDetectedError

毎朝6時をまたぐ試合がエラーとなる

毎朝6時にはクエスト更新やショップの更新などの通知メッセージがあります。そのため試合が6時をまたがって行われた場合、以下のメッセージを受け取ります。

  • ('inbound', '.lq.NotifyActivityTaskUpdate', {'progresses': ...})
  • ('outbound', '.lq.Lobby.fetchShopInterval', ...)

MatchPresentation では対応できていないためエラーとなります。
_on_end_of_match() などのメッセージを dequeue する処理において、上述したメッセージを dequeue してもエラーが発生しないよう修正する必要があります。

大会戦参加機能の実装

概要

大会戦に参加することができるようにする

進捗

  • テンプレート画像用のスクショを集める
  • ホーム画面から大会ロビーに移動する処理の実装
  • 大会ロビーから大会IDで検索する機能の実装
  • 大会名で検索する機能が必要か検討する → 実装しない方針に決定(#96 (comment) 参照)
  • 大会部屋で準備完了を押す機能の実装
  • 大会から対局に入ったときのWSメッセージをMatchPresentationで読み捨てる処理の実装
  • 大会の期間内か確認する処理が可能か調査
  • 対局から大会部屋に戻る処理の実装
  • 大会部屋からホーム画面に戻る処理の実装

リモートブラウザを起動しようとすると `net::ERR_PROXY_CONNECTION_FAILED` 例外が送出される場合がある

概要

リモートブラウザを起動しようとすると net::ERR_PROXY_CONNECTION_FAILED 例外が送出される場合がある.

再現手順

リモートブラウザの起動を試みる.ただし,タイミングに関するバグと推測されるため,必ずしも再現されるとは限らない.

原因

リモートブラウザの起動に関する実装において, sniffer プロセスの起動直後にブラウザを起動している.

sniffer_process = Popen(sniffer_args) # noqa: S603
try:
initial_position = f"--window-position={initial_left},{initial_top}"
proxy_server = f"--proxy-server=http://localhost:{proxy_port}"
ignore_certifi_errors = "--ignore-certificate-errors"
options = [initial_position, proxy_server, ignore_certifi_errors]
viewport_size = {"width": viewport_width, "height": viewport_height}
mute_audio_off = None if headless else ["--mute-audio"]
with (
sync_playwright() as playwright,
playwright.chromium.launch(
args=options,
ignore_default_args=mute_audio_off,
headless=headless,
) as browser,

この結果, sniffer プロセスがプロキシポートの listen を開始する前にブラウザがプロキシポートに接続しようとする可能性がある.この場合,プロキシポートがまだ開いていないため,ブラウザがプロキシに接続できず net::ERR_PROXY_CONNECTION_FAILED 例外を送出するものと推測される.

As Is

以下の例外が送出される.

Traceback (most recent call last):
  File "/root/majsoulrpa/.venv/bin/majsoulrpa_remote_browser", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/root/majsoulrpa/.venv/lib/python3.12/site-packages/majsoulrpa/remote_browser/_remote_browser.py", line 252, in main
    launch_remote_browser(
  File "/root/majsoulrpa/.venv/lib/python3.12/site-packages/majsoulrpa/remote_browser/_remote_browser.py", line 226, in launch_remote_browser
    _launch_remote_browser_core(
  File "/root/majsoulrpa/.venv/lib/python3.12/site-packages/majsoulrpa/remote_browser/_remote_browser.py", line 75, in _launch_remote_browser_core
    page.goto(URL_MAJSOUL)
  File "/root/majsoulrpa/.venv/lib/python3.12/site-packages/playwright/sync_api/_generated.py", line 9312, in goto
    self._sync(
  File "/root/majsoulrpa/.venv/lib/python3.12/site-packages/playwright/_impl/_sync_base.py", line 115, in _sync
    return task.result()
           ^^^^^^^^^^^^^
  File "/root/majsoulrpa/.venv/lib/python3.12/site-packages/playwright/_impl/_page.py", line 475, in goto
    return await self._main_frame.goto(**locals_to_params(locals()))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/majsoulrpa/.venv/lib/python3.12/site-packages/playwright/_impl/_frame.py", line 139, in goto
    await self._channel.send("goto", locals_to_params(locals()))
  File "/root/majsoulrpa/.venv/lib/python3.12/site-packages/playwright/_impl/_connection.py", line 62, in send
    return await self._connection.wrap_api_call(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/majsoulrpa/.venv/lib/python3.12/site-packages/playwright/_impl/_connection.py", line 492, in wrap_api_call
    return await cb()
           ^^^^^^^^^^
  File "/root/majsoulrpa/.venv/lib/python3.12/site-packages/playwright/_impl/_connection.py", line 100, in inner_send
    result = next(iter(done)).result()
             ^^^^^^^^^^^^^^^^^^^^^^^^^
playwright._impl._errors.Error: net::ERR_PROXY_CONNECTION_FAILED at https://game.mahjongsoul.com/

To Be

リモートブラウザが正常に起動する.

`remote_browser`にも`user_data_dir`を指定できるようにする

概要

ローカルブラウザではuser_data_dirの指定によってプロファイルを保存でき、
2回目以降の起動ではメールアドレス経由の認証を省略できる。

リモートブラウザでも同じ動作ができるようにする

To Be

launch_remote_browser関数、majsoulrpa_remote_browserコマンドにuser_data_dirを指定可能にする

A new API found: `.lq.Lobby.fetchManagerCustomizedContestList`

概要

大会に入室した際、新しい API .lq.Lobby.fetchManagerCustomizedContestList がやり取りされた。
現在 majsoulrpa に同梱している liqi_pb2.py に入っていないため、 liqi_pb2.py の更新およびメッセージ処理の追加が必要である。

詳細

実行したスクリプト

examples\enter_tournament.py

エラーメッセージ

(前略)
INFO:majsoulrpa.presentation.home:('outbound', '.lq.Lobby.loginSuccess', {}, {}, datetime.datetime(2024, 7, 24, 9, 30, 10, 983251, tzinfo=datetime.timezone.utc))
INFO:majsoulrpa.presentation.home:('outbound', '.lq.Lobby.heatbeat', {'no_operation_counter': 0}, {'error': {'code': 0, 'u32_params': [], 'str_params': [], 'json_param': ''}}, datetime.datetime(2024, 7, 24, 9, 30, 11, 390945, tzinfo=datetime.timezone.utc))
INFO:majsoulrpa.presentation.home:('outbound', '.lq.Lobby.heatbeat', {'no_operation_counter': 0}, {'error': {'code': 0, 'u32_params': [], 'str_params': [], 'json_param': ''}}, datetime.datetime(2024, 7, 24, 9, 30, 12, 542528, tzinfo=datetime.timezone.utc))
INFO:majsoulrpa.presentation.home:('outbound', '.lq.Lobby.loginBeat', {'contract': 'DF2vkXCnfeXp4WoGSBGNcJBufZiMN3UP'}, {}, datetime.datetime(2024, 7, 24, 9, 30, 12, 793821, tzinfo=datetime.timezone.utc))
INFO:majsoulrpa.presentation.home:('outbound', '.lq.Lobby.loginBeat', {'contract': 'DF2vkXCnfeXp4WoGSBGNcJBufZiMN3UP'}, {}, datetime.datetime(2024, 7, 24, 9, 30, 12, 823415, tzinfo=datetime.timezone.utc))
INFO:majsoulrpa.presentation.home:('outbound', '.lq.Lobby.heatbeat', {'no_operation_counter': 0}, {'error': {'code': 0, 'u32_params': [], 'str_params': [], 'json_param': ''}}, datetime.datetime(2024, 7, 24, 9, 30, 16, 452128, tzinfo=datetime.timezone.utc))
Tournament ID: 000000
INFO:majsoulrpa.presentation.home:('outbound', '.lq.Lobby.heatbeat', {'no_operation_counter': 0}, {'error': {'code': 0, 'u32_params': [], 'str_params': [], 'json_param': ''}}, datetime.datetime(2024, 7, 24, 9, 30, 20, 460244, tzinfo=datetime.timezone.utc))
Traceback (most recent call last):
  File "C:\Users\K_K\Documents\Program\mahjong\majsoulrpa\src\majsoulrpa\_impl\zmq_client.py", line 124, in jsonize
    parser = self._message_type_map[name][0]()
             ~~~~~~~~~~~~~~~~~~~~~~^^^^^^
KeyError: '.lq.Lobby.fetchManagerCustomizedContestList'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\Users\xxx\Documents\Program\mahjong\majsoulrpa\examples\enter_tournament.py", line 61, in <module>
    presentation.enter_tournament(tournament_id)
  File "C:\Users\xxx\Documents\Program\mahjong\majsoulrpa\src\majsoulrpa\presentation\home.py", line 504, in enter_tournament
    message = self._message_queue_client.dequeue_message(1)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\xxx\Documents\Program\mahjong\majsoulrpa\src\majsoulrpa\_impl\zmq_client.py", line 147, in dequeue_message
    jsonized_request = jsonize(name, request_data, is_response=False)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\xxx\Documents\Program\mahjong\majsoulrpa\src\majsoulrpa\_impl\zmq_client.py", line 137, in jsonize
    raise RuntimeError(msg) from ke
RuntimeError: A new API found:
  name: .lq.Lobby.fetchManagerCustomizedContestList
Raw data was saved to 2024-07-24-09-30-28-.lq.Lobby.fetchManagerCustomizedContestList.bin.
Please cooperate by providing data. Thank you for your cooperation.

生データ

バイナリのため Hex Editor で表示した内容を添付する

0A 02 6A 70

RPA と Presentation*** の循環参照を解消する

概要

RPA と Presentation が循環参照になっている
設計の観点からも RPA.wait() は本来 presentation モジュール側のクラスのメソッドかトップレベルの関数になると思われる

As Is

Presentation*** の import を RPA.wait() メソッド内まで遅延させて循環インポートを回避している

To Be

RPA.wait() に相当するトップレベルの関数もしくは別クラスのメソッドを作成する
RPA.wait() を削除する (しばらくは非推奨の警告を出しつつ残す)

認証コードの入力エラーを処理する

概要

認証コードの入力が間違っていても
入力が正しいときと同様のボタンの表示待ち・タイムアウトが発生する。

再現手順

  1. メールアドレスを入力して認証コードが入力できるようにする。
  2. AuthPresentation.enter_auth_code()に誤った認証コードを渡す
  3. 「認証失敗。後ほど再度お試しください」と「確認」ボタンが表示される
    (4. 「確認」をクリックするとログイン画面になる)
    (5. 「ログイン」ボタンをクリックするとメールアドレスは入力済みである)

To Be

認証コードの形式(半角数字6桁)が誤っていた場合は画面に入力せずにValueErrorを投げる
認証コードが誤っていた場合「確認」をクリック後「ログイン」をクリックして認証画面に戻ってからValueErrorを投げる

`user_data_dir` in the `config.toml` saved the data at wrong location on Windows

Tested on Windows 11, Python 3.11.

When user_data_dir is set to a value in the config.toml, the data is saved at wrong location.

Example of config.toml:

[browser]
user_data_dir = "./user_data"                # User data directory (comment out this line to use incognito mode).

Expected:
A dir called user_data appear at the execute location and the data is in there.

What Happened:
A dir called user_data appear at the execute location but data is not saved there.
Instead, it saved "./user_data" to <location of playwright installed>/user_data

protocへの依存を削除する

概要

protocはZMQClientが未知のAPIを受け取ったとき、エラーメッセージにデコードしたAPIを表示するためだけに必要である
未知のAPIに対応するには新しいliqi.jsonからliqi_pb2.pyを作成して差し替えるしかないため、
ユーザーにとってはエラーメッセージを表示する必要性は薄い
稀な事象でのスクショと同様に、バイナリ(bytes)の生データをそのままバイナリファイルに保存するだけでいいと思われる
その場合、ユーザーがインストールするソフトウェアを減らすことができる

As Is

ZMQClientが未知のAPIを受け取ったとき、
protocを使用してWebSocketメッセージをデコードし、その結果をエラーメッセージとして表示する

そのためにユーザーはprotocのインストールが必要となる

To Be

ZMQClientが未知のAPIを受け取ったとき、
WebSocketメッセージのバイナリをそのままファイルに保存する

ユーザーはprotocをインストールする必要がない

リモートブラウザがないときRPA.wait()が停止する

概要

リモートモードでRPAの接続先のリモートブラウザがないとき固まる
・タイムアウトを過ぎても処理が終了しない
・Ctrl+cが効かない
Loginの__init__内で止まっている

再現手順

majsoulrpa_remote_browserが起動していない状態で
リモートモードのRPAを起動し、RPA.wait()を実行する

To Be

リモートモードでRPAの接続先のリモートブラウザがないときも
・タイムアウトを過ぎるとTimeoutErrorを投げる
・Ctrl+cを入力するとKeyboardinterruptを投げる

EN版・CN版対応

概要

EN版、CN版に対応する

各国語版の違い

  • URL
  • 認証メールのアドレス
  • 画面の文字
  • (CN版のみ)ログインに認証コードではなくパスワードが必要
  • 表示言語の選択部が存在する

As Is

JP版のみ対応している

To Be

JP版、CN版(中文(简体)、中文(繫體))、EN版(English、中文(繁體)、한국어)に対応する

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.