From a6bdbe9bb24cfec862ac15dd8118e23a342ca061 Mon Sep 17 00:00:00 2001 From: ekkx Date: Wed, 28 Aug 2024 17:36:21 +0900 Subject: [PATCH 01/22] chore: update makefile --- Makefile | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index ab72f06..e4101c8 100644 --- a/Makefile +++ b/Makefile @@ -8,27 +8,16 @@ up: # 実行環境の構築&起動 down: # コンテナ停止 docker compose down -shell: +shell: # コンテナのシェルを起動 docker compose exec yaylib bash -# Distribution -.PHONY: build clean publish +# テスト +.PHONY: test -build: # パッケージのビルド - python setup.py sdist - python setup.py bdist_wheel +test: + poetry run python -m unittest discover -s tests -p "test_*.py" -clean: # ビルドファイル削除 - rm -rf build/ - rm -rf dist/ - rm -rf yaylib.egg-info/ - -publish: # PYPIにパッケージのアップロード - make build - twine upload --repository pypi dist/* - make clean - -# Documentation +# ドキュメント .PHONY: doc clean-doc doc: # ドキュメントの生成 From 7cc71d16a7bfc20d8e16e56bdae2c204d2efa3f1 Mon Sep 17 00:00:00 2001 From: ekkx Date: Wed, 28 Aug 2024 17:36:35 +0900 Subject: [PATCH 02/22] chore: add unit test ci --- .github/workflows/docs.yaml | 4 +++- .github/workflows/tests.yaml | 42 ++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/tests.yaml diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index a7c5c91..0d58bec 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -41,7 +41,9 @@ jobs: poetry config --list - name: Install dependencies - run: poetry install + run: | + poetry install + ./poetry_plugins.sh - name: Build Sphinx run: | diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml new file mode 100644 index 0000000..0d5a1b6 --- /dev/null +++ b/.github/workflows/tests.yaml @@ -0,0 +1,42 @@ +name: Run unit tests + +on: + push: + branches: + - develop + pull_request: + branches: + - develop + types: + - opened + - ready_for_review + - synchronize + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Poetry + run: pipx install poetry + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Check poetry + run: | + poetry --version + poetry config --list + + - name: Install dependencies + run: | + poetry install + ./poetry_plugins.sh + + - name: Run unit tests + run: | + poetry run python -m unittest discover -s tests -p "test_*.py" From bf739f0a5f404d4c32754e5eb244c85e13cbd207 Mon Sep 17 00:00:00 2001 From: ekkx Date: Wed, 28 Aug 2024 17:39:29 +0900 Subject: [PATCH 03/22] fix: ImportError: cannot import name 'Self' from 'typing' on device.py --- yaylib/device.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yaylib/device.py b/yaylib/device.py index 575e4d2..578ee18 100644 --- a/yaylib/device.py +++ b/yaylib/device.py @@ -23,7 +23,7 @@ """ import random -from typing import Dict, Optional, Self +from typing import Optional from . import config @@ -56,7 +56,7 @@ def __init__( self.model = model @classmethod - def create(cls, device: Optional[Dict] = None) -> Self: + def create(cls, device: Optional[dict] = None): """端末を生成する""" if device is None: device = random.choice(DEVICES) From f0f61a670b4f612c3d8caecbc78e07dd0b247507 Mon Sep 17 00:00:00 2001 From: ekkx Date: Wed, 28 Aug 2024 17:44:37 +0900 Subject: [PATCH 04/22] fix(tests): sqlite3.OperationalError: unable to open database file --- tests/test_storage.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/test_storage.py b/tests/test_storage.py index 1fcb0b3..b134a56 100644 --- a/tests/test_storage.py +++ b/tests/test_storage.py @@ -1,10 +1,10 @@ import os import unittest -from yaylib.state import Storage, LocalUser +from yaylib.state import LocalUser, Storage - -db_path = os.path.dirname(os.path.dirname(__file__)) + "/.config/test.db" +base_path = os.path.dirname(os.path.dirname(__file__)) + "/.config/tests/" +db_filename = base_path + "test.db" test_user = LocalUser( @@ -19,15 +19,17 @@ class TestStorage(unittest.TestCase): def setUp(self): self.clean() - self.storage = Storage(db_path) + if not os.path.exists(base_path): + os.makedirs(base_path) + self.storage = Storage(db_filename) def tearDown(self): self.clean() @staticmethod def clean(): - if os.path.isfile(db_path): - os.remove(db_path) + if os.path.isfile(db_filename): + os.remove(db_filename) def test_get_user(self): result = self.storage.create_user(test_user) From 86767a73915d4086b40a1bee79fd5565a81cf450 Mon Sep 17 00:00:00 2001 From: ekkx Date: Wed, 28 Aug 2024 17:50:05 +0900 Subject: [PATCH 05/22] chore: update makefile --- .github/workflows/docs.yaml | 2 +- .github/workflows/tests.yaml | 2 +- Makefile | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 0d58bec..20e1c25 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -47,7 +47,7 @@ jobs: - name: Build Sphinx run: | - poetry run make doc + make doc - name: Setup Pages uses: actions/configure-pages@v3 diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 0d5a1b6..70b20b6 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -39,4 +39,4 @@ jobs: - name: Run unit tests run: | - poetry run python -m unittest discover -s tests -p "test_*.py" + make test diff --git a/Makefile b/Makefile index e4101c8..935f3b1 100644 --- a/Makefile +++ b/Makefile @@ -22,8 +22,8 @@ test: doc: # ドキュメントの生成 make clean-doc - sphinx-apidoc -f -e -o ./docs . tests/* *_test.py setup.py - sphinx-build -M html ./docs ./docs/_build + poetry run sphinx-apidoc -f -e -o ./docs . tests/* *_test.py setup.py + poetry run sphinx-build -M html ./docs ./docs/_build clean-doc: # temp rm -rf docs/yaylib.*rst docs/modules.rst docs/_build From ebbfeb57aea93541ddd2a59be23ad11addeb2cec Mon Sep 17 00:00:00 2001 From: ekkx Date: Thu, 29 Aug 2024 14:45:39 +0900 Subject: [PATCH 06/22] chore: update doc makefile --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index 935f3b1..fc8aa78 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,6 @@ test: doc: # ドキュメントの生成 make clean-doc - poetry run sphinx-apidoc -f -e -o ./docs . tests/* *_test.py setup.py poetry run sphinx-build -M html ./docs ./docs/_build clean-doc: # temp From 71d669640d262ad218003c045870e3606a78096e Mon Sep 17 00:00:00 2001 From: ekkx Date: Thu, 29 Aug 2024 14:46:40 +0900 Subject: [PATCH 07/22] doc: remove auto generated rst files --- docs/modules.rst | 7 ------- docs/yaylib.api.auth.rst | 7 ------- docs/yaylib.api.call.rst | 7 ------- docs/yaylib.api.chat.rst | 7 ------- docs/yaylib.api.group.rst | 7 ------- docs/yaylib.api.misc.rst | 7 ------- docs/yaylib.api.notification.rst | 7 ------- docs/yaylib.api.post.rst | 7 ------- docs/yaylib.api.review.rst | 7 ------- docs/yaylib.api.rst | 27 ------------------------ docs/yaylib.api.thread.rst | 7 ------- docs/yaylib.api.user.rst | 7 ------- docs/yaylib.client.rst | 7 ------- docs/yaylib.config.rst | 7 ------- docs/yaylib.constants.rst | 7 ------- docs/yaylib.device.rst | 7 ------- docs/yaylib.errors.rst | 7 ------- docs/yaylib.models.rst | 7 ------- docs/yaylib.responses.rst | 7 ------- docs/yaylib.rst | 35 -------------------------------- docs/yaylib.state.rst | 7 ------- docs/yaylib.utils.rst | 7 ------- docs/yaylib.ws.rst | 7 ------- 23 files changed, 209 deletions(-) delete mode 100644 docs/modules.rst delete mode 100644 docs/yaylib.api.auth.rst delete mode 100644 docs/yaylib.api.call.rst delete mode 100644 docs/yaylib.api.chat.rst delete mode 100644 docs/yaylib.api.group.rst delete mode 100644 docs/yaylib.api.misc.rst delete mode 100644 docs/yaylib.api.notification.rst delete mode 100644 docs/yaylib.api.post.rst delete mode 100644 docs/yaylib.api.review.rst delete mode 100644 docs/yaylib.api.rst delete mode 100644 docs/yaylib.api.thread.rst delete mode 100644 docs/yaylib.api.user.rst delete mode 100644 docs/yaylib.client.rst delete mode 100644 docs/yaylib.config.rst delete mode 100644 docs/yaylib.constants.rst delete mode 100644 docs/yaylib.device.rst delete mode 100644 docs/yaylib.errors.rst delete mode 100644 docs/yaylib.models.rst delete mode 100644 docs/yaylib.responses.rst delete mode 100644 docs/yaylib.rst delete mode 100644 docs/yaylib.state.rst delete mode 100644 docs/yaylib.utils.rst delete mode 100644 docs/yaylib.ws.rst diff --git a/docs/modules.rst b/docs/modules.rst deleted file mode 100644 index ed54a72..0000000 --- a/docs/modules.rst +++ /dev/null @@ -1,7 +0,0 @@ -yaylib -====== - -.. toctree:: - :maxdepth: 4 - - yaylib diff --git a/docs/yaylib.api.auth.rst b/docs/yaylib.api.auth.rst deleted file mode 100644 index 0cfb912..0000000 --- a/docs/yaylib.api.auth.rst +++ /dev/null @@ -1,7 +0,0 @@ -yaylib.api.auth module -====================== - -.. automodule:: yaylib.api.auth - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/yaylib.api.call.rst b/docs/yaylib.api.call.rst deleted file mode 100644 index 95934ec..0000000 --- a/docs/yaylib.api.call.rst +++ /dev/null @@ -1,7 +0,0 @@ -yaylib.api.call module -====================== - -.. automodule:: yaylib.api.call - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/yaylib.api.chat.rst b/docs/yaylib.api.chat.rst deleted file mode 100644 index df8baf4..0000000 --- a/docs/yaylib.api.chat.rst +++ /dev/null @@ -1,7 +0,0 @@ -yaylib.api.chat module -====================== - -.. automodule:: yaylib.api.chat - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/yaylib.api.group.rst b/docs/yaylib.api.group.rst deleted file mode 100644 index a98e7a4..0000000 --- a/docs/yaylib.api.group.rst +++ /dev/null @@ -1,7 +0,0 @@ -yaylib.api.group module -======================= - -.. automodule:: yaylib.api.group - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/yaylib.api.misc.rst b/docs/yaylib.api.misc.rst deleted file mode 100644 index fe04885..0000000 --- a/docs/yaylib.api.misc.rst +++ /dev/null @@ -1,7 +0,0 @@ -yaylib.api.misc module -====================== - -.. automodule:: yaylib.api.misc - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/yaylib.api.notification.rst b/docs/yaylib.api.notification.rst deleted file mode 100644 index 8c5c6d0..0000000 --- a/docs/yaylib.api.notification.rst +++ /dev/null @@ -1,7 +0,0 @@ -yaylib.api.notification module -============================== - -.. automodule:: yaylib.api.notification - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/yaylib.api.post.rst b/docs/yaylib.api.post.rst deleted file mode 100644 index 9e9446a..0000000 --- a/docs/yaylib.api.post.rst +++ /dev/null @@ -1,7 +0,0 @@ -yaylib.api.post module -====================== - -.. automodule:: yaylib.api.post - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/yaylib.api.review.rst b/docs/yaylib.api.review.rst deleted file mode 100644 index 626ae48..0000000 --- a/docs/yaylib.api.review.rst +++ /dev/null @@ -1,7 +0,0 @@ -yaylib.api.review module -======================== - -.. automodule:: yaylib.api.review - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/yaylib.api.rst b/docs/yaylib.api.rst deleted file mode 100644 index 1cdf6a9..0000000 --- a/docs/yaylib.api.rst +++ /dev/null @@ -1,27 +0,0 @@ -yaylib.api package -================== - -Submodules ----------- - -.. toctree:: - :maxdepth: 4 - - yaylib.api.auth - yaylib.api.call - yaylib.api.chat - yaylib.api.group - yaylib.api.misc - yaylib.api.notification - yaylib.api.post - yaylib.api.review - yaylib.api.thread - yaylib.api.user - -Module contents ---------------- - -.. automodule:: yaylib.api - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/yaylib.api.thread.rst b/docs/yaylib.api.thread.rst deleted file mode 100644 index b30f339..0000000 --- a/docs/yaylib.api.thread.rst +++ /dev/null @@ -1,7 +0,0 @@ -yaylib.api.thread module -======================== - -.. automodule:: yaylib.api.thread - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/yaylib.api.user.rst b/docs/yaylib.api.user.rst deleted file mode 100644 index a95ab44..0000000 --- a/docs/yaylib.api.user.rst +++ /dev/null @@ -1,7 +0,0 @@ -yaylib.api.user module -====================== - -.. automodule:: yaylib.api.user - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/yaylib.client.rst b/docs/yaylib.client.rst deleted file mode 100644 index ba4304a..0000000 --- a/docs/yaylib.client.rst +++ /dev/null @@ -1,7 +0,0 @@ -yaylib.client module -==================== - -.. automodule:: yaylib.client - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/yaylib.config.rst b/docs/yaylib.config.rst deleted file mode 100644 index 36a62df..0000000 --- a/docs/yaylib.config.rst +++ /dev/null @@ -1,7 +0,0 @@ -yaylib.config module -==================== - -.. automodule:: yaylib.config - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/yaylib.constants.rst b/docs/yaylib.constants.rst deleted file mode 100644 index 71f7109..0000000 --- a/docs/yaylib.constants.rst +++ /dev/null @@ -1,7 +0,0 @@ -yaylib.constants module -======================= - -.. automodule:: yaylib.constants - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/yaylib.device.rst b/docs/yaylib.device.rst deleted file mode 100644 index 8d09527..0000000 --- a/docs/yaylib.device.rst +++ /dev/null @@ -1,7 +0,0 @@ -yaylib.device module -==================== - -.. automodule:: yaylib.device - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/yaylib.errors.rst b/docs/yaylib.errors.rst deleted file mode 100644 index fb63ddc..0000000 --- a/docs/yaylib.errors.rst +++ /dev/null @@ -1,7 +0,0 @@ -yaylib.errors module -==================== - -.. automodule:: yaylib.errors - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/yaylib.models.rst b/docs/yaylib.models.rst deleted file mode 100644 index 8dd3749..0000000 --- a/docs/yaylib.models.rst +++ /dev/null @@ -1,7 +0,0 @@ -yaylib.models module -==================== - -.. automodule:: yaylib.models - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/yaylib.responses.rst b/docs/yaylib.responses.rst deleted file mode 100644 index 2698a79..0000000 --- a/docs/yaylib.responses.rst +++ /dev/null @@ -1,7 +0,0 @@ -yaylib.responses module -======================= - -.. automodule:: yaylib.responses - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/yaylib.rst b/docs/yaylib.rst deleted file mode 100644 index 135cae7..0000000 --- a/docs/yaylib.rst +++ /dev/null @@ -1,35 +0,0 @@ -yaylib package -============== - -Subpackages ------------ - -.. toctree:: - :maxdepth: 4 - - yaylib.api - -Submodules ----------- - -.. toctree:: - :maxdepth: 4 - - yaylib.client - yaylib.config - yaylib.constants - yaylib.device - yaylib.errors - yaylib.models - yaylib.responses - yaylib.state - yaylib.utils - yaylib.ws - -Module contents ---------------- - -.. automodule:: yaylib - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/yaylib.state.rst b/docs/yaylib.state.rst deleted file mode 100644 index db6d4ed..0000000 --- a/docs/yaylib.state.rst +++ /dev/null @@ -1,7 +0,0 @@ -yaylib.state module -=================== - -.. automodule:: yaylib.state - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/yaylib.utils.rst b/docs/yaylib.utils.rst deleted file mode 100644 index 4cdd807..0000000 --- a/docs/yaylib.utils.rst +++ /dev/null @@ -1,7 +0,0 @@ -yaylib.utils module -=================== - -.. automodule:: yaylib.utils - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/yaylib.ws.rst b/docs/yaylib.ws.rst deleted file mode 100644 index 8eb26d3..0000000 --- a/docs/yaylib.ws.rst +++ /dev/null @@ -1,7 +0,0 @@ -yaylib.ws module -================ - -.. automodule:: yaylib.ws - :members: - :undoc-members: - :show-inheritance: From 96058119c24dbeac1f3091a5c20c6a888bd73b73 Mon Sep 17 00:00:00 2001 From: ekkx Date: Sat, 31 Aug 2024 13:02:48 +0900 Subject: [PATCH 08/22] doc: add api --- docs/api/auth.rst | 15 +++++ docs/api/call.rst | 15 +++++ docs/api/chat.rst | 15 +++++ docs/api/errors.rst | 18 ++++++ docs/api/group.rst | 15 +++++ docs/api/index.rst | 119 ++++++++++++++++++++++++++++++++++++++ docs/api/misc.rst | 15 +++++ docs/api/notification.rst | 15 +++++ docs/api/post.rst | 15 +++++ docs/api/review.rst | 15 +++++ docs/api/thread.rst | 15 +++++ docs/api/user.rst | 15 +++++ 12 files changed, 287 insertions(+) create mode 100644 docs/api/auth.rst create mode 100644 docs/api/call.rst create mode 100644 docs/api/chat.rst create mode 100644 docs/api/errors.rst create mode 100644 docs/api/group.rst create mode 100644 docs/api/index.rst create mode 100644 docs/api/misc.rst create mode 100644 docs/api/notification.rst create mode 100644 docs/api/post.rst create mode 100644 docs/api/review.rst create mode 100644 docs/api/thread.rst create mode 100644 docs/api/user.rst diff --git a/docs/api/auth.rst b/docs/api/auth.rst new file mode 100644 index 0000000..5ff9f1d --- /dev/null +++ b/docs/api/auth.rst @@ -0,0 +1,15 @@ +:description: 説明 + +認証 +===== + +.. rst-class:: lead + + 説明 + +---- + +説明 + +.. autoclass:: yaylib.api.AuthApi + :members: diff --git a/docs/api/call.rst b/docs/api/call.rst new file mode 100644 index 0000000..790959a --- /dev/null +++ b/docs/api/call.rst @@ -0,0 +1,15 @@ +:description: 説明 + +通話 +===== + +.. rst-class:: lead + + 説明 + +---- + +説明 + +.. autoclass:: yaylib.api.CallApi + :members: diff --git a/docs/api/chat.rst b/docs/api/chat.rst new file mode 100644 index 0000000..511d1a4 --- /dev/null +++ b/docs/api/chat.rst @@ -0,0 +1,15 @@ +:description: 説明 + +チャット +======== + +.. rst-class:: lead + + 説明 + +---- + +説明 + +.. autoclass:: yaylib.api.ChatApi + :members: diff --git a/docs/api/errors.rst b/docs/api/errors.rst new file mode 100644 index 0000000..7a0e785 --- /dev/null +++ b/docs/api/errors.rst @@ -0,0 +1,18 @@ +:description: 説明 + +例外クラス +========== + +.. rst-class:: lead + + 説明 + +---- + +説明 + +.. automodule:: yaylib.errors + :members: + :undoc-members: + :show-inheritance: + :exclude-members: raise_for_code, raise_for_status diff --git a/docs/api/group.rst b/docs/api/group.rst new file mode 100644 index 0000000..83dced7 --- /dev/null +++ b/docs/api/group.rst @@ -0,0 +1,15 @@ +:description: 説明 + +サークル +======== + +.. rst-class:: lead + + 説明 + +---- + +説明 + +.. autoclass:: yaylib.api.GroupApi + :members: diff --git a/docs/api/index.rst b/docs/api/index.rst new file mode 100644 index 0000000..87ebb99 --- /dev/null +++ b/docs/api/index.rst @@ -0,0 +1,119 @@ +:description: 好きでつながるバーチャルワールド - Yay!(イェイ)API ライブラリ 📚 + +API +====== + +.. rst-class:: lead + + 好きでつながるバーチャルワールド - Yay!(イェイ)API ライブラリ 📚 + +---- + +**yaylib** で利用可能な API 関数一覧を紹介します。 + +API とは Yay! のサーバーにアクセスするための入り口となるインターフェースのことです。yaylib +では、その API に Python 関数を通して簡単にアクセスすることができます。 + +API 関数にアクセスするには、以下のように ``yaylib.Client`` クラスを使用します。 + +.. code-block:: python + :caption: main.py + + import yaylib + + client = yaylib.Client() + + # タイムラインを取得する API にアクセス + client.get_timeline() + +また、``yaylib.Client.[APIの種類]`` のようにアクセスを行うことで非同期処理が可能です。 + +.. code-block:: python + :caption: main.py + + import yaylib + + client = yaylib.Client() + + # API の種類を指定してタイムラインを取得(非同期) + client.post.get_timeline() + +非同期処理については `こちらを参照 <../async.html>`_ してください。 + + +API 関数 +--------- + +種類別に分類した yaylib で利用可能な API 関数一覧です。 + +新しく追加してほしい機能などがありましたら、 `GitHub `_ や +`Discord `_ にてお伝えください。 + +.. grid:: 1 1 1 2 + :gutter: 2 + :padding: 0 + + .. grid-item-card:: :octicon:`shield-lock` 認証 + :link: ./auth.html + + .. grid-item-card:: :octicon:`person` ユーザー + :link: ./user.html + + .. grid-item-card:: :octicon:`note` 投稿 + :link: ./post.html + + .. grid-item-card:: :octicon:`list-unordered` スレッド + :link: ./thread.html + + .. grid-item-card:: :octicon:`bell` 通知 + :link: ./notification.html + + .. grid-item-card:: :octicon:`discussion-closed` チャット + :link: ./chat.html + + .. grid-item-card:: :octicon:`read` レター + :link: ./review.html + + .. grid-item-card:: :octicon:`apps` サークル + :link: ./group.html + + .. grid-item-card:: :octicon:`unmute` 通話 + :link: ./call.html + + .. grid-item-card:: :octicon:`book` その他 + :link: ./misc.html + +例外クラス +---------- + +yaylib では、実行中に発生しうる例外(エラー)を細かく定義しています。これにより利用者は特定の状況に応じて細かく処理を分岐することが可能です。 + +.. grid:: 1 + :gutter: 2 + :padding: 0 + + .. grid-item-card:: クライアント例外 + :link: ./errors.html + + ``ClientError`` を継承するサーバーレスポンスの ``error_code`` に基づいたエラークラスです。 + + .. grid-item-card:: HTTP 例外 + :link: ./errors.html + + ``HTTPError`` を継承するクライアント例外に該当しない、HTTP 通信上のエラークラスです。 + + +.. toctree:: + :hidden: + + auth + user + post + thread + notification + chat + review + group + call + misc + errors diff --git a/docs/api/misc.rst b/docs/api/misc.rst new file mode 100644 index 0000000..6da476d --- /dev/null +++ b/docs/api/misc.rst @@ -0,0 +1,15 @@ +:description: 説明 + +その他 +======= + +.. rst-class:: lead + + 説明 + +---- + +説明 + +.. autoclass:: yaylib.api.MiscApi + :members: diff --git a/docs/api/notification.rst b/docs/api/notification.rst new file mode 100644 index 0000000..0b447c7 --- /dev/null +++ b/docs/api/notification.rst @@ -0,0 +1,15 @@ +:description: 説明 + +通知 +===== + +.. rst-class:: lead + + 説明 + +---- + +説明 + +.. autoclass:: yaylib.api.NotificationApi + :members: diff --git a/docs/api/post.rst b/docs/api/post.rst new file mode 100644 index 0000000..35c05b7 --- /dev/null +++ b/docs/api/post.rst @@ -0,0 +1,15 @@ +:description: 説明 + +投稿 +===== + +.. rst-class:: lead + + 説明 + +---- + +説明 + +.. autoclass:: yaylib.api.PostApi + :members: diff --git a/docs/api/review.rst b/docs/api/review.rst new file mode 100644 index 0000000..1baa52a --- /dev/null +++ b/docs/api/review.rst @@ -0,0 +1,15 @@ +:description: 説明 + +レター +======= + +.. rst-class:: lead + + 説明 + +---- + +説明 + +.. autoclass:: yaylib.api.ReviewApi + :members: diff --git a/docs/api/thread.rst b/docs/api/thread.rst new file mode 100644 index 0000000..f889889 --- /dev/null +++ b/docs/api/thread.rst @@ -0,0 +1,15 @@ +:description: 説明 + +スレッド +======== + +.. rst-class:: lead + + 説明 + +---- + +説明 + +.. autoclass:: yaylib.api.ThreadApi + :members: diff --git a/docs/api/user.rst b/docs/api/user.rst new file mode 100644 index 0000000..adf9e2c --- /dev/null +++ b/docs/api/user.rst @@ -0,0 +1,15 @@ +:description: 説明 + +ユーザー +======== + +.. rst-class:: lead + + 説明 + +---- + +説明 + +.. autoclass:: yaylib.api.UserApi + :members: From 40af893a0342ceb5222a280825eb96d694ab6113 Mon Sep 17 00:00:00 2001 From: ekkx Date: Sat, 31 Aug 2024 13:08:54 +0900 Subject: [PATCH 09/22] doc: add models components --- docs/models/index.rst | 33 +++++++++++++++++++++++++++++++++ docs/models/models.rst | 17 +++++++++++++++++ docs/models/responses.rst | 17 +++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 docs/models/index.rst create mode 100644 docs/models/models.rst create mode 100644 docs/models/responses.rst diff --git a/docs/models/index.rst b/docs/models/index.rst new file mode 100644 index 0000000..84cce1c --- /dev/null +++ b/docs/models/index.rst @@ -0,0 +1,33 @@ +:description: 好きでつながるバーチャルワールド - Yay!(イェイ)API ライブラリ 📚 + +モデル一覧 +========== + +.. rst-class:: lead + + 好きでつながるバーチャルワールド - Yay!(イェイ)API ライブラリ 📚 + +---- + +**yaylib** では、サーバーからのレスポンスを Python オブジェクトとして扱えるようよう「モデル」を定義しています。これらのモデルは、レスポンス内のデータを明確化し操作を簡単にします。 + +.. grid:: 1 + :gutter: 2 + :padding: 0 + + .. grid-item-card:: :octicon:`package` 純粋なモデル + :link: ./models.html + + 単一のデータを表した純粋なモデルです。一つのモデルは複数のモデルを保持する場合があり、互いに参照し合うことがあります。 + + .. grid-item-card:: :octicon:`archive` レスポンスモデル + :link: ./responses.html + + 複数の純粋なモデルを統括するサーバーからのレスポンスモデルです。一つの処理に対して一つのレスポンスという関係性から、互いに参照し合うことはありません。 + + +.. toctree:: + :hidden: + + models + responses diff --git a/docs/models/models.rst b/docs/models/models.rst new file mode 100644 index 0000000..a0f3b48 --- /dev/null +++ b/docs/models/models.rst @@ -0,0 +1,17 @@ +:description: 説明 + +純粋なモデル +============ + +.. rst-class:: lead + + 説明 + +---- + +説明 + +.. automodule:: yaylib.models + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/models/responses.rst b/docs/models/responses.rst new file mode 100644 index 0000000..72cd3fb --- /dev/null +++ b/docs/models/responses.rst @@ -0,0 +1,17 @@ +:description: 説明 + +レスポンスモデル +================ + +.. rst-class:: lead + + 説明 + +---- + +説明 + +.. automodule:: yaylib.responses + :members: + :undoc-members: + :show-inheritance: From 9bea7ff57c0b128b9a8c23d93b314bc91e145943 Mon Sep 17 00:00:00 2001 From: ekkx Date: Sat, 31 Aug 2024 13:09:02 +0900 Subject: [PATCH 10/22] chore: update doc command --- Makefile | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Makefile b/Makefile index fc8aa78..e6b3c10 100644 --- a/Makefile +++ b/Makefile @@ -21,8 +21,4 @@ test: .PHONY: doc clean-doc doc: # ドキュメントの生成 - make clean-doc poetry run sphinx-build -M html ./docs ./docs/_build - -clean-doc: # temp - rm -rf docs/yaylib.*rst docs/modules.rst docs/_build From bf57f11b3077d25cbf4c5fea0d73ba125b6fddae Mon Sep 17 00:00:00 2001 From: ekkx Date: Sat, 31 Aug 2024 13:09:32 +0900 Subject: [PATCH 11/22] doc: small fix --- docs/async.rst | 2 +- docs/conf.py | 2 +- docs/development/index.rst | 12 ++++++++++++ docs/index.rst | 4 +++- 4 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 docs/development/index.rst diff --git a/docs/async.rst b/docs/async.rst index d957dc8..939fe6f 100644 --- a/docs/async.rst +++ b/docs/async.rst @@ -37,7 +37,7 @@ yaylib では、直接 ``yaylib.Client()`` のメソッドにアクセスする client.post.create_post('Hello with yaylib!') client.user.get_user(93) -.. warning:: +.. caution:: 上記の非同期コードは動作しません。 diff --git a/docs/conf.py b/docs/conf.py index a070d44..ca04155 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -39,7 +39,7 @@ # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output -language="ja" +language = "ja" html_theme = "shibuya" html_static_path = ["_static"] diff --git a/docs/development/index.rst b/docs/development/index.rst new file mode 100644 index 0000000..9ac7a74 --- /dev/null +++ b/docs/development/index.rst @@ -0,0 +1,12 @@ +:description: yaylib の開発に参加する方法を紹介します。 + +はじめに +======== + +.. rst-class:: lead + + ここでは yaylib 開発のルールについて解説します 💁‍♀️ + +---- + +こんにちは。 diff --git a/docs/index.rst b/docs/index.rst index 3c13666..49a9a40 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -55,9 +55,11 @@ yaylib install demo async + api/index + models/index .. toctree:: :caption: Development :hidden: - install + development/index From fdd9cb24934cfd64f3355069c1c92df6950fd906 Mon Sep 17 00:00:00 2001 From: ekkx Date: Sat, 31 Aug 2024 13:09:56 +0900 Subject: [PATCH 12/22] fix: delete unneeded reponse model --- yaylib/responses.py | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/yaylib/responses.py b/yaylib/responses.py index 2f415e3..2746e7e 100644 --- a/yaylib/responses.py +++ b/yaylib/responses.py @@ -1194,39 +1194,6 @@ def __repr__(self): return f"UserTimestampResponse(data={self.data})" -class ChannelResponse(Response): - __slots__ = ("identifier", "message", "type", "sid") - - def __init__(self, data: dict): - super().__init__(data) - - self.identifier = data.get("identifier") - - self.message = data.get("message") - if self.message is not None and isinstance(self.message, dict): - self.message = WebSocketMessageResponse(self.message) - - self.type = data.get("type") - self.sid = data.get("sid") - - def __repr__(self): - return f"ChannelResponse(data={self.data})" - - -class WebSocketMessageResponse(Response): - __slots__ = ("response", "data", "event") - - def __init__(self, data: dict): - super().__init__(data) - - self.response = data - self.data = data.get("data") - self.event = data.get("event") - - def __repr__(self): - return f"WebSocketMessageResponse(response={self.response})" - - class WebSocketTokenResponse(Response): __slots__ = "token" From d9edf48eae72343c2fe1a92df2a97c49c87c3ccd Mon Sep 17 00:00:00 2001 From: ekkx Date: Sat, 31 Aug 2024 13:19:20 +0900 Subject: [PATCH 13/22] feat: add type hint to models.py --- yaylib/models.py | 130 ++++++++++++++++++++++++----------------------- 1 file changed, 66 insertions(+), 64 deletions(-) diff --git a/yaylib/models.py b/yaylib/models.py index c7a0937..ac8b34a 100644 --- a/yaylib/models.py +++ b/yaylib/models.py @@ -23,7 +23,7 @@ """ import json -from typing import Optional +from typing import List, Optional class Model: @@ -55,23 +55,23 @@ def __init__(self, data: dict): self.created_at = data.get("created_at") self.type = data.get("type") - self.user = data.get("user") + self.user: User = data.get("user") if self.user is not None: self.user = User(self.user) - self.from_post = data.get("from_post") + self.from_post: Post = data.get("from_post") if self.from_post is not None: self.from_post = Post(self.from_post) - self.to_post = data.get("to_post") + self.to_post: Post = data.get("to_post") if self.to_post is not None: self.to_post = Post(self.to_post) - self.group = data.get("group") + self.group: Group = data.get("group") if self.group is not None: self.group = Group(self.group) - self.followers = data.get("followers") + self.followers: List[User] = data.get("followers") if self.followers is not None: self.followers = [User(follower) for follower in self.followers] @@ -79,11 +79,11 @@ def __init__(self, data: dict): self.from_post_ids = data.get("from_post_ids") self.vip_reward = data.get("vip_reward") - self.metadata = data.get("metadata") + self.metadata: Metadata = data.get("metadata") if self.metadata is not None: self.metadata = Metadata(self.metadata) - self.birthday_users = data.get("birthday_users") + self.birthday_users: List[User] = data.get("birthday_users") if self.birthday_users is not None: self.birthday_users = [ User(birthday_user) for birthday_user in self.birthday_users @@ -136,7 +136,7 @@ def __init__(self, data: dict): self.description = data.get("description") self.itunes_app_id = data.get("itunes_app_id") - self.settings = data.get("settings") + self.settings: ApplicationConfigSettings = data.get("settings") if self.settings is not None: self.settings = ApplicationConfigSettings(self.settings) @@ -302,7 +302,7 @@ class CallGiftHistory(Model): def __init__(self, data: dict): self.data = data - self.gifts_count = data.get("gifts_count") + self.gifts_count: List[GiftCount] = data.get("gifts_count") if self.gifts_count is not None: self.gifts_count = [ GiftCount(gifts_count) for gifts_count in self.gifts_count @@ -310,7 +310,7 @@ def __init__(self, data: dict): self.sent_at = data.get("sent_at") - self.sender = data.get("sender") + self.sender: User = data.get("sender") if self.sender is not None: self.sender = User(self.sender) @@ -341,20 +341,20 @@ def __init__(self, data: dict): self.unread_count = data.get("unread_count") self.updated_at = data.get("updated_at") - self.members = data.get("members") + self.members: List[User] = data.get("members") if self.members is not None: self.members = [User(member) for member in self.members] self.background = data.get("background") - self.last_message = data.get("last_message") + self.last_message: Message = data.get("last_message") if self.last_message is not None: self.last_message = Message(self.last_message) self.name = data.get("name") self.is_group = data.get("is_group") - self.owner = data.get("owner") + self.owner: User = data.get("owner") if self.owner is not None: self.owner = User(self.owner) @@ -388,7 +388,7 @@ class MessageEvent(Model): def __init__(self, data: dict): self.data = data - self.message = data.get("data") + self.message: Message = data.get("data") if self.message is not None: self.message = Message(self.message) @@ -413,7 +413,7 @@ def __init__(self, data: dict): self.icon_thumbnail = data.get("icon_thumbnail") self.id = data.get("id") - self.last_message = data.get("last_message") + self.last_message: Message = data.get("last_message") if self.last_message is not None: self.last_message = Message(self.last_message) @@ -536,18 +536,18 @@ def __init__(self, data: dict): self.call_type = data.get("call_type") self.joinable_by = data.get("joinable_by") - self.game = data.get("game") + self.game: Game = data.get("game") if self.game is not None: self.game = Game(self.game) - self.genre = data.get("genre") + self.genre: Genre = data.get("genre") if self.genre is not None: self.genre = Genre(self.genre) self.duration_seconds = data.get("duration_seconds") self.max_participants = data.get("max_participants") - self.conference_call_users = data.get("conference_call_users") + self.conference_call_users: List[User] = data.get("conference_call_users") if self.conference_call_users is not None: self.conference_call_users = [ User(conference_call_user) @@ -558,7 +558,9 @@ def __init__(self, data: dict): # if self.bump_params is not None: # self.bump_params = BumpParams(self.bump_params) - self.conference_call_user_roles = data.get("conference_call_user_roles") + self.conference_call_user_roles: List[ConferenceCallUserRole] = data.get( + "conference_call_user_roles" + ) if self.conference_call_user_roles is not None: self.conference_call_user_roles = [ ConferenceCallUserRole(conference_call_user_role) @@ -645,7 +647,7 @@ class Footprint(Model): def __init__(self, data: dict): self.data = data - self.user = data.get("user") + self.user: User = data.get("user") if self.user is not None: self.user = User(self.user) @@ -666,7 +668,7 @@ def __init__(self, data: dict): self.title = data.get("title") self.icon_url = data.get("icon_url") - self.platform_details = data.get("platform_details") + self.platform_details: PlatformDetails = data.get("platform_details") if self.platform_details is not None: self.platform_details = PlatformDetails(self.platform_details) @@ -711,7 +713,7 @@ def __init__(self, data: dict): self.name = data.get("name") self.language = data.get("language") - self.gifs = data.get("gifs") + self.gifs: List[GifImage] = data.get("gifs") if self.gifs is not None: self.gifs = [GifImage(gif) for gif in self.gifs] @@ -753,11 +755,11 @@ def __init__(self, data: dict): self.data = data self.transaction_at_seconds = data.get("transaction_at_seconds") - self.user = data.get("user") + self.user: User = data.get("user") if self.user is not None: self.user = User(self.user) - self.gifts = data.get("gifts") + self.gifts: List[ReceivedGift] = data.get("gifts") if self.gifts is not None: self.gifts = [ReceivedGift(gift) for gift in self.gifts] @@ -862,7 +864,7 @@ def __init__(self, data: dict): self.cover_image_thumbnail = data.get("cover_image_thumbnail") self.generation_groups_limit = data.get("generation_groups_limit") - self.owner = data.get("owner") + self.owner: User = data.get("owner") if self.owner is not None: self.owner = User(self.owner) @@ -917,7 +919,7 @@ class GroupGiftHistory(Model): def __init__(self, data: dict): self.data = data - self.gifts_count = data.get("gifts_count") + self.gifts_count: List[GiftCount] = data.get("gifts_count") if self.gifts_count is not None: self.gifts_count = [ GiftCount(gifts_count) for gifts_count in self.gifts_count @@ -925,7 +927,7 @@ def __init__(self, data: dict): self.received_date = data.get("received_date") - self.user = data.get("user") + self.user: User = data.get("user") if self.user is not None: self.user = User(self.user) @@ -946,7 +948,7 @@ class GroupUser(Model): def __init__(self, data: dict): self.data = data - self.user = data.get("user") + self.user: User = data.get("user") if self.user is not None: self.user = User(self.user) @@ -964,7 +966,7 @@ class HiddenRecommendedPost(Model): def __init__(self, data: dict): self.data = data - self.post = data.get("post") + self.post: Post = data.get("post") if self.post is not None: self.post = Post(self.post) @@ -1022,18 +1024,18 @@ def __init__(self, data: dict): self.attachment_read_count = data.get("attachment_read_count") self.attachment_thumbnail = data.get("attachment_thumbnail") - self.conference_call = data.get("conference_call") + self.conference_call: ConferenceCall = data.get("conference_call") if self.conference_call is not None: self.conference_call = ConferenceCall(self.conference_call) - self.parent = data.get("parent") + self.parent: ParentMessage = data.get("parent") if self.parent is not None: self.parent = ParentMessage(self.parent) self.created_at = data.get("created_at") self.font_size = data.get("font_size") - self.gif = data.get("gif") + self.gif: GifImage = data.get("gif") if self.gif is not None: self.gif = GifImage(self.gif) @@ -1043,7 +1045,7 @@ def __init__(self, data: dict): self.reacted = data.get("reacted") self.room_id = data.get("room_id") - self.sticker = data.get("sticker") + self.sticker: Sticker = data.get("sticker") if self.sticker is not None: self.sticker = Sticker(self.sticker) @@ -1093,7 +1095,7 @@ def __init__(self, data: dict): self.created_at = data.get("created_at") self.font_size = data.get("font_size") - self.gif = data.get("gif") + self.gif: GifImage = data.get("gif") if self.gif is not None: self.gif = GifImage(self.gif) @@ -1102,7 +1104,7 @@ def __init__(self, data: dict): self.reactions_count = data.get("reactions_count") self.room_id = data.get("room_id") - self.sticker = data.get("sticker") + self.sticker: Sticker = data.get("sticker") if self.sticker is not None: self.sticker = Sticker(self.sticker) @@ -1252,19 +1254,19 @@ def __init__(self, data: dict): self.in_reply_to_post = data.get("in_reply_to_post") self.in_reply_to_post_count = data.get("in_reply_to_post_count") - self.user = data.get("user") + self.user: User = data.get("user") if self.user is not None: self.user = User(self.user) - self.mentions = data.get("mentions") + self.mentions: List[User] = data.get("mentions") if self.mentions is not None: self.mentions = [User(mention) for mention in self.mentions] - self.group = data.get("group") + self.group: Group = data.get("group") if self.group is not None: self.group = Group(self.group) - self.conference_call = data.get("conference_call") + self.conference_call: ConferenceCall = data.get("conference_call") if self.conference_call is not None: self.conference_call = ConferenceCall(self.conference_call) @@ -1288,25 +1290,25 @@ def __init__(self, data: dict): self.attachment_9_thumbnail = data.get("attachment_9_thumbnail") self.shareable = data.get("shareable") - self.shared_url = data.get("shared_url") + self.shared_url: SharedUrl = data.get("shared_url") if self.shared_url is not None: self.shared_url = SharedUrl(self.shared_url) - self.survey = data.get("survey") + self.survey: Survey = data.get("survey") if self.survey is not None: self.survey = Survey(self.survey) - self.videos = data.get("videos") + self.videos: List[Video] = data.get("videos") if self.videos is not None: self.videos = [Video(video) for video in self.videos] - self.gifts_count = data.get("gifts_count") + self.gifts_count: List[GiftCount] = data.get("gifts_count") if self.gifts_count is not None: self.gifts_count = [ GiftCount(gifts_count) for gifts_count in self.gifts_count ] - self.shared_thread = data.get("shared_thread") + self.shared_thread: List[ThreadInfo] = data.get("shared_thread") if self.shared_thread is not None: self.shared_thread = [ ThreadInfo(shared_thread) for shared_thread in self.shared_thread @@ -1314,13 +1316,13 @@ def __init__(self, data: dict): self.thread_id = data.get("thread_id") - self.thread = data.get("thread") + self.thread: List[ThreadInfo] = data.get("thread") if self.thread is not None: self.thread = [ThreadInfo(thread) for thread in self.thread] self.highlighted = data.get("highlighted") - self.message_tags = data.get("message_tags") + self.message_tags: List[MessageTag] = data.get("message_tags") if self.message_tags is not None: self.message_tags = [ MessageTag(message_tag) for message_tag in self.message_tags @@ -1339,7 +1341,7 @@ def __init__(self, data: dict): self.data = data self.count = data.get("count") - self.gift = data.get("gift") + self.gift: Gift = data.get("gift") if self.gift is not None: self.gift = Gift(self.gift) @@ -1393,13 +1395,13 @@ class ReceivedGift(Model): def __init__(self, data: dict): self.data = data - self.gift = data.get("gift") + self.gift: Gift = data.get("gift") if self.gift is not None: self.gift = Gift(self.gift) self.received_count = data.get("received_count") - self.senders = data.get("senders") + self.senders: List[User] = data.get("senders") if self.senders is not None: self.senders = [User(sender) for sender in self.senders] @@ -1417,11 +1419,11 @@ def __init__(self, data: dict): self.id = data.get("id") self.type = data.get("type") - self.user = data.get("user") + self.user: User = data.get("user") if self.user is not None: self.user = User(self.user) - self.hashtag = data.get("hashtag") + self.hashtag: PostTag = data.get("hashtag") if self.hashtag is not None: self.hashtag = PostTag(self.hashtag) @@ -1462,7 +1464,7 @@ def __init__(self, data: dict): self.comment = data.get("comment") self.reported_count = data.get("reported_count") - self.user = data.get("user") + self.user: User = data.get("user") if self.user is not None: self.user = User(self.user) @@ -1659,15 +1661,15 @@ class Shareable(Model): def __init__(self, data: dict): self.data = data - self.post = data.get("post") + self.post: Post = data.get("post") if self.post is not None: self.post = Post(self.post) - self.group = data.get("group") + self.group: Group = data.get("group") if self.group is not None: self.group = Group(self.group) - self.thread = data.get("thread") + self.thread: ThreadInfo = data.get("thread") if self.thread is not None: self.thread = ThreadInfo(self.thread) @@ -1739,7 +1741,7 @@ def __init__(self, data: dict): self.description = data.get("description") self.cover = data.get("cover") - self.stickers = data.get("stickers") + self.stickers: List[Sticker] = data.get("stickers") if self.stickers is not None: self.stickers = [Sticker(sticker) for sticker in self.stickers] @@ -1757,7 +1759,7 @@ def __init__(self, data: dict): self.id = data.get("id") self.votes_count = data.get("votes_count") - self.choices = data.get("choices") + self.choices: List[Choice] = data.get("choices") if self.choices is not None: self.choices = [Choice(choice) for choice in self.choices] @@ -1788,11 +1790,11 @@ def __init__(self, data: dict): self.id = data.get("id") self.title = data.get("title") - self.owner = data.get("owner") + self.owner: User = data.get("owner") if self.owner is not None: self.owner = User(self.owner) - self.last_post = data.get("last_post") + self.last_post: Post = data.get("last_post") if self.last_post is not None: self.last_post = Post(self.last_post) @@ -1893,7 +1895,7 @@ def __init__(self, data: dict): ) self.is_selected_interests = data.get("interests_selected") - self.group_user = data.get("group_user") + self.group_user: GroupUser = data.get("group_user") if self.group_user is not None: self.group_user = GroupUser(self.group_user) @@ -1920,7 +1922,7 @@ def __init__(self, data: dict): self.refresh_token = data.get("refresh_token") self.expires_in = data.get("expires_in") - self.user = data.get("user") + self.user: User = data.get("user") if self.user is not None: self.user = User(self.user) @@ -1935,7 +1937,7 @@ def __init__(self, data: dict): self.data = data self.id = data.get("id") - self.user = data.get("user") + self.user: User = data.get("user") if self.user is not None: self.user = User(self.user) @@ -1995,7 +1997,7 @@ def __init__(self, data: dict): self.description = data.get("description") self.amount = data.get("amount") - self.coins = data.get("coins") + self.coins: CoinAmount = data.get("coins") if self.coins is not None: self.coins = CoinAmount(self.coins) From 5749942773186cfd667fd25a9828fa2681fe0855 Mon Sep 17 00:00:00 2001 From: ekkx Date: Sat, 31 Aug 2024 13:30:12 +0900 Subject: [PATCH 14/22] feat: add type hint to reponses.py --- yaylib/models.py | 4 +- yaylib/responses.py | 118 +++++++++++++++++++++++--------------------- 2 files changed, 63 insertions(+), 59 deletions(-) diff --git a/yaylib/models.py b/yaylib/models.py index ac8b34a..f5cbbcb 100644 --- a/yaylib/models.py +++ b/yaylib/models.py @@ -1691,7 +1691,7 @@ def __repr__(self): return f"SharedUrl(data={self.data})" -class SnsInfo(Model): +class SNSInfo(Model): __slots__ = ( "data", "type", @@ -1712,7 +1712,7 @@ def __init__(self, data: dict): self.gender = data.get("gender") def __repr__(self): - return f"SnsInfo(data={self.data})" + return f"SNSInfo(data={self.data})" class Sticker(Model): diff --git a/yaylib/responses.py b/yaylib/responses.py index 2746e7e..b07ceb0 100644 --- a/yaylib/responses.py +++ b/yaylib/responses.py @@ -22,6 +22,8 @@ SOFTWARE. """ +from typing import List + from .models import ( Activity, ApplicationConfig, @@ -51,7 +53,7 @@ Setting, Settings, SignaturePayload, - SnsInfo, + SNSInfo, StickerPack, Survey, ThreadInfo, @@ -95,7 +97,7 @@ def __init__(self, data: dict): self.last_loggedin_at = data.get("last_loggedin_at") - self.users = data.get("users") + self.users: List[User] = data.get("users") if self.users is not None: self.users = [User(user) for user in self.users] @@ -109,7 +111,7 @@ class ActivitiesResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.activities = data.get("activities") + self.activities: List[Activity] = data.get("activities") if self.activities is not None: self.activities = [Activity(activity) for activity in self.activities] @@ -125,7 +127,7 @@ class AdditionalSettingsResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.settings = data.get("settings") + self.settings: Setting = data.get("settings") if self.settings is not None: self.settings = Settings(self.settings) @@ -151,7 +153,7 @@ class BgmsResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.bgm = data.get("bgm") + self.bgm: List[Bgm] = data.get("bgm") if self.bgm is not None: self.bgm = [Bgm(bgm) for bgm in self.bgm] @@ -180,7 +182,7 @@ def __init__(self, data: dict): self.blocked_count = data.get("blocked_count") self.last_id = data.get("last_id") - self.users = data.get("users") + self.users: List[User] = data.get("users") if self.users is not None: self.users = [User(user) for user in self.users] @@ -220,7 +222,7 @@ class ChatRoomResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.chat = data.get("chat") + self.chat: ChatRoom = data.get("chat") if self.chat is not None: self.chat = ChatRoom(self.chat) @@ -234,14 +236,14 @@ class ChatRoomsResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.pinned_chat_rooms = data.get("pinned_chat_rooms") + self.pinned_chat_rooms: List[ChatRoom] = data.get("pinned_chat_rooms") if self.pinned_chat_rooms is not None: self.pinned_chat_rooms = [ ChatRoom(pinned_chat_room) for pinned_chat_room in self.pinned_chat_rooms ] - self.chat_rooms = data.get("chat_rooms") + self.chat_rooms: List[ChatRoom] = data.get("chat_rooms") if self.chat_rooms is not None: self.chat_rooms = [ChatRoom(chat_room) for chat_room in self.chat_rooms] @@ -269,7 +271,7 @@ class ConferenceCallResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.conference_call = data.get("conference_call") + self.conference_call: ConferenceCall = data.get("conference_call") if self.conference_call is not None: self.conference_call = ConferenceCall(self.conference_call) @@ -307,7 +309,7 @@ class CreateQuotaResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.create = data.get("create") + self.create: CreateGroupQuota = data.get("create") if self.create is not None: self.create = CreateGroupQuota(self.create) @@ -333,7 +335,7 @@ class CreatePostResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.conference_call = data.get("conference_call") + self.conference_call: ConferenceCall = data.get("conference_call") if self.conference_call is not None: self.conference_call = ConferenceCall(self.conference_call) @@ -405,7 +407,7 @@ class PresignedUrlsResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.presigned_urls = data.get("presigned_urls") + self.presigned_urls: List[PresignedUrl] = data.get("presigned_urls") if self.presigned_urls is not None: self.presigned_urls = [ PresignedUrl(presigned_url) for presigned_url in self.presigned_urls @@ -433,7 +435,7 @@ class DefaultSettingsResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.timeline_settings = data.get("timeline_settings") + self.timeline_settings: TimelineSettings = data.get("timeline_settings") if self.timeline_settings is not None: self.timeline_settings = TimelineSettings(self.timeline_settings) @@ -449,7 +451,7 @@ def __init__(self, data: dict): self.total = data.get("total") - self.users = data.get("users") + self.users: List[User] = data.get("users") if self.users is not None: self.users = [User(user) for user in self.users] @@ -479,7 +481,7 @@ def __init__(self, data: dict): self.last_follow_id = data.get("last_follow_id") - self.users = data.get("users") + self.users: List[User] = data.get("users") if self.users is not None: self.users = [User(user) for user in self.users] @@ -493,7 +495,7 @@ class FootprintsResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.footprints = data.get("footprints") + self.footprints: List[Footprint] = data.get("footprints") if self.footprints is not None: self.footprints = [Footprint(footprint) for footprint in self.footprints] @@ -507,7 +509,7 @@ class GamesResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.games = data.get("games") + self.games: List[Game] = data.get("games") if self.games is not None: self.games = [Game(game) for game in self.games] @@ -523,7 +525,7 @@ class GenresResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.genres = data.get("genres") + self.genres: List[Genre] = data.get("genres") if self.genres is not None: self.genres = [Genre(genre) for genre in self.genres] @@ -539,7 +541,7 @@ class GroupCategoriesResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.group_categories = data.get("group_categories") + self.group_categories: List[GroupCategory] = data.get("group_categories") if self.group_categories is not None: self.group_categories = [ GroupCategory(group_categorie) @@ -556,7 +558,7 @@ class GroupGiftHistoryResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.gift_history = data.get("gift_history") + self.gift_history: List[GroupGiftHistory] = data.get("gift_history") if self.gift_history is not None: self.gift_history = [ GroupGiftHistory(gift_history) for gift_history in self.gift_history @@ -574,7 +576,7 @@ class GroupNotificationSettingsResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.setting = data.get("setting") + self.setting: Setting = data.get("setting") if self.setting is not None: self.setting = Setting(self.setting) @@ -588,7 +590,7 @@ class GroupResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.group = data.get("group") + self.group: Group = data.get("group") if self.group is not None: self.group = Group(self.group) @@ -602,7 +604,7 @@ class GroupsRelatedResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.groups = data.get("groups") + self.groups: List[Group] = data.get("groups") if self.groups is not None: self.groups = [Group(group) for group in self.groups] @@ -618,13 +620,13 @@ class GroupsResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.pinned_groups = data.get("pinned_groups") + self.pinned_groups: List[Group] = data.get("pinned_groups") if self.pinned_groups is not None: self.pinned_groups = [ Group(pinned_group) for pinned_group in self.pinned_groups ] - self.groups = data.get("groups") + self.groups: List[Group] = data.get("groups") if self.groups is not None: self.groups = [Group(group) for group in self.groups] @@ -638,7 +640,7 @@ class GroupThreadListResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.threads = data.get("threads") + self.threads: List[ThreadInfo] = data.get("threads") if self.threads is not None: self.threads = [ThreadInfo(thread) for thread in self.threads] @@ -654,7 +656,7 @@ class GroupUserResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.group_user = data.get("group_user") + self.group_user: GroupUser = data.get("group_user") if self.group_user is not None: self.group_user = GroupUser(self.group_user) @@ -668,7 +670,7 @@ class GroupUsersResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.group_users = data.get("group_users") + self.group_users: List[GroupUser] = data.get("group_users") if self.group_users is not None: self.group_users = [ GroupUser(group_user) for group_user in self.group_users @@ -684,7 +686,7 @@ class GifsDataResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.gif_categories = data.get("gif_categories") + self.gif_categories: List[GifImageCategory] = data.get("gif_categories") if self.gif_categories is not None: self.gif_categories = [ GifImageCategory(gif_category) for gif_category in self.gif_categories @@ -700,7 +702,7 @@ class HiddenResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.hidden_users = data.get("hidden_users") + self.hidden_users: List[User] = data.get("hidden_users") if self.hidden_users is not None: self.hidden_users = [User(hidden_user) for hidden_user in self.hidden_users] @@ -731,9 +733,9 @@ def __init__(self, data: dict): self.username = data.get("username") self.is_new = data.get("is_new") - self.sns_info = data.get("sns_info") + self.sns_info: SNSInfo = data.get("sns_info") if self.sns_info is not None: - self.sns_info = SnsInfo(self.sns_info) + self.sns_info = SNSInfo(self.sns_info) self.access_token = data.get("access_token") self.refresh_token = data.get("refresh_token") @@ -774,7 +776,7 @@ def __init__(self, data: dict): self.id = data.get("id") - self.conference_call = data.get("conference_call") + self.conference_call: ConferenceCall = data.get("conference_call") if self.conference_call is not None: self.conference_call = ConferenceCall(self.conference_call) @@ -788,7 +790,7 @@ class MessagesResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.messages = data.get("messages") + self.messages: List[Message] = data.get("messages") if self.messages is not None: self.messages = [Message(message) for message in self.messages] @@ -815,7 +817,7 @@ class PostResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.post = data.get("post") + self.post: Post = data.get("post") if self.post is not None: self.post = Post(self.post) @@ -831,11 +833,11 @@ def __init__(self, data: dict): self.next_page_value = data.get("next_page_value") - self.posts = data.get("posts") + self.posts: List[Post] = data.get("posts") if self.posts is not None: self.posts = [Post(post) for post in self.posts] - self.pinned_posts = data.get("pinned_posts") + self.pinned_posts: List[Post] = data.get("pinned_posts") if self.pinned_posts is not None: self.pinned_posts = [Post(pinned_post) for pinned_post in self.pinned_posts] @@ -851,7 +853,7 @@ def __init__(self, data: dict): self.last_id = data.get("last_id") - self.users = data.get("users") + self.users: List[User] = data.get("users") if self.users is not None: self.users = [User(user) for user in self.users] @@ -865,7 +867,7 @@ class PostTagsResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.tags = data.get("tags") + self.tags: List[PostTag] = data.get("tags") if self.tags is not None: self.tags = [PostTag(tag) for tag in self.tags] @@ -879,7 +881,7 @@ class PromotionsResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.promotions = data.get("promotions") + self.promotions: List[Promotion] = data.get("promotions") if self.promotions is not None: self.promotions = [Promotion(promotion) for promotion in self.promotions] @@ -917,7 +919,9 @@ class RefreshCounterRequestsResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.reset_counter_requests = data.get("reset_counter_requests") + self.reset_counter_requests: List[RefreshCounterRequest] = data.get( + "reset_counter_requests" + ) if self.reset_counter_requests is not None: self.reset_counter_requests = [ RefreshCounterRequest(reset_counter_request) @@ -934,11 +938,11 @@ class ReviewsResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.reviews = data.get("reviews") + self.reviews: List[Review] = data.get("reviews") if self.reviews is not None: self.reviews = [Review(review) for review in self.reviews] - self.pinned_reviews = data.get("pinned_reviews") + self.pinned_reviews: List[Review] = data.get("pinned_reviews") if self.pinned_reviews is not None: self.pinned_reviews = [ Review(pinned_review) for pinned_review in self.pinned_reviews @@ -954,7 +958,7 @@ class SocialShareUsersResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.social_shared_users = data.get("social_shared_users") + self.social_shared_users: List[UserWrapper] = data.get("social_shared_users") if self.social_shared_users is not None: self.social_shared_users = [ UserWrapper(social_shared_user) @@ -971,7 +975,7 @@ class StickerPacksResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.sticker_packs = data.get("sticker_packs") + self.sticker_packs: List[StickerPack] = data.get("sticker_packs") if self.sticker_packs is not None: self.sticker_packs = [ StickerPack(sticker_pack) for sticker_pack in self.sticker_packs @@ -1022,7 +1026,7 @@ class VoteSurveyResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.survey = data.get("survey") + self.survey: Survey = data.get("survey") if self.survey is not None: self.survey = Survey(self.survey) @@ -1066,7 +1070,7 @@ class UserResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.user = data.get("user") + self.user: User = data.get("user") if self.user is not None: self.user = User(self.user) @@ -1086,7 +1090,7 @@ def __init__(self, data: dict): self.uuid = data.get("uuid") self.birthdate = data.get("birth_date") - self.gifting_ability = data.get("gifting_ability") + self.gifting_ability: GiftingAbility = data.get("gifting_ability") if self.gifting_ability is not None: self.gifting_ability = GiftingAbility(self.gifting_ability) @@ -1100,7 +1104,7 @@ class UsersResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.users = data.get("users") + self.users: List[User] = data.get("users") if self.users is not None: self.users = [User(user) for user in self.users] @@ -1116,7 +1120,7 @@ class RankingUsersResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.users = data.get("users") + self.users: List[User] = data.get("users") if self.users is not None: self.users = [User(user) for user in self.users] @@ -1156,7 +1160,7 @@ class HimaUsersResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.hima_users = data.get("hima_users") + self.hima_users: List[UserWrapper] = data.get("hima_users") if self.hima_users is not None: self.hima_users = [UserWrapper(hima_user) for hima_user in self.hima_users] @@ -1172,7 +1176,7 @@ def __init__(self, data: dict): self.last_timestamp = data.get("last_timestamp") - self.users = data.get("users") + self.users: List[User] = data.get("users") if self.users is not None: self.users = [User(user) for user in self.users] @@ -1212,7 +1216,7 @@ class ApplicationConfigResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.app = data.get("app") + self.app: ApplicationConfig = data.get("app") if self.app is not None: self.app = ApplicationConfig(self.app) @@ -1226,7 +1230,7 @@ class BanWordsResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.ban_words = data.get("ban_words") + self.ban_words: List[BanWord] = data.get("ban_words") if self.ban_words is not None: self.ban_words = [BanWord(ban_word) for ban_word in self.ban_words] @@ -1240,7 +1244,7 @@ class PopularWordsResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.popular_words = data.get("popular_words") + self.popular_words: List[PopularWord] = data.get("popular_words") if self.popular_words is not None: self.popular_words = [ PopularWord(popular_word) for popular_word in self.popular_words @@ -1256,7 +1260,7 @@ class CallActionSignatureResponse(Response): def __init__(self, data: dict): super().__init__(data) - self.signature_payload = data.get("signature_payload") + self.signature_payload: SignaturePayload = data.get("signature_payload") if self.signature_payload is not None: self.signature_payload = SignaturePayload(self.signature_payload) From dc8b2a363ce6a97b5e4f2b70ca4e1111c8a10eed Mon Sep 17 00:00:00 2001 From: ekkx Date: Sat, 31 Aug 2024 13:37:30 +0900 Subject: [PATCH 15/22] doc: fix docstring examples --- yaylib/api/misc.py | 6 +++--- yaylib/api/user.py | 6 +++--- yaylib/client.py | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/yaylib/api/misc.py b/yaylib/api/misc.py index 2ac997a..d3d4f63 100644 --- a/yaylib/api/misc.py +++ b/yaylib/api/misc.py @@ -231,12 +231,12 @@ async def upload_image( 投稿に画像を付与する場合 >>> # サーバー上にアップロード - >>> attachments = api.upload_image( + >>> attachments = client.upload_image( >>> image_type=yaylib.ImageType.POST, >>> image_paths=["./example.jpg"], >>> ) >>> # サーバー上のファイル名を指定 - >>> api.create_post( + >>> client.create_post( >>> "Hello with yaylib!", >>> attachment_filename=attachments[0].filename >>> ) @@ -352,7 +352,7 @@ async def upload_video(self, video_path: str) -> str: >>> # サーバー上にアップロード >>> filename = client.upload_video("./example.mp4") >>> # サーバー上のファイル名を指定 - >>> api.create_post( + >>> client.create_post( >>> "Hello with yaylib!", >>> video_file_name=filename >>> ) diff --git a/yaylib/api/user.py b/yaylib/api/user.py index 9063528..9a5be1a 100644 --- a/yaylib/api/user.py +++ b/yaylib/api/user.py @@ -242,13 +242,13 @@ async def get_user_ranking(self, mode: str) -> RankingUsersResponse: Examples: >>> # ルーキーを取得する: - >>> api.get_user_ranking(mode="one_month") + >>> client.get_user_ranking(mode="one_month") >>> # ミドルを取得する: - >>> api.get_user_ranking(mode="six_months") + >>> client.get_user_ranking(mode="six_months") >>> # 殿堂入りを取得する: - >>> api.get_user_ranking(mode="all_time") + >>> client.get_user_ranking(mode="all_time") Args: mode (str): diff --git a/yaylib/client.py b/yaylib/client.py index a519080..b56f188 100644 --- a/yaylib/client.py +++ b/yaylib/client.py @@ -1735,12 +1735,12 @@ def upload_image(self, image_paths: List[str], image_type: str) -> List[Attachme 投稿に画像を付与する場合 >>> # サーバー上にアップロード - >>> attachments = api.upload_image( + >>> attachments = client.upload_image( >>> image_type=yaylib.ImageType.POST, >>> image_paths=["./example.jpg"], >>> ) >>> # サーバー上のファイル名を指定 - >>> api.create_post( + >>> client.create_post( >>> "Hello with yaylib!", >>> attachment_filename=attachments[0].filename >>> ) @@ -1766,7 +1766,7 @@ def upload_video(self, video_path: str) -> str: >>> # サーバー上にアップロード >>> filename = client.upload_video("./example.mp4") >>> # サーバー上のファイル名を指定 - >>> api.create_post( + >>> client.create_post( >>> "Hello with yaylib!", >>> video_file_name=filename >>> ) @@ -2770,13 +2770,13 @@ def get_user_ranking(self, mode: str) -> RankingUsersResponse: Examples: >>> # ルーキーを取得する: - >>> api.get_user_ranking(mode="one_month") + >>> client.get_user_ranking(mode="one_month") >>> # ミドルを取得する: - >>> api.get_user_ranking(mode="six_months") + >>> client.get_user_ranking(mode="six_months") >>> # 殿堂入りを取得する: - >>> api.get_user_ranking(mode="all_time") + >>> client.get_user_ranking(mode="all_time") Args: mode (str): From 337effe4754f1e8ea9fc57a4f7d32b71ad412e1c Mon Sep 17 00:00:00 2001 From: ekkx Date: Sat, 31 Aug 2024 14:14:10 +0900 Subject: [PATCH 16/22] chore: add test code for deivce.py --- tests/test_device.py | 54 ++++++++++++++++++++++++++++++++++++++++++++ yaylib/device.py | 11 +++++++-- 2 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 tests/test_device.py diff --git a/tests/test_device.py b/tests/test_device.py new file mode 100644 index 0000000..ff30e47 --- /dev/null +++ b/tests/test_device.py @@ -0,0 +1,54 @@ +import unittest + +from yaylib.config import VERSION_NAME +from yaylib.device import DEVICES, Device + +TEST_DEVICE_TYPE = "android" +TEST_OS_VERSION = "11" +TEST_SCREEN_DENSITY = "3.5" +TEST_SCREEN_SIZE = "1440x2960" +TEST_MODEL = "Galaxy S9" + + +class TestDevice(unittest.TestCase): + def test_create_device_with_model(self): + created_device = Device.create(model=TEST_MODEL) + self.assertIsNotNone(created_device) + self.assertEqual(created_device.model, TEST_MODEL) + self.assertEqual(created_device.device_type, TEST_DEVICE_TYPE) + self.assertEqual(created_device.os_version, TEST_OS_VERSION) + self.assertEqual(created_device.screen_density, TEST_SCREEN_DENSITY) + self.assertEqual(created_device.screen_size, TEST_SCREEN_SIZE) + + def test_create_random_device(self): + created_device = Device.create() + created_device_dict = next( + (device for device in DEVICES if device["model"] == created_device.model), + None, + ) + self.assertIsNotNone(created_device) + + created_device_type = created_device_dict.get("device_type") + created_os_version = created_device_dict.get("os_version") + created_screen_density = created_device_dict.get("screen_density") + created_screen_size = created_device_dict.get("screen_size") + + self.assertEqual(created_device.device_type, created_device_type) + self.assertEqual(created_device.os_version, created_os_version) + self.assertEqual(created_device.screen_density, created_screen_density) + self.assertEqual(created_device.screen_size, created_screen_size) + + def test_device_user_agent(self): + excpected_user_agent = f"{TEST_DEVICE_TYPE} {TEST_OS_VERSION} ({TEST_SCREEN_DENSITY}x {TEST_SCREEN_SIZE} {TEST_MODEL})" + created_device = Device.create(model=TEST_MODEL) + self.assertEqual(created_device.get_user_agent(), excpected_user_agent) + + def test_device_info(self): + excpected_user_agent = f"{TEST_DEVICE_TYPE} {TEST_OS_VERSION} ({TEST_SCREEN_DENSITY}x {TEST_SCREEN_SIZE} {TEST_MODEL})" + excpected_device_info = f"yay {VERSION_NAME} {excpected_user_agent}" + created_device = Device.create(model=TEST_MODEL) + self.assertEqual(created_device.get_device_info(), excpected_device_info) + + def test_create_device_with_invalid_model(self): + with self.assertRaises(ValueError): + Device.create(model="__invalid_model__") diff --git a/yaylib/device.py b/yaylib/device.py index 578ee18..a3875c0 100644 --- a/yaylib/device.py +++ b/yaylib/device.py @@ -56,10 +56,17 @@ def __init__( self.model = model @classmethod - def create(cls, device: Optional[dict] = None): + def create(cls, device: Optional[dict] = None, model: Optional[str] = None): """端末を生成する""" if device is None: - device = random.choice(DEVICES) + if model is not None: + device = next( + (device for device in DEVICES if device["model"] == model), None + ) + if device is None: + raise ValueError("No device found with model: " + model) + else: + device = random.choice(DEVICES) return cls(**device) def get_user_agent(self) -> str: From 02714a01cb9cecfb44a06573b1b4f4c847ab31be Mon Sep 17 00:00:00 2001 From: ekkx Date: Sat, 31 Aug 2024 15:00:28 +0900 Subject: [PATCH 17/22] chore: add test code for ratelimit --- tests/test_ratelimit.py | 49 +++++++++++++++++++++++++++++++++++++++++ yaylib/client.py | 15 +++++++++---- 2 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 tests/test_ratelimit.py diff --git a/tests/test_ratelimit.py b/tests/test_ratelimit.py new file mode 100644 index 0000000..aec8901 --- /dev/null +++ b/tests/test_ratelimit.py @@ -0,0 +1,49 @@ +import unittest +from unittest.mock import patch + +from yaylib.client import RateLimit + + +class TestException(Exception): + pass + + +class TestRateLimit(unittest.IsolatedAsyncioTestCase): + def test_initial_retries_performed(self): + ratelimit = RateLimit(wait_on_ratelimit=True, max_retries=3, retry_after=1) + self.assertEqual(ratelimit.retries_performed, 0) + + def test_initial_max_retries(self): + ratelimit = RateLimit(wait_on_ratelimit=True, max_retries=3, retry_after=1) + self.assertEqual(ratelimit.max_retries, 3) + + def test_max_retries_reached(self): + ratelimit = RateLimit(wait_on_ratelimit=True, max_retries=3, retry_after=1) + ratelimit.retries_performed = 3 + self.assertTrue(ratelimit.max_retries_reached) + + def test_reset(self): + ratelimit = RateLimit(wait_on_ratelimit=True, max_retries=3, retry_after=1) + ratelimit.retries_performed = 2 + self.assertEqual(ratelimit.retries_performed, 2) + ratelimit.reset() + self.assertEqual(ratelimit.retries_performed, 0) + + @patch("asyncio.sleep", return_value=None) + async def test_wait_on_ratelimit_enabled(self, mock_sleep): + ratelimit = RateLimit(wait_on_ratelimit=True, max_retries=1, retry_after=1) + + await ratelimit.wait(TestException()) + self.assertEqual(ratelimit.retries_performed, 1) + + with self.assertRaises(TestException): + await ratelimit.wait(TestException()) + + @patch("asyncio.sleep", return_value=None) + async def test_wait_on_ratelimit_disabled(self, mock_sleep): + ratelimit = RateLimit(wait_on_ratelimit=False, max_retries=2, retry_after=1) + + self.assertTrue(ratelimit.max_retries_reached) + + with self.assertRaises(TestException): + await ratelimit.wait(TestException()) diff --git a/yaylib/client.py b/yaylib/client.py index b56f188..db29859 100644 --- a/yaylib/client.py +++ b/yaylib/client.py @@ -131,11 +131,13 @@ class RateLimit: """レート制限を管理するクラス""" - def __init__(self, wait_on_ratelimit: bool, max_retries: int) -> None: + def __init__( + self, wait_on_ratelimit: bool, max_retries: int, retry_after=60 * 5 + ) -> None: self.__wait_on_ratelimit = wait_on_ratelimit self.__max_retries = max_retries self.__retries_performed = 0 - self.__retry_after = 60 * 5 + self.__retry_after = retry_after @property def retries_performed(self) -> int: @@ -146,6 +148,10 @@ def retries_performed(self) -> int: """ return self.__retries_performed + @retries_performed.setter + def retries_performed(self, value: int) -> None: + self.__retries_performed = min(value, self.__max_retries) + @property def max_retries(self) -> int: """レート制限によるリトライ回数の上限 @@ -155,7 +161,8 @@ def max_retries(self) -> int: """ return self.__max_retries - def __max_retries_reached(self) -> bool: + @property + def max_retries_reached(self) -> bool: """リトライ回数上限に達したか否か""" return not self.__wait_on_ratelimit or ( self.__retries_performed >= self.__max_retries @@ -171,7 +178,7 @@ async def wait(self, err: Exception) -> None: Raises: Exception: リトライ回数の上限でスロー """ - if not self.__wait_on_ratelimit or self.__max_retries_reached(): + if not self.__wait_on_ratelimit or self.max_retries_reached: raise err await asyncio.sleep(self.__retry_after) self.__retries_performed += 1 From 63bcf287a9d28d60bbe60b9723268fb3d0f3e014 Mon Sep 17 00:00:00 2001 From: ekkx Date: Mon, 2 Sep 2024 13:49:58 +0900 Subject: [PATCH 18/22] feat: add header manager properties --- yaylib/client.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/yaylib/client.py b/yaylib/client.py index db29859..d246b5b 100644 --- a/yaylib/client.py +++ b/yaylib/client.py @@ -199,6 +199,26 @@ def __init__(self, device: Device, state: State, locale="ja") -> None: self.__connection_type = "wifi" self.__content_type = "application/json;charset=UTF-8" + @property + def locale(self): + """ロケール""" + return self.__locale + + @property + def user_agent(self): + """ユーザーエージェント""" + return self.__user_agent + + @property + def device_info(self) -> str: + """デバイス情報""" + return self.__device_info + + @property + def app_version(self) -> str: + """アプリバージョン""" + return self.__app_version + @property def client_ip(self) -> str: """クライアント IP アドレス""" @@ -208,6 +228,21 @@ def client_ip(self) -> str: def client_ip(self, value: str) -> None: self.__client_ip = value + @property + def connection_speed(self) -> str: + """コネクション速度""" + return self.__connection_speed + + @property + def connection_type(self) -> str: + """コネクション種別""" + return self.__connection_type + + @property + def content_type(self) -> str: + """コンテントタイプ""" + return self.__content_type + def generate(self, jwt_required=False) -> Dict[str, str]: """HTTPヘッダーを生成する""" headers = { From 3359ad98ccb9833544f674dd499be3861cc89219 Mon Sep 17 00:00:00 2001 From: ekkx Date: Mon, 2 Sep 2024 13:50:18 +0900 Subject: [PATCH 19/22] chore: update unit test ci to use multiple python versions --- .github/workflows/tests.yaml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 70b20b6..de376c9 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -13,7 +13,13 @@ on: - synchronize jobs: - publish: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.10", "3.11", "3.12"] + + test: runs-on: ubuntu-latest steps: - name: Checkout @@ -22,10 +28,13 @@ jobs: - name: Install Poetry run: pipx install poetry - - name: Setup Python + - name: Setup Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: ${{ matrix.python-version }} + + - name: Display Python version + run: python -c "import sys; print(sys.version)" - name: Check poetry run: | From d0084ab97d83984fd7a52a213920fc2b06290082 Mon Sep 17 00:00:00 2001 From: ekkx Date: Mon, 2 Sep 2024 13:53:23 +0900 Subject: [PATCH 20/22] chore: fix unit test ci --- .github/workflows/tests.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index de376c9..2d34964 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -19,8 +19,6 @@ jobs: matrix: python-version: ["3.10", "3.11", "3.12"] - test: - runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 From 5e5c509d3f9ef4a10b545c21275e6db79b54f7d9 Mon Sep 17 00:00:00 2001 From: ekkx Date: Mon, 2 Sep 2024 14:34:49 +0900 Subject: [PATCH 21/22] doc: update readme.md --- README.md | 208 +++++++++++++++++++++--------------------------------- 1 file changed, 82 insertions(+), 126 deletions(-) diff --git a/README.md b/README.md index d6384df..3c0e92b 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@

- + Logo

yaylib

@@ -21,62 +21,30 @@

- Report Bug + ドキュメント · - Request Feature + バグを報告 · - Join the discord + Discord に参加

- - -
- Table of Contents -
    -
  1. Buy me a coffee
  2. -
  3. インストール
  4. -
  5. 使用例
  6. -
  7. yaylib で誕生したロボットたち
  8. -
  9. 共同開発について
  10. -
  11. 免責事項
  12. -
  13. 利用許諾
  14. -
-
- - - -## Buy me a coffee - -このライブラリが気に入っていただけたら、**リポジトリにスターをお願いします(⭐)** -また、Buy Me a Coffee からご支援いただけますと幸いです。 - -Buy Me A Coffee +
## [](https://github.com/ekkx) Installation -**※ Python 3.10 以上のバージョンが必要です。** - -「yaylib」をインストールするには、以下のコマンドをターミナル上で実行します: +**yaylib** は `pip` コマンドからインストールします。 ```shell pip install yaylib ``` -
- > [!TIP] -> 開発環境をインストールする場合は、以下の手順を実行します: +> 動作条件は `Python 3.10` 以上からです。 -```shell -git clone https://github.com/ekkx/yaylib.git - -cd yaylib - -make up -``` +
@@ -87,21 +55,44 @@ make up ```python import yaylib -client = yaylib.Client() -client.login(email="your_email", password="your_password") +bot = yaylib.Client() +bot.login('your_email', 'your_password') + +bot.create_post('Hello with yaylib!') +``` + +#### ✨ タイムラインを取得する + +```python +import yaylib + +bot = yaylib.Client() + +timeline = bot.get_timeline(number=100) -client.create_post("Hello with yaylib!") +for post in timeline.posts: + print(post.user.nickname) # 投稿者名 + print(post.text) # 本文 + print(post.likes_count) # いいね数 + print(post.reposts_count) # (´∀`∩)↑age↑の数 + print(post.in_reply_to_post_count) # 返信の数 ``` -#### ✨ 埋め込みリンクの投稿を作成する +#### ✨ タイムラインをキーワードで検索して「いいね」する ```python import yaylib -client = yaylib.Client() -client.login(email="your_email", password="your_password") +bot = yaylib.Client() +bot.login('your_email', 'your_password') + +timeline = bot.get_timeline_by_keyword( + keyword='プログラミング', + number=15 +) -client.create_post("Hello with yaylib!", shared_url="https://github.com/ekkx/yaylib") +for post in timeline.posts: + bot.like(post.id) ``` #### ✨ 画像と一緒に投稿を作成する @@ -109,122 +100,87 @@ client.create_post("Hello with yaylib!", shared_url="https://github.com/ekkx/yay ```python import yaylib -client = yaylib.Client() -client.login(email="your_email", password="your_password") +bot = yaylib.Client() +bot.login('your_email', 'your_password') # 画像のパスを指定 image_paths = [ - "./test1.jpg", - "./test2.jpg", - "./test3.jpg", + './test1.jpg', + './test2.jpg', + './test3.jpg', ] # 画像の使い道を指定 -image_type = yaylib.ImageType.post +image_type = yaylib.ImageType.POST # サーバー上にアップロード -attachments = client.upload_image(image_paths, image_type) +attachments = bot.upload_image(image_paths, image_type) # サーバー上のファイル名を指定する # attachmentsが一つ飛ばしなのはオリジナル品質の画像のみを指定するため -client.create_post( - "Hello with yaylib!", +bot.create_post( + 'Hello with yaylib!', attachment_filename=attachments[0].filename, attachment_2_filename=attachments[2].filename, attachment_3_filename=attachments[4].filename, ) ``` -#### ✨ タイムラインを 100 件取得する - -```python -import yaylib - -client = yaylib.Client() - -timeline = client.get_timeline(number=100) - -for post in timeline.posts: - print(post.user.nickname) # 投稿者名 - print(post.text) # 本文 - print(post.likes_count) # いいね数 - print(post.reposts_count) # (´∀`∩)↑age↑の数 - print(post.in_reply_to_post_count) # 返信の数 -``` - -#### ✨ タイムラインをキーワードで検索して「いいね」する - -```python -import yaylib - -client = yaylib.Client() -client.login(email="your_email", password="your_password") - -timeline = client.get_timeline_by_keyword( - keyword="プログラミング", - number=15 -) - -for post in timeline.posts: - client.like(post.id) -``` - #### ✨ 新規ユーザーをフォローする ```python import yaylib -client = yaylib.Client() -client.login(email="your_email", password="your_password") +bot = yaylib.Client() +bot.login('your_email', 'your_password') -new_users = client.search_users(recently_created=True) +new_users = bot.search_users(recently_created=True) for new_user in new_users.users: - client.follow_user(new_user.id) + bot.follow_user(new_user.id) ``` #### ✨ リアルタイムでチャットを取得する ```python import yaylib -from yaylib import Message -class MyBot(yaylib.Client): - def on_ready(self): - print("ボットがオンラインになりました!") +class ChatBot(yaylib.Client): + async def on_ready(): + print('Botがオンラインになりました!') - def on_chat_request(self, total_count): - # チャットリクエストはすべて承認する - chat_requests = self.get_chat_requests() + async def on_chat_request(self, total_count): + # チャットリクエストを承認し on_message() に送信する + chat_requests = await self.chat.get_chat_requests() for chat_room in chat_requests.chat_rooms: - self.accept_chat_requests([chat_room.id]) - - # 最新のメッセージをon_message_create関数に送信 - message = self.get_messages(chat_requests.chat_rooms[0].id) - self.on_message_create(message[0]) + await self.chat.accept_chat_requests(chat_room_ids=[chat_room.id]) + message = await self.chat.get_messages(chat_requests.chat_rooms[0].id) + await self.on_message(message[0]) - def on_message_create(self, message: Message): - # 「ping」というメッセージに対して「pong」と返信する - if message.text == "ping": - self.send_message(message.room_id, text="pong") + async def on_message(self, message: yaylib.Message): + if message.text == 'ping': + await self.chat.send_message( + message.room_id, + text='pong', + ) - def on_chat_room_delete(self, room_id): - print(f"チャットルームが削除されました。ルームID: {room_id}") + async def on_chat_delete(self, room_id): + print(f'チャットルームが削除されました。{room_id}') intents = yaylib.Intents.none() intents.chat_message = True -bot = MyBot(intents=intents) -bot.run("your_email", "your_password") +bot = ChatBot(intents=intents) +bot.run('your_email', 'your_password') ``` -より詳しい使用例については、[こちら](https://github.com/ekkx/yaylib/blob/master/examples) を参照してください。 +より詳しい使用例については、[ドキュメント](https://ekkx.github.io/yaylib/demo.html)を参照してください。 -

