新增gsrc

This commit is contained in:
Wuyi无疑 2023-03-10 23:40:41 +08:00
parent f66336a293
commit 74a20a54db
7 changed files with 274 additions and 4 deletions

View 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)

View 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))

View File

@ -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()

View File

@ -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

View File

@ -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)

View File

@ -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
View File

@ -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]