Skip to content

Commit

Permalink
feat: Support the PCWeb client (#12)
Browse files Browse the repository at this point in the history
feat: Support the PCWeb client
  • Loading branch information
GuangChen2333 authored May 25, 2024
2 parents 1b74685 + e9bd1f6 commit eadd19d
Show file tree
Hide file tree
Showing 11 changed files with 114 additions and 56 deletions.
32 changes: 18 additions & 14 deletions docs/api/interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ type_: Optional[[NotificationType](#notificationtype)] = None, is_sort: Optional
>
> **形参:**
>
> - game_type ([GameType](#gametype)): 游戏类型。
> - game_type ([**GameType**](#gametype)): 游戏类型。
>
> **返回值:** `str` - 该游戏类型的版本号。
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -228,14 +232,14 @@ _class mhyy.UserClientType_

> 客户端类型。
!!! Note

目前 mhyy.py 仅支持模拟安卓设备操作。

`Android` = 2

> 安卓。
`PCWeb` = 16

> PC 网页版。
## WalletData

_class mhyy.WalletData_
Expand Down
7 changes: 4 additions & 3 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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 将会自动识别并进行操作。

在上述操作中,你成功完成了从**定义用户****获取钱包信息**再到**判断是否进行了签到操作**的过程。

Expand Down
19 changes: 19 additions & 0 deletions docs/usage/multi_client_support.md
Original file line number Diff line number Diff line change
@@ -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
)
```
12 changes: 9 additions & 3 deletions docs/usage/quick_start.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

## 定义一个客户端并进行客户端操作

这一步你并不需要准备任何东西,你只需要像这样:
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ nav:
- 简介: index.md
- 使用:
- 快速入门: usage/quick_start.md
- 多客户端支持: usage/multi_client_support.md
- API 参考:
- 开发人员接口: api/interface.md

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "mhyy.py"
version = "2.0.0"
version = "2.1.0"
authors = [
{ name = "GuangChen2333", email = "[email protected]" },
]
Expand Down
6 changes: 3 additions & 3 deletions src/mhyy/__init__.py
Original file line number Diff line number Diff line change
@@ -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
60 changes: 37 additions & 23 deletions src/mhyy/_api.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from ._types import GameType, UserChannel
from ._types import GameType, UserChannel, UserClientType


class API:
Expand All @@ -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:
Expand Down
16 changes: 9 additions & 7 deletions src/mhyy/_client.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions src/mhyy/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ class UserClientType(Enum):
Attributes:
Android: 安卓。
PCWeb: PC 网页版。
"""
Android = 2
PCWeb = 16


class UserChannel(Enum):
Expand Down
13 changes: 11 additions & 2 deletions src/mhyy/_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class User:
"""
用户类。
"""

def __init__(
self,
combo_token: str,
Expand All @@ -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
):
"""
Expand Down Expand Up @@ -57,14 +58,22 @@ 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:
if self._game_type != detected_game_type:
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:
Expand Down

0 comments on commit eadd19d

Please sign in to comment.