(トップに戻る)

+
-## :crown: yaylib で誕生したロボットたち +## 👑 yaylib で誕生したロボットたち 「yaylib」を用いて開発したロボットがある場合は、ぜひ教えてください! @@ -257,27 +213,27 @@ bot.run("your_email", "your_password") - +
-## :handshake: 共同開発について + -私たちと開発することに興味を持っていただけているなら、ぜひ参加して頂きたいです! +## 🤝 共同開発について -- [プルリクエストを送信する](https://github.com/ekkx/yaylib/pulls) -- [Discord サーバーに参加する](https://discord.gg/MEuBfNtqRN) -- [nikola.desuga@gmail.com](mailto:nikola.desuga@gmail.com) にメールを送信する +詳しい **yaylib** の開発参加手順については、[こちら](https://github.com/ekkx/yaylib/blob/develop/CONTRIBUTING.md)を参照してください。 -のいずれかの方法で繋がりましょう。詳しくは[こちらから](https://github.com/ekkx/yaylib/blob/master/CONTRIBUTING.md)! +
-## 免責事項 +## 📜 免責事項 yaylib は、API の公式なサポートやメンテナンスを提供するものではありません。このクライアントを使用する場合、**利用者はリスクや責任を自己負担できるもの**とします。このクライアントによって提供される情報やデータの正確性、信頼性、完全性、適時性について、いかなる保証も行いません。また、このクライアントの使用によって生じた損害や不利益について、一切の責任を負いかねます。利用者は自己の責任において、このクライアントを使用し、API にアクセスするものとします。なお、この免責事項は予告なく変更される場合があります。 +
+ -## ライセンス +## ⚖️ ライセンス

From f050519a3bd93685a62ce6e98abe17fbd8749544 Mon Sep 17 00:00:00 2001 From: ekkx Date: Mon, 2 Sep 2024 14:38:19 +0900 Subject: [PATCH 22/22] chore: update yaylib version to 1.5.0 beta 1 --- yaylib/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yaylib/__init__.py b/yaylib/__init__.py index b28c8a4..2e4cefa 100644 --- a/yaylib/__init__.py +++ b/yaylib/__init__.py @@ -23,7 +23,7 @@ # 1.0.0rc1 Release Candidate # 1.0.0 Final Release # 1.0.0.post1 Post Release -__version__ = "1.5.0.dev4" +__version__ = "1.5.0.b1" from .client import Client from .constants import *