mirror of
https://github.com/xuthus83/LittlePaimon.git
synced 2024-10-21 16:27:15 +08:00
新增米游社商品兑换
功能,更新README
This commit is contained in:
parent
c3bedc282f
commit
7e39d3cadf
1
.gitignore
vendored
1
.gitignore
vendored
@ -146,6 +146,5 @@ nohup.out
|
||||
|
||||
# 忽略文件夹
|
||||
.history
|
||||
_Paimon_exchange/*
|
||||
user_data/gacha_log_data/*
|
||||
user_data/player_info/*
|
||||
|
158
Paimon_Exchange/__init__.py
Normal file
158
Paimon_Exchange/__init__.py
Normal file
@ -0,0 +1,158 @@
|
||||
import re
|
||||
from nonebot import on_command
|
||||
from nonebot.params import CommandArg, T_State, Arg
|
||||
from nonebot.adapters.onebot.v11 import PrivateMessageEvent, Message
|
||||
from .data_source import get_address, get_goods, save_exchange_info, get_exchange_info, delete_exchange_info
|
||||
|
||||
__paimon_help__ = {
|
||||
'type': '工具',
|
||||
'range': ['private']
|
||||
}
|
||||
|
||||
myb_exchange = on_command('myb', aliases={'米游币兑换', '米游币商品兑换', '米游社商品兑换'}, priority=4, block=True)
|
||||
myb_exchange.__paimon_help__ = {
|
||||
"usage": "myb",
|
||||
"introduce": "让派蒙帮你兑换米游币商品哦",
|
||||
"priority": 7
|
||||
}
|
||||
myb_info = on_command('myb_info', aliases={'米游币兑换信息', '米游币兑换计划'}, priority=4, block=True)
|
||||
myb_info.__paimon_help__ = {
|
||||
"usage": "myb_info",
|
||||
"introduce": "查看你的米游币兑换计划",
|
||||
"priority": 8
|
||||
}
|
||||
myb_delete = on_command('myb_delete', aliases={'米游币兑换删除', '米游币兑换取消'}, priority=4, block=True)
|
||||
myb_delete.__paimon_help__ = {
|
||||
"usage": "myb_delete",
|
||||
"introduce": "取消你的米游币兑换计划",
|
||||
"priority": 9
|
||||
}
|
||||
|
||||
|
||||
@myb_exchange.handle()
|
||||
async def _(event: PrivateMessageEvent, state: T_State, msg: Message = CommandArg()):
|
||||
if msg:
|
||||
msg = msg.extract_plain_text().strip()
|
||||
if '虚拟' in msg:
|
||||
state['商品类型'] = '虚拟'
|
||||
elif '实体' in msg:
|
||||
state['商品类型'] = '实体'
|
||||
state['uid'] = None
|
||||
|
||||
|
||||
@myb_exchange.got('商品类型', prompt='请给出要抢的商品类型(虚拟|实体),例如原石属于虚拟')
|
||||
async def _(event: PrivateMessageEvent, state: T_State, type: Message = Arg('商品类型')):
|
||||
type = type.extract_plain_text().strip()
|
||||
if '虚拟' in type:
|
||||
state['商品类型'] = '虚拟'
|
||||
print(state)
|
||||
elif '实体' in type:
|
||||
state['商品类型'] = '实体'
|
||||
state['uid'] = None
|
||||
else:
|
||||
await myb_exchange.reject('请给出要抢的商品类型(虚拟|实体),例如原石属于虚拟')
|
||||
|
||||
|
||||
@myb_exchange.got('uid', prompt='请把虚拟商品要兑换到的游戏uid告诉我')
|
||||
async def _(event: PrivateMessageEvent, state: T_State, uid: Message = Arg('uid')):
|
||||
uid = uid.extract_plain_text().strip()
|
||||
find_uid = re.search(r'(?P<uid>(1|2|5)\d{8})', uid)
|
||||
if find_uid:
|
||||
state['uid'] = find_uid.group('uid')
|
||||
else:
|
||||
await myb_exchange.reject('这不是有效的uid')
|
||||
|
||||
|
||||
@myb_exchange.got('cookie', prompt='请把米游币cookie给我,cookie获取方式详见:\ndocs.qq.com/doc/DQ3JLWk1vQVllZ2Z1')
|
||||
async def _(event: PrivateMessageEvent, state: T_State, cookie: Message = Arg('cookie')):
|
||||
cookie = cookie.extract_plain_text().strip()
|
||||
address = await get_address(cookie)
|
||||
if address is None:
|
||||
await myb_exchange.reject('这个cookie无效,请检查是否以按照正常方法获取')
|
||||
elif len(address) == 0:
|
||||
await myb_exchange.finish('你的账号还没有填写收货地址哦,请先去填写收货地址重新再来')
|
||||
else:
|
||||
state['cookie'] = cookie
|
||||
if len(address) == 1:
|
||||
state['address_id'] = address[0]
|
||||
else:
|
||||
state['address_list'] = address
|
||||
if state['商品类型'] == '虚拟':
|
||||
if 'login_ticket' not in cookie and 'stoken' not in cookie:
|
||||
await myb_exchange.reject('你的cookie中没有login_ticket字段哦,请尝试退出后重新登录再获取cookie')
|
||||
|
||||
|
||||
@myb_exchange.got('address_id', prompt='回复任意文字继续,接下来回复选择你的收货地址的ID')
|
||||
async def _(event: PrivateMessageEvent, state: T_State, address_id: Message = Arg('address_id')):
|
||||
address_id = address_id.extract_plain_text().strip()
|
||||
flag = False
|
||||
for add in state['address_list']:
|
||||
if address_id == add['id']:
|
||||
state['address_id'] = add
|
||||
flag = True
|
||||
break
|
||||
if not flag:
|
||||
address_list = ''
|
||||
for add in state['address_list']:
|
||||
address_list += f'ID:{add["id"]},{add["地址"]}\n'
|
||||
await myb_exchange.reject(f'请选择收货地址ID:\n{address_list}')
|
||||
|
||||
|
||||
@myb_exchange.got('game', prompt='请给出要抢的商品所属游戏名称,有崩坏3|原神|崩坏学园2|未定事件簿|米游社')
|
||||
async def _(event: PrivateMessageEvent, state: T_State, game: Message = Arg('game')):
|
||||
game = game.extract_plain_text().strip()
|
||||
if game in ['崩坏3', 'bh3', '崩崩崩', '三崩子']:
|
||||
state['goods_list'] = await get_goods('崩坏3')
|
||||
elif game in ['原神', 'ys']:
|
||||
state['goods_list'] = await get_goods('原神')
|
||||
elif game in ['崩坏学园2', 'bh2', '二崩子', '崩坏学院2', '崩崩']:
|
||||
state['goods_list'] = await get_goods('崩坏学园2')
|
||||
elif game in ['未定事件簿', 'wdsjb', '未定']:
|
||||
state['goods_list'] = await get_goods('未定事件簿')
|
||||
elif game in ['米游社', 'mys']:
|
||||
state['goods_list'] = await get_goods('米游社')
|
||||
else:
|
||||
await myb_exchange.reject('请给出要抢的商品所属游戏名称,有崩坏3|原神|崩坏学园2|未定事件簿|米游社')
|
||||
|
||||
|
||||
@myb_exchange.got('goods_search', prompt='请给出要兑换的商品名,或者其含有的关键词')
|
||||
async def _(event: PrivateMessageEvent, state: T_State, goods_search: Message = Arg('goods_search')):
|
||||
goods_search = goods_search.extract_plain_text().strip()
|
||||
match_goods = []
|
||||
for good in state['goods_list']:
|
||||
if goods_search in good['name']:
|
||||
match_goods.append(good)
|
||||
if len(match_goods) == 1:
|
||||
state['goods'] = match_goods[0]
|
||||
save_exchange_info(event.user_id, state)
|
||||
await myb_exchange.finish('完成')
|
||||
elif len(match_goods) > 1:
|
||||
state['goods_search_result'] = match_goods
|
||||
else:
|
||||
await myb_exchange.reject('没有相关可兑换的商品,请重新输入')
|
||||
|
||||
|
||||
@myb_exchange.got('goods', prompt='回复任意文字继续,接下来回复选择你想要兑换的商品的ID')
|
||||
async def _(event: PrivateMessageEvent, state: T_State, msg: Message = Arg('goods')):
|
||||
msg = msg.extract_plain_text().strip()
|
||||
for good in state['goods_search_result']:
|
||||
if msg == good['id']:
|
||||
state['goods'] = good
|
||||
save_exchange_info(event.user_id, state)
|
||||
await myb_exchange.finish('派蒙记住啦!到时候会帮你兑换,发送 myb_info 可以再次确认兑换信息,发送 myb_delete 可以取消兑换计划')
|
||||
good_str = ''
|
||||
for good in state['goods_search_result']:
|
||||
good_str += f'ID:{good["id"]}, 商品名:{good["name"]}\n'
|
||||
await myb_exchange.reject('请选择商品ID:\n'+good_str)
|
||||
|
||||
|
||||
@myb_info.handle()
|
||||
async def _(event: PrivateMessageEvent):
|
||||
info = get_exchange_info(str(event.user_id))
|
||||
await myb_info.finish(info)
|
||||
|
||||
|
||||
@myb_delete.handle()
|
||||
async def _(event: PrivateMessageEvent):
|
||||
delete_exchange_info(str(event.user_id))
|
||||
await myb_delete.finish('米游币兑换计划已全部取消')
|
226
Paimon_Exchange/data_source.py
Normal file
226
Paimon_Exchange/data_source.py
Normal file
@ -0,0 +1,226 @@
|
||||
import datetime
|
||||
import random
|
||||
import time
|
||||
import json
|
||||
import string
|
||||
import re
|
||||
from pathlib import Path
|
||||
from asyncio import sleep
|
||||
from nonebot import require, get_bot, get_driver
|
||||
from utils import aiorequests
|
||||
from utils.file_handler import load_json, save_json
|
||||
|
||||
require('nonebot_plugin_apscheduler')
|
||||
from nonebot_plugin_apscheduler import scheduler
|
||||
|
||||
driver = get_driver()
|
||||
|
||||
|
||||
async def get_address(cookie):
|
||||
address_url = 'https://api-takumi.mihoyo.com/account/address/list'
|
||||
header = {
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'Accept-Encoding': 'gzip, deflate, br',
|
||||
'Accept-Language': 'zh-CN,zh-Hans;q=0.9',
|
||||
'Connection': 'keep-alive',
|
||||
'Cookie': cookie,
|
||||
'Host': 'api-takumi.mihoyo.com',
|
||||
'Origin': 'https://user.mihoyo.com',
|
||||
'Referer': 'https://user.mihoyo.com/',
|
||||
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/2.25.1'
|
||||
}
|
||||
res = await aiorequests.get(url=address_url, headers=header)
|
||||
res = res.json()
|
||||
if res['message'] == 'OK':
|
||||
address = res['data']['list']
|
||||
add_list = []
|
||||
for add in address:
|
||||
add_list.append({'id': add['id'],
|
||||
'地址': f'姓名:{add["connect_name"]} 电话:{add["connect_mobile"]} 地址:{add["province_name"] + add["city_name"] + add["county_name"] + add["addr_ext"]}'})
|
||||
return add_list
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
async def get_goods(game):
|
||||
game_type = {'崩坏3': 'bh3', '原神': 'hk4e', '崩坏学园2': 'bh2', '未定事件簿': 'nxx', '米游社': 'bbs'}
|
||||
url = 'https://api-takumi.mihoyo.com/mall/v1/web/goods/list?app_id=1&point_sn=myb&page_size=20&page={page}&game=' + \
|
||||
game_type[game]
|
||||
goods_list = []
|
||||
goods_list_new = []
|
||||
page = 1
|
||||
while True:
|
||||
res = await aiorequests.get(url=url.format(page=page))
|
||||
res = res.json()
|
||||
if not res['data']['list']:
|
||||
break
|
||||
else:
|
||||
goods_list += res['data']['list']
|
||||
page += 1
|
||||
|
||||
for good in goods_list:
|
||||
if good['next_time'] == 0 and good['type'] == 1:
|
||||
continue
|
||||
good_info = {}
|
||||
good_info['id'] = good['goods_id']
|
||||
good_info['name'] = good['goods_name']
|
||||
good_info['price'] = good['price']
|
||||
good_info['time'] = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(good['next_time']))
|
||||
goods_list_new.append(good_info)
|
||||
|
||||
return goods_list_new
|
||||
|
||||
|
||||
def save_exchange_info(user_id, state):
|
||||
info = {}
|
||||
info['user_id'] = user_id
|
||||
info['类型'] = state['商品类型']
|
||||
info['cookie'] = state['cookie']
|
||||
info['商品'] = state['goods']
|
||||
info['地址'] = state['address_id']
|
||||
if info['类型'] == '虚拟':
|
||||
info['uid'] = state['uid']
|
||||
|
||||
t = f"{user_id}-{info['商品']['id']}"
|
||||
|
||||
path = Path() / 'data' / 'LittlePaimon' / 'myb_exchange' / f'{t}.json'
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
save_json(data=info, path=path)
|
||||
|
||||
scheduler.add_job(
|
||||
id=t,
|
||||
replace_existing=True,
|
||||
misfire_grace_time=5,
|
||||
func=exchange_action,
|
||||
trigger='date',
|
||||
args=(info,),
|
||||
next_run_time=datetime.datetime.strptime(info['商品']['time'], '%Y-%m-%d %H:%M:%S')
|
||||
)
|
||||
|
||||
|
||||
async def get_bbs_info(info, headers):
|
||||
url = 'https://api-takumi.mihoyo.com/binding/api/getUserGameRolesByCookie'
|
||||
res = (await aiorequests.get(url=url, headers=headers)).json()
|
||||
if res['retcode'] == 0:
|
||||
data = res['data']['list']
|
||||
for d in data:
|
||||
if d['game_uid'] == info['uid']:
|
||||
return d['game_biz'], d['region']
|
||||
return None, None
|
||||
|
||||
|
||||
async def get_stoken(cookie):
|
||||
bbs_cookie_url = 'https://webapi.account.mihoyo.com/Api/cookie_accountinfo_by_loginticket?login_ticket={}'
|
||||
bbs_cookie_url2 = 'https://api-takumi.mihoyo.com/auth/api/getMultiTokenByLoginTicket?login_ticket={}&token_types=3&uid={}'
|
||||
|
||||
login_ticket = re.search('login_ticket=[a-zA-Z0-9]{0,100}', cookie)
|
||||
data = (await aiorequests.get(url=bbs_cookie_url.format(login_ticket.group().split('=')[1]))).json()
|
||||
if '成功' in data['data']['msg']:
|
||||
stuid = data['data']['cookie_info']['account_id']
|
||||
data2 = (await aiorequests.get(url=bbs_cookie_url2.format(login_ticket.group().split('=')[1], stuid))).json()
|
||||
return data2['data']['list'][0]['token']
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
async def exchange_action(info):
|
||||
url = 'https://api-takumi.mihoyo.com/mall/v1/web/goods/exchange'
|
||||
headers = {
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'Accept-Encoding': 'gzip, deflate, br',
|
||||
'Accept-Language': 'zh-CN,zh-Hans;q=0.9',
|
||||
'Connection': 'keep-alive',
|
||||
# 'Content-Length': '88',
|
||||
'Content-Type': 'application/json;charset=utf-8',
|
||||
'Cookie': info['cookie'],
|
||||
'Host': 'api-takumi.mihoyo.com',
|
||||
'Origin': 'https://webstatic.mihoyo.com',
|
||||
'Referer': 'https://webstatic.mihoyo.com/',
|
||||
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_1 like Mac OS X) AppleWebKit/605.1.15 (KHtimeL, like Gecko) miHoYoBBS/2.14.1',
|
||||
'x-rpc-app_version': '2.14.1',
|
||||
'x-rpc-channel': 'appstore',
|
||||
'x-rpc-client_type': '1',
|
||||
'x-rpc-device_id': ''.join(random.sample(string.ascii_letters + string.digits, 32)).upper(),
|
||||
'x-rpc-device_model': 'iPhone10,2',
|
||||
'x-rpc-device_name': ''.join(random.sample(string.ascii_letters + string.digits, random.randrange(5))).upper(),
|
||||
'x-rpc-sys_version': '15.1'
|
||||
}
|
||||
data = {
|
||||
"app_id": 1,
|
||||
"point_sn": "myb",
|
||||
"goods_id": info['商品']['id'],
|
||||
"exchange_num": 1,
|
||||
"address_id": info['地址']['id']
|
||||
}
|
||||
if info['类型'] == '虚拟':
|
||||
game_biz, region = await get_bbs_info(info, headers)
|
||||
data['uid'] = info['uid']
|
||||
data['game_biz'] = game_biz
|
||||
data['region'] = region
|
||||
if 'stoken' not in info['cookie']:
|
||||
stoken = await get_stoken(info['cookie'])
|
||||
info['cookie'] += f'stoken={stoken};'
|
||||
|
||||
exchange_url = 'https://api-takumi.mihoyo.com/mall/v1/web/goods/exchange'
|
||||
flag = False
|
||||
for _ in range(3):
|
||||
exchange_res = (await aiorequests.post(url=exchange_url, headers=headers, json=data)).json()
|
||||
if exchange_res['retcode'] == 0:
|
||||
await get_bot().send_private_msg(user_id=info['user_id'],
|
||||
message=f'你的米游币商品{info["商品"]["name"]}的兑换成功,结果为:\n{exchange_res["message"]}')
|
||||
flag = True
|
||||
break
|
||||
await sleep(0.2)
|
||||
try:
|
||||
if not flag:
|
||||
await get_bot().send_private_msg(user_id=info['user_id'],
|
||||
message=f'你的米游币商品{info["商品"]["name"]}兑换失败:\n{exchange_res["message"]}')
|
||||
except:
|
||||
pass
|
||||
|
||||
path = Path() / 'data' / 'LittlePaimon' / 'myb_exchange' / f"{info['user_id']}-{info['商品']['id']}.json"
|
||||
path.unlink()
|
||||
|
||||
|
||||
@driver.on_startup
|
||||
async def _():
|
||||
path = Path() / 'data' / 'LittlePaimon' / 'myb_exchange'
|
||||
for exchange_data in path.iterdir():
|
||||
info = load_json(path=exchange_data)
|
||||
t = str(exchange_data).replace('data\\LittlePaimon\\myb_exchange\\', '').replace('data/LittlePaimon/myb_exchange/', '').replace(
|
||||
'.json', '')
|
||||
scheduler.add_job(
|
||||
id=t,
|
||||
replace_existing=True,
|
||||
misfire_grace_time=5,
|
||||
func=exchange_action,
|
||||
trigger='date',
|
||||
args=(info,),
|
||||
next_run_time=datetime.datetime.strptime(info['商品']['time'], '%Y-%m-%d %H:%M:%S')
|
||||
)
|
||||
|
||||
|
||||
def get_exchange_info(user_id):
|
||||
result = ''
|
||||
path = Path() / 'data' / 'LittlePaimon' / 'myb_exchange'
|
||||
i = 1
|
||||
for exchange_data in path.iterdir():
|
||||
file_name = str(exchange_data).replace('data\\LittlePaimon\\myb_exchange\\', '').replace('data/LittlePaimon/myb_exchange/', '')
|
||||
if file_name.startswith(user_id):
|
||||
info = load_json(path=exchange_data)
|
||||
result += f"{i}.{info['商品']['name']} {info['商品']['time']}\n"
|
||||
if info['类型'] == '虚拟':
|
||||
result += f"兑换至uid{info['uid']}\n"
|
||||
else:
|
||||
result += f"兑换至{info['地址']['地址']}\n"
|
||||
i += 1
|
||||
return result or '你还没有米游币兑换计划哦'
|
||||
|
||||
|
||||
def delete_exchange_info(user_id):
|
||||
path = Path() / 'data' / 'LittlePaimon' / 'myb_exchange'
|
||||
for exchange_data in path.iterdir():
|
||||
file_name = str(exchange_data).replace('data\\LittlePaimon\\myb_exchange\\', '').replace('data/LittlePaimon/myb_exchange/', '')
|
||||
if file_name.startswith(user_id):
|
||||
exchange_data.unlink()
|
@ -151,13 +151,15 @@ javascript:(function(){prompt(document.domain,document.cookie)})();
|
||||
- [NoneBot2](https://github.com/nonebot/nonebot2) - 跨平台异步机器人框架
|
||||
- [go-cqhttp](https://github.com/Mrs4s/go-cqhttp) - Onebot标准的框架实现
|
||||
- [西北一枝花](https://github.com/Nwflower) - 美工大大和武器攻略图提供
|
||||
- [nicklly](https://github.com/nicklly) - 原神日历、云原神等功能贡献者
|
||||
- [nicklly](https://github.com/nicklly) 、[SCU_OP](https://github.com/SCUOP) - PR贡献者们
|
||||
- [egenshin](https://github.com/pcrbot/erinilis-modules/tree/master/egenshin) - 抽卡和猜语音代码、资源参考
|
||||
- [bluemushoom](https://bbs.nga.cn/nuke.php?func=ucp&uid=62861898) - 全角色收益曲线和参考面板攻略图来源
|
||||
- [genshin-gacha-export](https://github.com/sunfkny/genshin-gacha-export) - 抽卡记录导出代码参考
|
||||
- [GenshinUID](https://github.com/KimigaiiWuyi/GenshinUID) - 部分map资源来源
|
||||
- [Pallas-Bot](https://github.com/InvoluteHell/Pallas-Bot/tree/master/src/plugins/repeater) - 群聊记录发言学习代码参考
|
||||
- [西风驿站](https://bbs.mihoyo.com/ys/collection/307224) - 角色攻略一图流来源
|
||||
- [游创工坊](https://space.bilibili.com/176858937) - 深渊排行榜数据来源
|
||||
- [Enka Network](https://enka.shinshin.moe/) - 角色面板查询数据来源
|
||||
|
||||
## 丨赞助
|
||||
- 如果本项目对你有帮助,给个star~~求求啦
|
||||
@ -173,7 +175,7 @@ javascript:(function(){prompt(document.domain,document.cookie)})();
|
||||
| 永远的皇珈骑士 | 30 |
|
||||
| 小兔和鹿 | 30 |
|
||||
| el psy congroo | 20 |
|
||||
| Why U Bully Me | 30 |
|
||||
| SCU_OP | 30 |
|
||||
| 南絮ヽ | 20 |
|
||||
| 夜空koi我老婆 | 30 |
|
||||
## 丨其他
|
||||
|
Loading…
Reference in New Issue
Block a user