mirror of
https://github.com/KimigaiiWuyi/GenshinUID.git
synced 2025-05-07 20:45:49 +08:00
✨ 新增gsrc
This commit is contained in:
parent
f66336a293
commit
74a20a54db
14
GenshinUID/genshinuid_topup/__init__.py
Normal file
14
GenshinUID/genshinuid_topup/__init__.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
from gsuid_core.sv import SV
|
||||||
|
from gsuid_core.bot import Bot
|
||||||
|
from gsuid_core.models import Event
|
||||||
|
|
||||||
|
from .gs_topup import topup_
|
||||||
|
|
||||||
|
|
||||||
|
@SV('原神充值').on_fullmatch(('gsrc', '原神充值'))
|
||||||
|
async def send_qrcode_login(bot: Bot, ev: Event):
|
||||||
|
await bot.logger.info('开始执行[原神充值]')
|
||||||
|
goods_id = int(ev.text) if ev.text.isdigit() else None
|
||||||
|
if goods_id is None:
|
||||||
|
return await bot.send('请输入正确的商品编号(1~6), 例如原神充值6!')
|
||||||
|
await topup_(bot, ev.bot_id, ev.user_id, ev.group_id, ev.text)
|
93
GenshinUID/genshinuid_topup/gs_topup.py
Normal file
93
GenshinUID/genshinuid_topup/gs_topup.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import io
|
||||||
|
import base64
|
||||||
|
import asyncio
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
import qrcode
|
||||||
|
from gsuid_core.bot import Bot
|
||||||
|
from nonebot.log import logger
|
||||||
|
from qrcode import ERROR_CORRECT_L
|
||||||
|
from gsuid_core.segment import MessageSegment
|
||||||
|
|
||||||
|
from ..utils.mys_api import mys_api
|
||||||
|
from ..utils.database import get_sqla
|
||||||
|
from ..utils.error_reply import get_error
|
||||||
|
from ..gsuid_utils.api.mys.models import MysOrder
|
||||||
|
|
||||||
|
disnote = '''免责声明:
|
||||||
|
该充值接口由米游社提供,不对充值结果负责。
|
||||||
|
请在充值前仔细阅读米哈游的充值条款。
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def get_qrcode_base64(url: str):
|
||||||
|
qr = qrcode.QRCode(
|
||||||
|
version=1,
|
||||||
|
error_correction=ERROR_CORRECT_L,
|
||||||
|
box_size=10,
|
||||||
|
border=4,
|
||||||
|
)
|
||||||
|
qr.add_data(url)
|
||||||
|
qr.make(fit=True)
|
||||||
|
img = qr.make_image(fill_color='black', back_color='white')
|
||||||
|
img_byte = io.BytesIO()
|
||||||
|
img.save(img_byte, format='PNG') # type:ignore
|
||||||
|
img_byte = img_byte.getvalue()
|
||||||
|
return base64.b64encode(img_byte).decode()
|
||||||
|
|
||||||
|
|
||||||
|
async def refresh(order: MysOrder, uid: str) -> str:
|
||||||
|
times = 0
|
||||||
|
while True:
|
||||||
|
await asyncio.sleep(5)
|
||||||
|
order_status = await mys_api.check_order(order, uid)
|
||||||
|
if isinstance(order_status, int):
|
||||||
|
return get_error(order_status)
|
||||||
|
if order_status['status'] != 900:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return '支付成功'
|
||||||
|
times += 1
|
||||||
|
if times > 60:
|
||||||
|
return '支付超时'
|
||||||
|
|
||||||
|
|
||||||
|
async def topup_(
|
||||||
|
bot: Bot, bot_id: str, user_id: str, group_id: str, goods_id: int
|
||||||
|
):
|
||||||
|
sqla = get_sqla(bot_id)
|
||||||
|
uid = await sqla.get_bind_uid(user_id)
|
||||||
|
if uid is None:
|
||||||
|
return await bot.send('未绑定米游社账号')
|
||||||
|
fetchgoods_data = await mys_api.get_fetchgoods()
|
||||||
|
if isinstance(fetchgoods_data, int):
|
||||||
|
return await bot.send(get_error(fetchgoods_data))
|
||||||
|
if goods_id < len(fetchgoods_data):
|
||||||
|
goods_data = fetchgoods_data[goods_id]
|
||||||
|
else:
|
||||||
|
return await bot.send('商品不存在,最大为' + str(len(fetchgoods_data) - 1))
|
||||||
|
order = await mys_api.topup(uid, goods_data)
|
||||||
|
if isinstance(order, int):
|
||||||
|
logger.warning(f'[充值] {group_id} {user_id} 出错!')
|
||||||
|
return await bot.send(get_error(order))
|
||||||
|
try:
|
||||||
|
im = []
|
||||||
|
b64_data = get_qrcode_base64(order['encode_order'])
|
||||||
|
qrc = MessageSegment.image(b64_data)
|
||||||
|
im.append(MessageSegment.text(f'充值uid:{uid}'))
|
||||||
|
im.append(
|
||||||
|
MessageSegment.text(
|
||||||
|
f'商品名称:{goods_data["goods_name"]}×{goods_data["goods_unit"]}'
|
||||||
|
if int(goods_data['goods_unit']) > 0
|
||||||
|
else goods_data['goods_name']
|
||||||
|
),
|
||||||
|
)
|
||||||
|
im.append(MessageSegment.text(f'商品价格:{int(order["amount"])/100}'))
|
||||||
|
im.append(MessageSegment.text(f'订单号:{order["order_no"]}'))
|
||||||
|
im.append(MessageSegment.text(disnote))
|
||||||
|
im.append(MessageSegment.text(qrc))
|
||||||
|
return await bot.send(MessageSegment.node(im))
|
||||||
|
except Exception:
|
||||||
|
traceback.print_exc()
|
||||||
|
logger.warning(f'[充值] {group_id} 图片发送失败')
|
||||||
|
return await bot.send(await refresh(order, uid))
|
@ -116,4 +116,10 @@ bbs_Detailurl = BBS_URL + '/post/api/getPostFull?post_id={}'
|
|||||||
bbs_Shareurl = BBS_URL + '/apihub/api/getShareConf?entity_id={}&entity_type=1'
|
bbs_Shareurl = BBS_URL + '/apihub/api/getShareConf?entity_id={}&entity_type=1'
|
||||||
bbs_Likeurl = f'{BBS_URL}/apihub/sapi/upvotePost'
|
bbs_Likeurl = f'{BBS_URL}/apihub/sapi/upvotePost'
|
||||||
|
|
||||||
|
# 原神充值中心
|
||||||
|
fetchGoodsurl = f"{HK4_SDK_URL}/hk4e_cn/mdk/shopwindow/shopwindow/fetchGoods"
|
||||||
|
CreateOrderurl = f"{HK4_SDK_URL}/hk4e_cn/mdk/atropos/api/createOrder"
|
||||||
|
CheckOrderurl = f"{HK4_SDK_URL}/hk4e_cn/mdk/atropos/api/checkOrder"
|
||||||
|
PriceTierurl = f"{HK4_SDK_URL}/hk4e_cn/mdk/shopwindow/shopwindow/listPriceTier"
|
||||||
|
|
||||||
_API = locals()
|
_API = locals()
|
||||||
|
@ -591,3 +591,58 @@ class MysGameSwitch(TypedDict):
|
|||||||
switch_id: int
|
switch_id: int
|
||||||
is_public: bool
|
is_public: bool
|
||||||
switch_name: str
|
switch_name: str
|
||||||
|
|
||||||
|
|
||||||
|
class MysGoods(TypedDict):
|
||||||
|
goods_id: str
|
||||||
|
goods_name: str
|
||||||
|
goods_name_i18n_key: str
|
||||||
|
goods_desc: str
|
||||||
|
goods_desc_i18n_key: str
|
||||||
|
goods_type: Literal['Normal', 'Special']
|
||||||
|
goods_unit: str
|
||||||
|
goods_icon: str
|
||||||
|
currency: Literal['CNY']
|
||||||
|
price: str
|
||||||
|
symbol: Literal['¥']
|
||||||
|
tier_id: Literal['Tier_1']
|
||||||
|
bonus_desc: MysGoodsBonus
|
||||||
|
once_bonus_desc: MysGoodsBonus
|
||||||
|
available: bool
|
||||||
|
tips_desc: str
|
||||||
|
tips_i18n_key: str
|
||||||
|
battle_pass_limit: str
|
||||||
|
|
||||||
|
|
||||||
|
class MysGoodsBonus(TypedDict):
|
||||||
|
bonus_desc: str
|
||||||
|
bonus_desc_i18n_key: str
|
||||||
|
bonus_unit: int
|
||||||
|
bonus_goods_id: str
|
||||||
|
bonus_icon: str
|
||||||
|
|
||||||
|
|
||||||
|
class MysOrderCheck(TypedDict):
|
||||||
|
status: int # 900为成功
|
||||||
|
amount: str
|
||||||
|
goods_title: str
|
||||||
|
goods_num: str
|
||||||
|
order_no: str
|
||||||
|
pay_plat: Literal['alipay']
|
||||||
|
|
||||||
|
|
||||||
|
class MysOrder(TypedDict):
|
||||||
|
goods_id: str
|
||||||
|
order_no: str
|
||||||
|
currency: Literal['CNY']
|
||||||
|
amount: str
|
||||||
|
redirect_url: str
|
||||||
|
foreign_serial: str
|
||||||
|
encode_order: str
|
||||||
|
account: str # mysid
|
||||||
|
create_time: str
|
||||||
|
ext_info: str
|
||||||
|
balance: str
|
||||||
|
method: str
|
||||||
|
action: str
|
||||||
|
session_cookie: str
|
||||||
|
@ -19,6 +19,7 @@ from .tools import (
|
|||||||
random_text,
|
random_text,
|
||||||
get_ds_token,
|
get_ds_token,
|
||||||
generate_os_ds,
|
generate_os_ds,
|
||||||
|
gen_payment_sign,
|
||||||
get_web_ds_token,
|
get_web_ds_token,
|
||||||
generate_passport_ds,
|
generate_passport_ds,
|
||||||
)
|
)
|
||||||
@ -28,6 +29,8 @@ from .models import (
|
|||||||
MysSign,
|
MysSign,
|
||||||
RegTime,
|
RegTime,
|
||||||
GachaLog,
|
GachaLog,
|
||||||
|
MysGoods,
|
||||||
|
MysOrder,
|
||||||
SignInfo,
|
SignInfo,
|
||||||
SignList,
|
SignList,
|
||||||
AbyssData,
|
AbyssData,
|
||||||
@ -38,6 +41,7 @@ from .models import (
|
|||||||
CalculateInfo,
|
CalculateInfo,
|
||||||
DailyNoteData,
|
DailyNoteData,
|
||||||
GameTokenInfo,
|
GameTokenInfo,
|
||||||
|
MysOrderCheck,
|
||||||
CharDetailData,
|
CharDetailData,
|
||||||
CookieTokenInfo,
|
CookieTokenInfo,
|
||||||
LoginTicketInfo,
|
LoginTicketInfo,
|
||||||
@ -642,6 +646,90 @@ class MysApi:
|
|||||||
else:
|
else:
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
async def get_fetchgoods(self) -> Union[int, List[MysGoods]]:
|
||||||
|
data = {
|
||||||
|
'released_flag': True,
|
||||||
|
'game': 'hk4e_cn',
|
||||||
|
'region': 'cn_gf01',
|
||||||
|
'uid': '1',
|
||||||
|
'account': '1',
|
||||||
|
}
|
||||||
|
resp = await self._mys_request(
|
||||||
|
url=_API['fetchGoodsurl'],
|
||||||
|
method='POST',
|
||||||
|
data=data,
|
||||||
|
)
|
||||||
|
if isinstance(resp, int):
|
||||||
|
return resp
|
||||||
|
return cast(List[MysGoods], resp['data']['goods_list'])
|
||||||
|
|
||||||
|
async def topup(self, uid: str, goods: MysGoods) -> Union[int, MysOrder]:
|
||||||
|
device_id = str(uuid.uuid4())
|
||||||
|
HEADER = copy.deepcopy(_HEADER)
|
||||||
|
ck = await self.get_ck(uid, 'OWNER')
|
||||||
|
if ck is None:
|
||||||
|
return -51
|
||||||
|
HEADER['Cookie'] = ck
|
||||||
|
account = HEADER['Cookie'].split('account_id=')[1].split(';')[0]
|
||||||
|
order = {
|
||||||
|
'account': str(account),
|
||||||
|
'region': 'cn_gf01',
|
||||||
|
'uid': uid,
|
||||||
|
'delivery_url': '',
|
||||||
|
'device': device_id,
|
||||||
|
'channel_id': 1,
|
||||||
|
'client_ip': '',
|
||||||
|
'client_type': 4,
|
||||||
|
'game': 'hk4e_cn',
|
||||||
|
'amount': goods['price'],
|
||||||
|
# 'amount': 600,
|
||||||
|
'goods_num': 1,
|
||||||
|
'goods_id': goods['goods_id'],
|
||||||
|
'goods_title': f'{goods["goods_name"]}×{str(goods["goods_unit"])}'
|
||||||
|
if int(goods['goods_unit']) > 0
|
||||||
|
else goods['goods_name'],
|
||||||
|
'price_tier': goods['tier_id'],
|
||||||
|
# 'price_tier': 'Tier_1',
|
||||||
|
'currency': 'CNY',
|
||||||
|
'pay_plat': 'alipay',
|
||||||
|
}
|
||||||
|
data = {'order': order, 'sign': gen_payment_sign(order)}
|
||||||
|
HEADER['x-rpc-device_id'] = device_id
|
||||||
|
HEADER['x-rpc-client_type'] = '4'
|
||||||
|
resp = await self._mys_request(
|
||||||
|
url=_API['CreateOrderurl'],
|
||||||
|
method='POST',
|
||||||
|
header=HEADER,
|
||||||
|
data=data,
|
||||||
|
)
|
||||||
|
if isinstance(resp, int):
|
||||||
|
return resp
|
||||||
|
return cast(MysOrder, resp['data'])
|
||||||
|
|
||||||
|
async def check_order(
|
||||||
|
self, order: MysOrder, uid: str
|
||||||
|
) -> Union[int, MysOrderCheck]:
|
||||||
|
HEADER = copy.deepcopy(_HEADER)
|
||||||
|
ck = await self.get_ck(uid, 'OWNER')
|
||||||
|
if ck is None:
|
||||||
|
return -51
|
||||||
|
HEADER['Cookie'] = ck
|
||||||
|
data = {
|
||||||
|
'order_no': order['order_no'],
|
||||||
|
'game': 'hk4e_cn',
|
||||||
|
'region': 'cn_gf01',
|
||||||
|
'uid': uid,
|
||||||
|
}
|
||||||
|
resp = await self._mys_request(
|
||||||
|
url=_API['CheckOrderurl'],
|
||||||
|
method='GET',
|
||||||
|
header=HEADER,
|
||||||
|
params=data,
|
||||||
|
)
|
||||||
|
if isinstance(resp, int):
|
||||||
|
return resp
|
||||||
|
return cast(MysOrderCheck, resp['data'])
|
||||||
|
|
||||||
async def simple_mys_req(
|
async def simple_mys_req(
|
||||||
self,
|
self,
|
||||||
URL: str,
|
URL: str,
|
||||||
@ -657,7 +745,6 @@ class MysApi:
|
|||||||
server_id = RECOGNIZE_SERVER.get(uid[0])
|
server_id = RECOGNIZE_SERVER.get(uid[0])
|
||||||
is_os = False if int(uid[0]) < 6 else True
|
is_os = False if int(uid[0]) < 6 else True
|
||||||
ex_params = '&'.join([f'{k}={v}' for k, v in params.items()])
|
ex_params = '&'.join([f'{k}={v}' for k, v in params.items()])
|
||||||
print(ex_params)
|
|
||||||
if is_os:
|
if is_os:
|
||||||
_URL = _API[f'{URL}_OS']
|
_URL = _API[f'{URL}_OS']
|
||||||
HEADER = copy.deepcopy(_HEADER_OS)
|
HEADER = copy.deepcopy(_HEADER_OS)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import hmac
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
import random
|
import random
|
||||||
@ -76,3 +77,17 @@ def generate_os_ds(salt: str = '') -> str:
|
|||||||
|
|
||||||
def generate_passport_ds(q: str = '', b: Optional[Dict[str, Any]] = None):
|
def generate_passport_ds(q: str = '', b: Optional[Dict[str, Any]] = None):
|
||||||
return _random_str_ds(_S['PD'], string.ascii_letters, True, q, b)
|
return _random_str_ds(_S['PD'], string.ascii_letters, True, q, b)
|
||||||
|
|
||||||
|
|
||||||
|
def HMCASHA256(data: str, key: str):
|
||||||
|
key_bytes = key.encode('utf-8')
|
||||||
|
message = data.encode('utf-8')
|
||||||
|
sign = hmac.new(key_bytes, message, digestmod=hashlib.sha256).digest()
|
||||||
|
return sign.hex()
|
||||||
|
|
||||||
|
|
||||||
|
def gen_payment_sign(data):
|
||||||
|
data = dict(sorted(data.items(), key=lambda x: x[0]))
|
||||||
|
value = ''.join([str(i) for i in data.values()])
|
||||||
|
sign = HMCASHA256(value, '6bdc3982c25f3f3c38668a32d287d16b')
|
||||||
|
return sign
|
||||||
|
6
poetry.lock
generated
6
poetry.lock
generated
@ -1544,14 +1544,14 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "platformdirs"
|
name = "platformdirs"
|
||||||
version = "3.1.0"
|
version = "3.1.1"
|
||||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
{file = "platformdirs-3.1.0-py3-none-any.whl", hash = "sha256:13b08a53ed71021350c9e300d4ea8668438fb0046ab3937ac9a29913a1a1350a"},
|
{file = "platformdirs-3.1.1-py3-none-any.whl", hash = "sha256:e5986afb596e4bb5bde29a79ac9061aa955b94fca2399b7aaac4090860920dd8"},
|
||||||
{file = "platformdirs-3.1.0.tar.gz", hash = "sha256:accc3665857288317f32c7bebb5a8e482ba717b474f3fc1d18ca7f9214be0cef"},
|
{file = "platformdirs-3.1.1.tar.gz", hash = "sha256:024996549ee88ec1a9aa99ff7f8fc819bb59e2c3477b410d90a16d32d6e707aa"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user