diff --git a/docs/api/interface.md b/docs/api/interface.md index c48a3c4..f6bd8d4 100644 --- a/docs/api/interface.md +++ b/docs/api/interface.md @@ -38,7 +38,7 @@ type_: Optional[[NotificationType](#notificationtype)] = None, is_sort: Optional > > **形参:** > -> - game_type ([GameType](#gametype)): 游戏类型。 +> - game_type ([**GameType**](#gametype)): 游戏类型。 > > **返回值:** `str` - 该游戏类型的版本号。 @@ -166,21 +166,25 @@ _class mhyy.NotificationType_ _class mhyy.User(combo_token: str, sys_version: str, device_id: str, device_name: str, device_model: str, *, game_type: Optional[[GameType](#gametype)] = None, client_type: Optional[[UserClientType](#userclienttype)] = -UserClientType.Android, channel: Optional[UserChannel] = UserChannel.Official +None, channel: Optional[UserChannel] = UserChannel.Official )_ > 用户类。 > > 形参: > -> - **combo_token** (str): 对应 headers 中的 x-rpc-combo_token。 -> - **sys_version** (str): 对应 headers 中的 x-rpc-sys_version。 -> - **device_id** (str): 对应 headers 中的 x-rpc-device_id。 -> - **device_name** (str): 对应 headers 中的 x-rpc-device_name。 -> - **device_model** (str): 对应 headers 中的 x-rpc-device_model。 -> - **game_type** (Optional[[GameType](#gametype)]): 游戏类型,若为空则将会从 combo_token 中自动识别。 -> - **client_type** (Optional[[UserClientType](#userclienttype)]): 用户的客户端种类。 -> - **channel** (Optional[[UserChannel](#userchannel)]): 用户的游戏渠道。 +> - combo_token (**str**): 对应 headers 中的 x-rpc-combo_token。 +> - sys_version (**str**): 对应 headers 中的 x-rpc-sys_version。 +> - device_id (**str**): 对应 headers 中的 x-rpc-device_id。 +> - device_name (**str**): 对应 headers 中的 x-rpc-device_name。 +> - device_model (**str**): 对应 headers 中的 x-rpc-device_model。 +> - game_type (**Optional[[GameType](#gametype)]**): 游戏类型,若为空则将会从 combo_token 中自动识别。 +> - client_type (**Optional[[UserClientType](#userclienttype)]**): 用户的客户端种类。 +> - channel (**Optional[[UserChannel](#userchannel)]**): 用户的游戏渠道。 + +!!! Warning + + 在下一次迭代 (v2.1.1) 中,将强制要求指定 `client_type`。 !!! Note @@ -228,14 +232,14 @@ _class mhyy.UserClientType_ > 客户端类型。 -!!! Note - - 目前 mhyy.py 仅支持模拟安卓设备操作。 - `Android` = 2 > 安卓。 +`PCWeb` = 16 + +> PC 网页版。 + ## WalletData _class mhyy.WalletData_ diff --git a/docs/index.md b/docs/index.md index c06608c..a4021d4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -26,13 +26,14 @@ $ pip install mhyy.py 像这样创建用户并填写 headers 后进行签到操作吧 : ```pycon ->>> from mhyy import User, Client +>>> from mhyy import User, Client, UserClientType >>> user = User( ... combo_token="x-rpc-combo_token", ... sys_version="x-rpc-sys_version", ... device_id="x-rpc-device_id", ... device_name="x-rpc-device_name", -... device_model="x-rpc-device_model" +... device_model="x-rpc-device_model", +... client_type=UserClientType.Android ... ) >>> client = Client() >>> r = client.get_wallet_data(user) @@ -46,7 +47,7 @@ WalletData(coin=CoinData(coin_num=0, free_coin_num=0, coin_limit=200000, ...)) True ``` -是的,mhyy.py 支持 云·原神、云·星穹铁道。你无需填写某一位用户属于什么游戏,mhyy.py 将会自动识别并进行操作。 +是的,mhyy.py 支持 云·原神、云·星穹铁道 以及它们的网页版。你无需填写某一位用户属于什么游戏,mhyy.py 将会自动识别并进行操作。 在上述操作中,你成功完成了从**定义用户**到**获取钱包信息**再到**判断是否进行了签到操作**的过程。 diff --git a/docs/usage/multi_client_support.md b/docs/usage/multi_client_support.md new file mode 100644 index 0000000..da0862b --- /dev/null +++ b/docs/usage/multi_client_support.md @@ -0,0 +1,19 @@ +在 `2.1.0` 版本中,我们添加了 mhyy.py 的第二种客户端类型—— +[PCWeb](../api/interface.md#userclienttype) + +因此,你可以直接从 云·原神 或 云·星穹铁道 的网页版获取你的 headers。 + +所以你可以这样定义你在云游戏网页版的用户: + +```python +from mhyy import User, UserClientType + +user = User( + combo_token="x-rpc-combo_token", + sys_version="x-rpc-sys_version", + device_id="x-rpc-device_id", + device_name="x-rpc-device_name", + device_model="x-rpc-device_model", + client_type=UserClientType.PCWeb +) +``` \ No newline at end of file diff --git a/docs/usage/quick_start.md b/docs/usage/quick_start.md index 1cc2eef..5853f7b 100644 --- a/docs/usage/quick_start.md +++ b/docs/usage/quick_start.md @@ -10,20 +10,26 @@ 在进行这一步之前,你要通过 **抓包** 等方式获取用户的 headers 信息。 -若你已经拥有了你的 headers,像这样定义一个用户吧: +若你已经拥有了你的 headers,像这样定义一个用户吧: ```pycon ->>> user = mhyy.User( +>>>user = mhyy.User( ... combo_token="x-rpc-combo_token", ... sys_version="x-rpc-sys_version", ... device_id="x-rpc-device_id", ... device_name="x-rpc-device_name", -... device_model="x-rpc-device_model" +... device_model="x-rpc-device_model", +... client_type=mhyy.UserClientType.Android ... ) ``` 至此,你已经完成了定义用户的操作。 +这里的 `client_type` 是根据你的抓包客户端来选择的,如果你的客户端是云·原神(星穹铁道) +网页版,请选择`mhyy.UserClientType.PCWeb`。 + +参见 [多客户端支持](./multi_client_support.md)。 + ## 定义一个客户端并进行客户端操作 这一步你并不需要准备任何东西,你只需要像这样: diff --git a/mkdocs.yml b/mkdocs.yml index 26bff8a..b7ce8e6 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -21,6 +21,7 @@ nav: - 简介: index.md - 使用: - 快速入门: usage/quick_start.md + - 多客户端支持: usage/multi_client_support.md - API 参考: - 开发人员接口: api/interface.md diff --git a/src/mhyy/__init__.py b/src/mhyy/__init__.py index 49d0247..09c8f10 100644 --- a/src/mhyy/__init__.py +++ b/src/mhyy/__init__.py @@ -1,10 +1,10 @@ -# Client. +# Client from ._client import Client from ._types import GameType -# User. +# User from ._user import User from ._types import UserClientType, UserChannel -# Notification. +# Notification from ._types import NotificationType, NotificationStatus diff --git a/src/mhyy/_api.py b/src/mhyy/_api.py index 9cc6cf2..d075afb 100644 --- a/src/mhyy/_api.py +++ b/src/mhyy/_api.py @@ -1,4 +1,4 @@ -from ._types import GameType, UserChannel +from ._types import GameType, UserChannel, UserClientType class API: @@ -24,49 +24,63 @@ def get_game_version_url(game_type: GameType) -> str: }[game_type] @staticmethod - def get_app_id(game_type: GameType) -> str: + def get_app_id(game_type: GameType, client_type: UserClientType) -> str: return { - GameType.GenshinImpact: "1953439974", - GameType.StarRail: "1953445976" - }[game_type] + UserClientType.Android: { + GameType.GenshinImpact: "1953439974", + GameType.StarRail: "1953445976", + }, + UserClientType.PCWeb: { + GameType.GenshinImpact: "4", + GameType.StarRail: "8" + } + }[client_type][game_type] @staticmethod - def get_vendor_id(game_type: GameType) -> str: + def get_vendor_id(game_type: GameType, client_type: UserClientType) -> str: return { - GameType.GenshinImpact: "1", - GameType.StarRail: "2" - }[game_type] + UserClientType.Android: { + GameType.GenshinImpact: "1", + GameType.StarRail: "2", + }, + UserClientType.PCWeb: { + GameType.GenshinImpact: "2", + GameType.StarRail: "2" + } + }[client_type][game_type] @staticmethod def get_cg_game_biz(game_type: GameType) -> str: return { GameType.GenshinImpact: "hk4e_cn", - GameType.StarRail: "hkrpg_cn" + GameType.StarRail: "hkrpg_cn", }[game_type] @staticmethod def get_op_biz(game_type: GameType) -> str: return { GameType.GenshinImpact: "clgm_cn", - GameType.StarRail: "clgm_hkrpg-cn" + GameType.StarRail: "clgm_hkrpg-cn", }[game_type] @staticmethod - def get_cps(game_type: GameType) -> str: + def get_cps(game_type: GameType, client_type: UserClientType) -> str: return { - GameType.GenshinImpact: "cyydmihoyo", - GameType.StarRail: "gw_An_C" - }[game_type] + UserClientType.Android: { + GameType.GenshinImpact: "mihoyo", + GameType.StarRail: "mihoyo", + }, + UserClientType.PCWeb: { + GameType.GenshinImpact: "mihoyo", + GameType.StarRail: "mihoyo" + } + }[client_type][game_type] @staticmethod - def get_channel_id(user_channel: UserChannel, game_type: GameType) -> str: - channels = { - UserChannel.Official: { - GameType.GenshinImpact: "cyydmihoyo", - GameType.StarRail: "gw_An_C" - } - } - return channels[user_channel][game_type] + def get_channel_id(user_channel: UserChannel) -> str: + return { + UserChannel.Official: "mihoyo" + }[user_channel] @staticmethod def get_wallet_data_url(game_type: GameType) -> str: diff --git a/src/mhyy/_client.py b/src/mhyy/_client.py index 7ed6b16..00eb461 100644 --- a/src/mhyy/_client.py +++ b/src/mhyy/_client.py @@ -1,6 +1,6 @@ from enum import Enum from typing import Optional, List -from ._types import GameType, NotificationType, NotificationStatus +from ._types import GameType, NotificationType, NotificationStatus, UserClientType from ._api import API from ._user import User from ._exceptions import WebRequestError, APIRequestError @@ -67,23 +67,24 @@ def _update_version(self, game_type: GameType) -> None: self._versions[game_type] = resp["data"]["game"]["latest"]["version"] - def _get_common_headers(self, game_type: GameType) -> dict: + def _get_common_headers(self, game_type: GameType, client_type: UserClientType) -> dict: """ 获取指定游戏类型的 headers 常量。 Args: game_type (GameType): 需要获取 headers 的游戏类型。 + client_type (UserClientType): 用户的客户端类型。 Returns: 一个字典,包含了指定游戏的 headers。 """ return { "x-rpc-app_version": self._versions[game_type], - "x-rpc-app_id": API.get_app_id(game_type), - "x-rpc-vendor_id": API.get_vendor_id(game_type), + "x-rpc-app_id": API.get_app_id(game_type, client_type), + "x-rpc-vendor_id": API.get_vendor_id(game_type, client_type), "x-rpc-cg_game_biz": API.get_cg_game_biz(game_type), "x-rpc-op_biz": API.get_op_biz(game_type), - "x-rpc-cps": API.get_cps(game_type) + "x-rpc-cps": API.get_cps(game_type, client_type) } def _user_web_get(self, user: User, url: str, params: Optional[dict] = None) -> httpx.Response: @@ -112,13 +113,14 @@ def _user_web_get(self, user: User, url: str, params: Optional[dict] = None) -> self._status = ClientStatus.OPENED # Get the special common headers of the game. - headers: dict = self._get_common_headers(user.game_type) + headers: dict = self._get_common_headers(user.game_type, user.client_type) user_headers: dict = user.get_user_headers() - user_headers["x-rpc-channel"] = API.get_channel_id(user.channel, user.game_type) headers.update(user_headers) + headers["x-rpc-channel"] = API.get_channel_id(user.channel) + resp = self._client.get(url, headers=headers, params=params) if resp.status_code != 200: diff --git a/src/mhyy/_types.py b/src/mhyy/_types.py index 5cd9265..b5665c0 100644 --- a/src/mhyy/_types.py +++ b/src/mhyy/_types.py @@ -20,8 +20,10 @@ class UserClientType(Enum): Attributes: Android: 安卓。 + PCWeb: PC 网页版。 """ Android = 2 + PCWeb = 16 class UserChannel(Enum): diff --git a/src/mhyy/_user.py b/src/mhyy/_user.py index 88fe5d6..30a5b0f 100644 --- a/src/mhyy/_user.py +++ b/src/mhyy/_user.py @@ -8,6 +8,7 @@ class User: """ 用户类。 """ + def __init__( self, combo_token: str, @@ -17,7 +18,7 @@ def __init__( device_model: str, *, game_type: Optional[GameType] = None, - client_type: Optional[UserClientType] = UserClientType.Android, + client_type: Optional[UserClientType] = None, channel: Optional[UserChannel] = UserChannel.Official ): """ @@ -57,6 +58,13 @@ def __init__( "hkrpg_cn": GameType.StarRail }[bi] + if self._client_type is None: + self._client_type = UserClientType.Android + warnings.warn( + "In future versions, unspecified client_type will no longer be supported.", + UserWarning + ) + if self._game_type is None: self._game_type = detected_game_type else: @@ -64,7 +72,8 @@ def __init__( warnings.warn( "The program detected a difference between the GameType you entered and the GameType it detected. " "This time, it will use your input as the standard. So the data may be incorrect.”" - "Please pay attention to the GameType." + "Please pay attention to the GameType.", + SyntaxWarning ) def get_user_headers(self) -> dict: