From d9d04528c3eb59195da9ec5b0b02afa0d54c217f Mon Sep 17 00:00:00 2001 From: CMHopeSunshine <277073121@qq.com> Date: Mon, 11 Apr 2022 21:18:06 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E7=94=A8=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E5=AD=98=E5=82=A8=EF=BC=8C=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=EF=BC=8C=E6=B3=A8=E6=84=8F=E7=9C=8B=E9=87=8D=E8=A6=81=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=EF=BC=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 98 ++----- .../Genshin_Paimon/abyss_info/__init__.py | 68 ++--- .../Genshin_Paimon/daily_note/__init__.py | 48 ++-- hoshino/modules/Genshin_Paimon/db_util.py | 244 ++++++++++++++++++ .../modules/Genshin_Paimon/gacha/__init__.py | 20 +- .../gacha_log_export/__init__.py | 105 ++------ .../Genshin_Paimon/game_guild/__init__.py | 11 +- hoshino/modules/Genshin_Paimon/get_data.py | 121 +++++---- .../Genshin_Paimon/monthinfo/__init__.py | 71 ++--- .../Genshin_Paimon/player_card/__init__.py | 153 ++++------- .../Genshin_Paimon/player_card/get_img.py | 5 +- .../Genshin_Paimon/user_data/__init__.py | 50 +++- hoshino/modules/Genshin_Paimon/util.py | 235 ++++++++--------- hoshino/modules/botmanage/group_invite.py | 2 +- 14 files changed, 655 insertions(+), 576 deletions(-) create mode 100644 hoshino/modules/Genshin_Paimon/db_util.py diff --git a/README.md b/README.md index 635be12..0e8db80 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,11 @@ -## 简介 +## 简介✨ 通过米游社接口,查询uid的游戏信息,并附带各种娱乐功能。 -## 功能示例 +## 功能示例💖 ys @@ -61,98 +61,36 @@ 抽卡记录 gachalog +## 功能列表 -## 指令列表 +详见[WIKI](https://github.com/CMHopeSunshine/LittlePaimon/wiki/%E5%8A%9F%E8%83%BD%E5%88%97%E8%A1%A8),向派蒙发送`#帮助派蒙`可以查看指令列表 -### 查询功能 +## 重要通知⚠️ -以下指令会记录上一次查询的uid,因此只需第一次查询时写上uid即可。 +4.11对代码进行了一次较大幅度的重构,cookie数据存储方式改用了`sqlite`数据库,原`json`数据会在首次启动时自动导入数据库;如果您对本项目代码有修改,请确保`git pull`时能解决冲突,目前测试未有BUG,如有请发起issue,且注意备份用户数据! -| 指令 | 介绍 | 备注 | -| ----------------- | --------------------------------------- | :----------------------------------------------------------- | -| ys uid | 查询uid的个人信息卡片 | | -| ysa uid | 查询uid拥有的角色和武器 | 没绑cookie则只显示8个 | -| ysc uid 角色名 | 查询uid指定角色的信息 | 没绑cookie则只能查公开的8个,且不显示天赋;支持角色别名 | -| ysb cookie | 绑定私人cookie到qq号 | 建议使用私聊绑定 | -| 添加公共ck cookie | 添加cookie到公共cookie池 | 需要添加至少一个公共cookie才能使用查询功能,每个cookie每日查询上限30次 | -| sy uid (层数) | 查询uid的深渊信息 | 绑定私人cookie后才能查看具体层数信息 | -| ssbq uid | 查询uid的实时便签,包括树脂、派遣情况等 | uid必须绑定了对应私人cookie才能使用 | -| myzj uid (月份) | 查询uid的该月札记 | uid必须绑定了对应私人cookie才能使用,不写月份时默认为本月,只能看最近3个月 | - -### 抽卡记录导出和分析 - -| 指令 | 介绍 | 备注 | -| --------------------- | -------------------------------- | -------------------------------------- | -| 查看抽卡记录 uid 池子 | 查看uid已有的抽卡记录的分析图片 | 池子有all\|角色\|武器\|常驻,默认为all | -| 获取抽卡记录 uid 链接 | 从api获取抽卡记录,时间较长 | | -| 导出抽卡记录 uid 格式 | 导出抽卡记录的文件,上传到群文件 | 格式有xlsx和json;只能在群里导出 | - -抽卡记录链接的获取方式和其他工具是一样的,这里不多介绍了; - -本派蒙导出的xlsx和json符合UIGF标准,可用于其他UIGF标准的抽卡记录分析工具。 - -### 模拟抽卡功能 - -| 指令 | 介绍 | 备注 | -| -------------------------- | ---------------------------------- | ------------------------------------------------------------ | -| 抽n十连xx | 模拟抽n个xx池子的十连 | n必须为阿拉伯数字,最多同时5次;xx池子有角色1\|角色2\|武器\|常驻\|彩蛋,可以DIY池子 | -| 选择定轨 武器名称 | 武器定轨 | 武器名必须是全称 | -| 查看定轨 | 查看当前定轨的武器和能量值 | | -| 删除定轨 | 删除定轨 | | -| 查看模拟抽卡记录 | 查看模拟抽卡的出货率、保底数等信息 | | -| 查看模拟抽卡记录 角色/武器 | 查看模拟抽卡抽到的角色/武器列表 | | -| 删除模拟抽卡记录 | 清空自己的模拟抽卡记录 | | - -### 原神WIKI - -| 指令 | 介绍 | 备注 | -| ---------- | ----------------------------------- | -------- | -| xx角色攻略 | 查看西风驿站出品的角色攻略一图流 | 支持别名 | -| xx角色材料 | 查看我出品的角色材料一图流 | 支持别名 | -| xx参考面板 | 查看bluemushoom出品的角色参考面板图 | 支持别名 | -| xx收益曲线 | 查看bluemushoom出品的角色收益曲线图 | 支持别名 | - -### 米游币帮兑功能 - -私聊机器人回复```米游币兑换```,跟着派蒙的提示步骤来使用。 - -### 派蒙语音功能 - -> 发送语音功能需要额外安装FFmpeg,请自行安装 - -群聊关键词可能会触发派蒙语音哦,尝试发送`诶嘿、大佬、羡慕`等词吧! - -### 头像表情包制作 - -| 指令 | 介绍 | 备注 | 例子 | -| ------------------------------------------------------------ | --------------------------- | :-------- | -------------- | -| #亲亲/贴贴/拍拍/给爷爬/吃掉/扔掉/撕掉/精神支柱/要我一直 @人/qq号/图片 | 好玩的头像图片gif表情包生成 | 要以#开头 | #精神支柱@群主 | - -## 新功能更新 +## 新功能更新😙 - 3.20 新增Windows一键部署脚本 - 3.22 新增蓝佬授权提供的收益曲线和参考面板攻略图 - 3.24 新增抽卡记录导出和分析功能,原模拟抽卡的指令更改 - 3.30 个人信息卡片新增层岩巨渊和神里绫人信息 - 3.31 实时便签加入参量质变仪信息 +- 4.11 改用数据库进行数据存储,优化代码 -## 已知问题\待优化 -- [ ] 公共cookie达到每日30次上限时不会更换 -- [ ] 公共cookie没有复用 -- [ ] 指令参数判别不够清晰 - -## ToDo +## ToDo🕛 - [ ] 实时便签树脂提醒 - [x] 抽卡记录导出和分析 - [ ] ocr圣遗物评分和角色面板记录 -- [ ] 角色、武器和圣遗物wiki(进度30%) +- [ ] 角色、武器和圣遗物wiki - [ ] 派蒙AI闲聊 -- [ ] 米游社自动签到(进度70%) +- [ ] 米游社自动签到 - [ ] 今日可刷材料 - [ ] 角色练度统计 +- [ ] 派蒙戳一戳集卡 -## 部署方法 +## 部署方法🖥️ > 本项目和HoshinoBot的部署方式一样,因此Linux可以参考: > @@ -160,6 +98,8 @@ ### 一键安装脚本 +⚠️一键脚本会因计算机环境不一样而可能产生各种各样的问题,出现问题的话请尝试手动部署,部署方法参考HoshinoBot + #### Windows 在你想安装的位置打开`powershell`,输入执行: @@ -179,13 +119,13 @@ javascript:(function(){prompt(document.domain,document.cookie)})(); #### Linux -代补充... +待补充...懒 -## 额外说明 +## 额外说明🗝️ -本项目也可作为HoshinoBot的插件来使用,移植`hoshino/modules`内模块即可,不过对HoshinoBot有所魔改,报错时查看修改一下代码即可。 +本项目也可作为HoshinoBot的插件来使用,移植`hoshino\modules`内模块即可,另外还需在`hoshino\util\__init__.py`中添加`PriFreqLimiter`方法用于模拟抽卡和派蒙聊天的冷却限制。 -## 感谢 +## 感谢❤️ 代码水平很烂,站在巨人的肩膀上努力学习ing...... diff --git a/hoshino/modules/Genshin_Paimon/abyss_info/__init__.py b/hoshino/modules/Genshin_Paimon/abyss_info/__init__.py index 854b18a..58e1813 100644 --- a/hoshino/modules/Genshin_Paimon/abyss_info/__init__.py +++ b/hoshino/modules/Genshin_Paimon/abyss_info/__init__.py @@ -1,56 +1,40 @@ import json,os,re from hoshino import R,MessageSegment,logger, Service from hoshino.typing import CQEvent, Message -from ..util import get_uid_by_qq, get_cookie, update_last_query_to_qq +from ..util import get_uid_in_msg from ..get_data import get_abyss_data from .get_img import draw_abyss_card -sv = Service('原神深渊查询') +help_msg=''' +[sy/深渊查询/深境螺旋查询 (uid) (层数)]查询深渊战绩信息 +*绑定私人cookie之后才能查看层数具体阵容哦 +''' +sv = Service('派蒙深渊查询', bundle='派蒙', help_=help_msg) @sv.on_prefix(('sy','深渊查询','深境螺旋查询')) async def main(bot,ev): - msg = ev.message.extract_plain_text().strip().split(' ') - uid = '' - if len(msg[0]) == 9 and msg[0].isdigit(): - uid = msg[0] - del msg[0] - if not msg: - abyss_floor = [] - else: - abyss_floor = msg - abyss_floor_true = [] - for floor in abyss_floor: - if floor.isdigit() and (int(floor) <= 12 and int(floor) >= 9): - abyss_floor_true.append(int(floor)) - abyss_floor_true.sort() - if len(abyss_floor_true)>2: - abyss_floor_true = [abyss_floor_true[0],abyss_floor_true[1]] - qq = str(ev.user_id) - # nickname = ev['sender']['nickname'] - if ev.message_type == 'guild': - rm = str(ev.message) - else: - rm = str(ev.raw_message) - match = re.search(r"\[CQ:at,qq=(.*)\]", rm) - if match: - uid = '' - qq = str(match.group(1)) + uid, msg, user_id, use_cache = await get_uid_in_msg(ev) if not uid: - uid = get_uid_by_qq(qq) - if not uid: - await bot.send(ev,'请把uid给派蒙哦,比如sy100000001',at_sender=True) - return - cookie = await get_cookie(qq, uid) - update_last_query_to_qq(qq, uid) - if not cookie: - await bot.send(ev,'当前cookie池中没有可用的cookie,请联系开发者',at_sender=True) + await bot.send(ev,'请把正确的uid给派蒙哦,例如sy100123456!',at_sender=True) + return + if not msg: + floor = [] else: - try: - data = await get_abyss_data(uid, cookie) - abyss_card = await draw_abyss_card(data, uid, abyss_floor_true) - await bot.send(ev,abyss_card,at_sender=True) - except Exception as e: - await bot.send(ev, f'派蒙出现了问题:{e}',at_sender=True) + floor = msg.split(' ') + true_floor = [] + for f in floor: + if f.isdigit() and (9 <= int(f) <=12) and len(true_floor) < 2: + true_floor.append(int(f)) + true_floor.sort() + # try: + data = await get_abyss_data(user_id, uid, use_cache=use_cache) + if isinstance(data, str): + await bot.send(ev, data, at_sender=True) + else: + abyss_card = await draw_abyss_card(data, uid, true_floor) + await bot.send(ev, abyss_card, at_sender=True) + # except Exception as e: + # await bot.send(ev, f'派蒙出现了问题:{e}',at_sender=True) diff --git a/hoshino/modules/Genshin_Paimon/daily_note/__init__.py b/hoshino/modules/Genshin_Paimon/daily_note/__init__.py index 94ddd29..1def99e 100644 --- a/hoshino/modules/Genshin_Paimon/daily_note/__init__.py +++ b/hoshino/modules/Genshin_Paimon/daily_note/__init__.py @@ -2,43 +2,25 @@ import json,os,re from hoshino import R,MessageSegment,logger, Service from hoshino.typing import CQEvent, Message from hoshino.util import filt_message -from ..util import get_uid_by_qq, get_cookie, check_uid_by_qq, update_last_query_to_qq +from ..util import get_uid_in_msg from ..get_data import get_daily_note_data from .get_img import draw_daily_note_card -sv = Service('原神实时便签') +help_msg=''' +[ssbq/实时便签 (uid)]查询当前树脂、洞天宝钱、派遣状况等 +*绑定私人cookie之后才能使用 +''' +sv = Service('派蒙实时便签', bundle='派蒙', help_=help_msg) @sv.on_prefix(('ssbq','实时便笺','实时便签')) async def main(bot,ev): - uid = ev.message.extract_plain_text().strip() - qq = str(ev.user_id) - if ev.message_type == 'guild': - rm = str(ev.message) + uid, msg, user_id, use_cache = await get_uid_in_msg(ev) + # try: + data = await get_daily_note_data(uid) + if isinstance(data, str): + await bot.send(ev, data, at_sender=True) else: - rm = str(ev.raw_message) - match = re.search(r"\[CQ:at,qq=(.*)\]", rm) - if match: - uid = '' - qq = str(match.group(1)) - if uid and not check_uid_by_qq(qq, uid): - await bot.send(ev,'派蒙没有这个uid的绑定信息哦',at_sender=True) - return - if not uid: - uid = get_uid_by_qq(qq) - if not uid: - await bot.send(ev,'你还没把信息绑定给派蒙哦',at_sender=True) - return - if len(uid) != 9 or not uid.isdigit(): - await bot.send(ev,f'uid {filt_message(uid)} 不合规,是不是打错了呀',at_sender=True) - return - cookie = await get_cookie(qq, uid, only_private = True, only_match_uid = True) - update_last_query_to_qq(qq, uid) - if not cookie: - await bot.send(ev,'你没有绑定cookie或者cookie失效了噢!',at_sender=True) - else: - try: - data = await get_daily_note_data(uid, cookie) - daily_note_card = await draw_daily_note_card(data, uid) - await bot.send(ev, daily_note_card, at_sender=True) - except Exception as e: - await bot.send(ev, f'派蒙出现了问题:{e}',at_sender=True) + daily_note_card = await draw_daily_note_card(data, uid) + await bot.send(ev, daily_note_card, at_sender=True) + # except Exception as e: + # await bot.send(ev, f'派蒙出现了问题:{e}',at_sender=True) diff --git a/hoshino/modules/Genshin_Paimon/db_util.py b/hoshino/modules/Genshin_Paimon/db_util.py new file mode 100644 index 0000000..c83417d --- /dev/null +++ b/hoshino/modules/Genshin_Paimon/db_util.py @@ -0,0 +1,244 @@ +import sqlite3 +import json +import os +import re +from hoshino import logger +from datetime import datetime +db_path = os.path.join(os.path.dirname(__file__), 'user_data', 'user_data.db') + +# 重载公共cookie +def reload_public_cookie(is_drop=True): + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + if is_drop: + cursor.execute('DROP TABLE IF EXISTS public_cookies;') + cursor.execute('''CREATE TABLE IF NOT EXISTS public_cookies + ( + no int PRIMARY KEY + cookie TEXT, + status TEXT, + );''') + try: + with open(os.path.join(os.path.dirname(__file__), 'user_data', 'user_cookies.json'), 'r', encoding='utf-8') as f: + data = json.load(f) + for d in data['通用']: + cursor.execute('INSERT IGNORE INTO public_cookies VALUES (?, ?, "OK");', (d['no'], d['cookie'])) + conn.commit() + conn.close() + logger.info('---公共cookie池载入成功!---') + return f'公共cookie池载入成功,共载入{len(data)}条cookie' + except Exception as e: + logger.error(f'---公共cookie池载入失败,错误:{e}---') + return f'公共cookie池载入失败,错误:{e}' + +# 初始化数据库,将原json数据导入数据库 +def init_db(): + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + cursor.execute('SELECT NAME FROM sqlite_master WHERE TYPE="table" and NAME="private_cookies"') + if not cursor.fetchone(): + cursor.execute('''CREATE TABLE private_cookies + ( + user_id TEXT NOT NULL, + uid TEXT NOT NULL, + mys_id TEXT, + cookie TEXT, + stoken TEXT, + PRIMARY KEY (user_id, uid) + );''') + cursor.execute('DROP TABLE IF EXISTS last_query;') + cursor.execute('''CREATE TABLE last_query + ( + user_id TEXT PRIMARY KEY NOT NULL, + uid TEXT, + mys_id TEXT, + last_time datetime + );''') + try: + with open(os.path.join(os.path.dirname(__file__), 'user_data', 'user_cookies.json'), 'r', encoding='utf-8') as f: + data = json.load(f) + for d in data['私人'].items(): + for c in d[1]['cookies']: + match = re.search(r'account_id=(\d{6,12})', c['cookie']) + mys_id = match.group(1) if match else '' + cursor.execute('INSERT INTO private_cookies (user_id, uid, mys_id, cookie) VALUES (?, ?, ?, ?);', (d[0], c['uid'], mys_id, c['cookie'])) + cursor.execute('INSERT INTO last_query (user_id, uid, last_time) VALUES (?, ?, ?);', (d[0], d[1]['last_query'], datetime.now().strftime('%Y-%m-%d %H:%M:%S'))) + cursor.execute('''CREATE TABLE IF NOT EXISTS public_cookies ( + no int IDENTITY(1,1) PRIMARY KEY, + cookie TEXT, + status TEXT);''') + for d in data['通用']: + if d['cookie']: + try: + cursor.execute('INSERT INTO public_cookies VALUES (?, ?, "OK");', (d['no'], d['cookie'])) + except: + pass + logger.info('---派蒙初始化数据库成功,已导入原json数据---') + except: + logger.error('---派蒙初始化数据库失败,请检查user_cookies.json文件是否存在---') + conn.commit() + conn.close() + +# 获取公共cookie +async def get_public_cookie(): + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + cursor.execute('''CREATE TABLE IF NOT EXISTS public_cookies( + no int IDENTITY(1,1) PRIMARY KEY, + cookie TEXT, + status TEXT);''') + cursor.execute('SELECT no, cookie FROM public_cookies WHERE status="OK";') + cookie = cursor.fetchone() + conn.commit() + conn.close() + return cookie + +# 插入公共cookie +async def insert_public_cookie(cookie): + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + cursor.execute('''CREATE TABLE IF NOT EXISTS public_cookies + ( + no int IDENTITY(1,1) PRIMARY KEY, + cookie TEXT, + status TEXT, + );''') + cursor.execute('INSERT IGNORE INTO public_cookies (cookie, status) VALUES (?,"ok");', (cookie,)) + conn.commit() + conn.close() + +# 设置公共cookie到上限 +async def limit_public_cookie(cookie): + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + cursor.execute('''CREATE TABLE IF NOT EXISTS public_cookies( + no int PRIMARY KEY + cookie TEXT, + status TEXT);''') + cursor.execute('UPDATE public_cookies SET status="limited30" WHERE cookie=?;', (cookie,)) + conn.commit() + conn.close() + +# 通过key(如user_id, uid)获取私人cookie +async def get_private_cookie(value, key='user_id'): + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + cursor.execute(f'SELECT user_id, cookie, uid, mys_id FROM private_cookies WHERE {key}="{value}";') + cookie = cursor.fetchall() + conn.close() + return cookie + +# 更新cookie +async def update_private_cookie(user_id, uid='', mys_id='', cookie='', stoken=''): + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + cursor.execute('REPLACE INTO private_cookies VALUES (?, ?, ?, ?, ?);', (user_id, uid, mys_id, cookie, stoken)) + conn.commit() + conn.close() + +# 删除私人cookie +async def delete_private_cookie(user_id): + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + cursor.execute('DELETE FROM private_cookies WHERE user_id=?',(user_id,)) + conn.commit() + conn.close() + +# 删除cookie +async def delete_cookie(cookie, type='public'): + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + cursor.execute('DELETE FROM cookie_cache WHERE cookie=?;', (cookie,)) + cursor.execute(f'DELETE FROM {type}_cookies WHERE cookie="{cookie}";') + conn.commit() + conn.close() + + +# 获取cookie缓存 +async def get_cookie_cache(value, key='uid'): + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + cursor.execute('''CREATE TABLE IF NOT EXISTS cookie_cache( + uid TEXT PRIMARY KEY NOT NULL, + mys_id TEXT, + cookie TEXT);''') + cursor.execute(f'SELECT cookie FROM cookie_cache WHERE {key}="{value}"') + res = cursor.fetchone() + if res: + try: + cursor.execute('SELECT user_id, uid, mys_id FROM private_cookies WHERE cookie=?;', (res[0],)) + is_in_private = cursor.fetchone() + if is_in_private: + return {'type':'private', 'user_id': is_in_private[0], 'cookie': res[0], 'uid': is_in_private[1], 'mys_id': is_in_private[2]} + except: + pass + try: + cursor.execute('SELECT no FROM public_cookies WHERE cookie=?;', (res[0],)) + is_in_public = cursor.fetchone() + if is_in_public: + return {'type':'public', 'cookie': res[0], 'no': is_in_public[0]} + except: + pass + conn.close() + return None + +# 更新cookie缓存 +async def update_cookie_cache(cookie, value, key='uid'): + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + cursor.execute('''CREATE TABLE IF NOT EXISTS cookie_cache( + uid TEXT PRIMARY KEY NOT NULL, + mys_id TEXT, + cookie TEXT);''') + cursor.execute(f'REPLACE INTO cookie_cache ({key}, cookie) VALUES ("{value}", "{cookie}");') + conn.commit() + conn.close() + +# 删除cookie缓存 +async def delete_cookie_cache(value='', key='cookie', all=False): + try: + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + if all: + cursor.execute('DROP TABLE cookie_cache;') + else: + cursor.execute(f'DELETE FROM cookie_cache WHERE {key}="{value}";') + conn.commit() + conn.close() + except: + pass + +# 获取user_id最后查询的uid +async def get_last_query(user_id): + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + cursor.execute('''CREATE TABLE IF NOT EXISTS last_query( + user_id TEXT PRIMARY KEY NOT NULL, + uid TEXT, + last_time datetime);''') + cursor.execute('SELECT uid FROM last_query WHERE user_id=?;', (user_id,)) + uid = cursor.fetchone() + conn.close() + return uid[0] if uid else None + +# 更新user_id最后查询的uid +async def update_last_query(user_id, value, key='uid'): + t = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + cursor.execute('''CREATE TABLE IF NOT EXISTS last_query( + user_id TEXT PRIMARY KEY NOT NULL, + uid TEXT, + mys_id TEXT, + last_time datetime);''') + cursor.execute(f'REPLACE INTO last_query (user_id, {key}, last_time) VALUES ("{user_id}", "{value}", "{t}");') + conn.commit() + conn.close() + + + + +init_db() + + diff --git a/hoshino/modules/Genshin_Paimon/gacha/__init__.py b/hoshino/modules/Genshin_Paimon/gacha/__init__.py index 5145be5..d0faf1b 100644 --- a/hoshino/modules/Genshin_Paimon/gacha/__init__.py +++ b/hoshino/modules/Genshin_Paimon/gacha/__init__.py @@ -1,15 +1,23 @@ import requests, json,re -from hoshino import R,MessageSegment +from hoshino import MessageSegment, Service, aiorequests from hoshino.typing import CQEvent, Message from hoshino.util import PriFreqLimiter -import hoshino from ..util import Dict -from hoshino import aiorequests from .gacha_role import * from .gacha_wish import more_ten -lmt = PriFreqLimiter(60) -sv=hoshino.Service('原神模拟抽卡') +lmt = PriFreqLimiter(30) +help_msg=''' +1.[抽n十连xx池]抽n次xx池的十连,最多同时5次 +*池子和官方同步,有角色1|角色2|武器|常驻,默认为角色1 +2.[模拟抽卡记录]查看模拟抽卡记录总结 +3.[模拟抽卡记录 角色/武器]查看模拟抽卡抽到的五星角色/武器 +4.[删除模拟抽卡记录]顾名思义 +5.[选择定轨 武器全名]选择武器定轨 +6.[查看定轨]查看当前定轨的武器 +7.[删除定轨]删除当前定轨的武器 +''' +sv = Service('派蒙模拟抽卡', bundle='派蒙', help_=help_msg) #activity = 301 限定卡池 #activity2 = 400 限定卡池2 #weapon = 302 武器卡池 @@ -44,7 +52,7 @@ async def gacha(bot, ev): if not lmt.check(gid,uid): await bot.finish(ev, f'模拟抽卡冷却中(剩余{int(lmt.left_time(gid,uid)) + 1}秒)', at_sender=True) return - lmt.start_cd(gid,uid,60) + lmt.start_cd(gid, uid, 30) if num >= 3: await bot.send(ev, '抽卡图正在生成中,请稍候') if isinstance(gacha_type,int): diff --git a/hoshino/modules/Genshin_Paimon/gacha_log_export/__init__.py b/hoshino/modules/Genshin_Paimon/gacha_log_export/__init__.py index c526f7c..ef3a0ca 100644 --- a/hoshino/modules/Genshin_Paimon/gacha_log_export/__init__.py +++ b/hoshino/modules/Genshin_Paimon/gacha_log_export/__init__.py @@ -1,12 +1,17 @@ import json,os,re from hoshino import R,MessageSegment,logger, Service from hoshino.typing import CQEvent, Message -from ..util import get_uid_by_qq, update_last_query_to_qq +from ..util import get_uid_in_msg from .gacha_logs import get_data from .get_img import get_gacha_log_img from .api import toApi, checkApi -sv = Service('原神抽卡记录导出') +help_msg=''' +1.[获取抽卡记录 (uid) (url)]提供url,获取原神抽卡记录,需要一定时间 +2.[查看抽卡记录 (uid)]查看抽卡记录分析 +3.[导出抽卡记录 (uid) (xlsx/json)]导出抽卡记录文件,上传到群文件中 +''' +sv = Service('派蒙抽卡记录导出', bundle='派蒙', help_=help_msg) data_path = os.path.join(os.path.dirname(os.path.dirname(__file__)),'user_data', 'gacha_log_data') if not os.path.exists(data_path): @@ -20,33 +25,13 @@ async def ckjl(bot,ev): if ev.message_type != 'group': await bot.send(ev,'在群聊中才能导出抽卡记录文件哦!') return - msg = ev.message.extract_plain_text().strip().split(' ') - uid = '' - if len(msg[0]) == 9 and msg[0].isdigit(): - uid = msg[0] - if len(msg) >= 2: - filetype = msg[1] - else: - filetype = 'xlsx' - else: - filetype = msg[0] - if not filetype or filetype not in ['xlsx', 'json']: - filetype = 'xlsx' - qq = str(ev.user_id) - if ev.message_type == 'guild': - rm = str(ev.message) - else: - rm = str(ev.raw_message) - match = re.search(r"\[CQ:at,qq=(.*)\]", rm) - if match: - uid = '' - qq = str(match.group(1)) + uid, msg, user_id, use_cache = await get_uid_in_msg(ev) if not uid: - uid = get_uid_by_qq(qq) - if not uid: - await bot.send(ev,'请把uid给派蒙哦,比如抽卡记录导出100000001 xlsx',at_sender=True) - return - update_last_query_to_qq(qq, uid) + await bot.send(ev,'请把uid给派蒙哦,比如获取抽卡记录100000001 链接',at_sender=True) + return + find_filetype = r'(?Pxlsx|json)' + match = re.search(find_filetype, msg) + filetype = match.group('filetype') if match else 'xlsx' if filetype == 'xlsx': filetype = f'gachaExport-{uid}.xlsx' else: @@ -59,24 +44,12 @@ async def ckjl(bot,ev): @sv.on_prefix(('更新抽卡记录', '获取抽卡记录', 'updategachalog', 'gxckjl')) async def update_ckjl(bot,ev): - msg = ev.message.extract_plain_text().strip().split(' ') - uid = '' - if len(msg[0]) == 9 and msg[0].isdigit(): - uid = msg[0] - if len(msg) >= 2: - url = msg[1] - else: - url = '' - else: - url = msg[0] - qq = str(ev.user_id) + uid, msg, user_id, use_cache = await get_uid_in_msg(ev) if not uid: - uid = get_uid_by_qq(qq) - if not uid: - await bot.send(ev,'请把uid给派蒙哦,比如获取抽卡记录100000001 链接',at_sender=True) - return - if url: - match = re.search(r'(https://webstatic.mihoyo.com/.*#/log)', url) + await bot.send(ev,'请把uid给派蒙哦,比如获取抽卡记录100000001 链接',at_sender=True) + return + if msg: + match = re.search(r'(https://webstatic.mihoyo.com/.*#/log)', msg) if match: url = str(match.group(1)) else: @@ -85,17 +58,17 @@ async def update_ckjl(bot,ev): else: with open(os.path.join(data_path, 'user_gacha_log.json'), 'r', encoding="utf-8") as f: user_data = json.load(f) - if qq in user_data and uid in user_data[qq]: - url = user_data[qq][uid] + if user_id in user_data and uid in user_data[user_id]: + url = user_data[user_id][uid] await bot.send(ev,'发现历史抽卡记录链接,尝试使用...') else: await bot.send(ev, '拿到游戏抽卡记录链接后,对派蒙说[获取抽卡记录 uid 链接]就可以啦\n获取抽卡记录链接的方式和vx小程序的是一样的,还请旅行者自己搜方法', at_sender=True) return with open(os.path.join(data_path, 'user_gacha_log.json'), 'r', encoding="utf-8") as f: user_data = json.load(f) - if qq not in user_data: - user_data[qq] = {} - user_data[qq][uid] = url + if user_id not in user_data: + user_data[user_id] = {} + user_data[user_id][uid] = url with open(os.path.join(data_path, 'user_gacha_log.json'), 'w', encoding="utf-8") as f: json.dump(user_data, f, ensure_ascii=False, sort_keys=False, indent=4) @@ -115,34 +88,12 @@ async def update_ckjl(bot,ev): @sv.on_prefix('查看抽卡记录', 'ckjl', 'gachalog') async def get_ckjl(bot,ev): - msg = ev.message.extract_plain_text().strip().split(' ') - uid = '' - if len(msg[0]) == 9 and msg[0].isdigit(): - uid = msg[0] - if len(msg) >= 2: - pool = msg[1] - else: - pool = 'all' - else: - pool = msg[0] - if not pool or pool not in ['all', '角色', '武器', '常驻', '新手']: - pool = 'all' - qq = str(ev.user_id) - if ev.message_type == 'guild': - rm = str(ev.message) - else: - rm = str(ev.raw_message) - match = re.search(r"\[CQ:at,qq=(.*)\]", rm) - if match: - uid = '' - qq = str(match.group(1)) + uid, msg, user_id, use_cache = get_uid_in_msg(ev) if not uid: - uid = get_uid_by_qq(qq) - if not uid: - await bot.send(ev,'请把uid给派蒙哦,比如抽卡记录100000001 all',at_sender=True) - return - update_last_query_to_qq(qq, uid) - + await bot.send(ev,'请把uid给派蒙哦,比如获取抽卡记录100000001 链接',at_sender=True) + return + match = re.search(r'(all|角色|武器|常驻|新手)',msg) + pool = match.group(1) if match else 'all' local_data = os.path.join(data_path, f'gachaData-{uid}.json') if not os.path.exists(local_data): await bot.send(ev, '你在派蒙这里还没有抽卡记录哦,对派蒙说 获取抽卡记录 吧!', at_sender=True) diff --git a/hoshino/modules/Genshin_Paimon/game_guild/__init__.py b/hoshino/modules/Genshin_Paimon/game_guild/__init__.py index 4401617..6ab0c24 100644 --- a/hoshino/modules/Genshin_Paimon/game_guild/__init__.py +++ b/hoshino/modules/Genshin_Paimon/game_guild/__init__.py @@ -7,7 +7,16 @@ from .blue import get_blue_pic from ..util import pil2b64 from hoshino.util import filt_message -sv=hoshino.Service('原神角色wiki') +help_msg=''' +1.[xx角色攻略]查看西风驿站出品的角色一图流攻略 +2.[xx角色材料]查看惜月出品的角色材料统计 +3.[xx参考面板]查看blue菌hehe出品的参考面板攻略 +4.[xx收益曲线]查看blue菌hehe出品的收益曲线攻略 +*感谢来自大佬们的授权。角色支持别名查询 +''' +sv = Service('派蒙WIKI', bundle='派蒙', help_=help_msg) + + res_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'res') @sv.on_prefix('角色攻略') diff --git a/hoshino/modules/Genshin_Paimon/get_data.py b/hoshino/modules/Genshin_Paimon/get_data.py index b162c7a..a0641b2 100644 --- a/hoshino/modules/Genshin_Paimon/get_data.py +++ b/hoshino/modules/Genshin_Paimon/get_data.py @@ -1,31 +1,38 @@ from hoshino import aiorequests -from .util import get_headers, cache +from .util import get_headers, cache, get_use_cookie, get_own_cookie, check_retcode +from .db_util import update_cookie_cache import datetime import re +import random -@cache(ttl=datetime.timedelta(minutes=30)) -async def get_abyss_data(uid, cookie, schedule_type = "1", use_cache=True): - if uid[0] == '5': - server_id = "cn_qd01" - else: - server_id = "cn_gf01" +# TODO:注意每处参数顺序的更改 +@cache(ttl=datetime.timedelta(hours=1)) +async def get_abyss_data(user_id, uid, schedule_type = "1", use_cache=True): + server_id = "cn_qd01" if uid[0] == '5' else "cn_gf01" url ="https://api-takumi.mihoyo.com/game_record/app/genshin/api/spiralAbyss" - headers = get_headers(q="role_id=" + uid + "&schedule_type=" + schedule_type + "&server=" + server_id, cookie=cookie) params ={ "schedule_type": schedule_type, "role_id": uid, - "server": server_id - } - res = await aiorequests.get(url=url, headers=headers, params=params) - return await res.json() + "server": server_id} + while True: + cookie = await get_use_cookie(user_id, uid=uid, action='查询深渊') + if not cookie: + return '现在派蒙没有可以用的cookie哦,请让主人 添加公共ck 吧!' + headers = get_headers(q=f'role_id={uid}&schedule_type={schedule_type}&server={server_id}', cookie=cookie['cookie']) + res = await aiorequests.get(url=url, headers=headers, params=params) + data = await res.json() + if await check_retcode(data, cookie, uid): + return data + -async def get_daily_note_data(uid, cookie): - if uid[0] == '5': - server_id = "cn_qd01" - else: - server_id = "cn_gf01" +async def get_daily_note_data(uid): + server_id = "cn_qd01" if uid[0] == '5' else "cn_gf01" url ="https://api-takumi.mihoyo.com/game_record/app/genshin/api/dailyNote" - headers = get_headers(q="role_id=" + uid + "&server=" + server_id, cookie=cookie) + cookie = await get_own_cookie(uid, action='查询实时便签') + if not cookie: + return f'你的uid{uid}没有绑定对应的cookie哦,先用ysb给派蒙绑定吧!' + await update_cookie_cache(cookie['cookie'], uid, 'uid') + headers = get_headers(q=f'role_id={uid}&server={server_id}', cookie=cookie['cookie']) params = { "server": server_id, "role_id": uid @@ -34,67 +41,79 @@ async def get_daily_note_data(uid, cookie): return await res.json() @cache(ttl=datetime.timedelta(hours=1)) -async def get_player_card_data(uid, cookie, use_cache=True): - if uid[0] == '5': - server_id = "cn_qd01" - else: - server_id = "cn_gf01" +async def get_player_card_data(user_id, uid, use_cache=True): + server_id = "cn_qd01" if uid[0] == '5' else "cn_gf01" url ="https://api-takumi.mihoyo.com/game_record/app/genshin/api/index" - headers = get_headers(q="role_id=" + uid + "&server=" + server_id, cookie=cookie) params = { "server": server_id, "role_id": uid } - res = await aiorequests.get(url=url, headers=headers, params=params) - return await res.json() + while True: + cookie = await get_use_cookie(user_id, uid=uid, action='查询原神卡片') + if not cookie: + return '现在派蒙没有可以用的cookie哦,请让主人 添加公共ck 吧!' + headers = get_headers(q=f'role_id={uid}&server={server_id}', cookie=cookie['cookie']) + res = await aiorequests.get(url=url, headers=headers, params=params) + data = await res.json() + if await check_retcode(data, cookie, uid): + return data @cache(ttl=datetime.timedelta(hours=1)) -async def get_chara_detail_data(uid, cookie, use_cache=True): - if uid[0] == '5': - server_id = "cn_qd01" - else: - server_id = "cn_gf01" +async def get_chara_detail_data(user_id, uid, use_cache=True): + server_id = "cn_qd01" if uid[0] == '5' else "cn_gf01" json_data = { "server": server_id, "role_id": uid, "character_ids": [] } url = 'https://api-takumi.mihoyo.com/game_record/app/genshin/api/character' - headers = get_headers(b=json_data, cookie=cookie) - res = await aiorequests.post(url=url, headers=headers, json=json_data) - return await res.json() + while True: + cookie = await get_use_cookie(user_id, uid=uid, action='查询角色详情') + if not cookie: + return '现在派蒙没有可以用的cookie哦,请让主人 添加公共ck 吧!' + headers = get_headers(b=json_data, cookie=cookie['cookie']) + res = await aiorequests.post(url=url, headers=headers, json=json_data) + data = await res.json() + if await check_retcode(data, cookie, uid): + return data @cache(ttl=datetime.timedelta(hours=1)) -async def get_chara_skill_data(uid, chara_id, cookie, use_cache=True): - if uid[0] == '5': - server_id = "cn_qd01" - else: - server_id = "cn_gf01" +async def get_chara_skill_data(uid, chara_id, use_cache=True): + server_id = "cn_qd01" if uid[0] == '5' else "cn_gf01" url = 'https://api-takumi.mihoyo.com/event/e20200928calculate/v1/sync/avatar/detail' - headers = get_headers(q="uid=" + uid + "®ion=" + server_id + "&avatar_id=" + str(chara_id), cookie=cookie) + cookie = await get_own_cookie(uid, action='查询角色天赋') + if not cookie: + return None + await update_cookie_cache(cookie['cookie'], uid, 'uid') + headers = get_headers(q=f'uid={uid}®ion={server_id}&avatar_id={chara_id}', cookie=cookie['cookie']) params = { "region": server_id, "uid": uid, "avatar_id": chara_id } res = await aiorequests.get(url=url, headers=headers, params=params) - return await res.json() + data = await res.json() + # TODO:待定,未知cookie对技能的影响 + return data @cache(ttl=datetime.timedelta(hours=1)) -async def get_monthinfo_data(uid, month, cookie, use_cache=True): - if uid[0] == '5': - server_id = "cn_qd01" - else: - server_id = "cn_gf01" +async def get_monthinfo_data(uid, month, use_cache=True): + server_id = "cn_qd01" if uid[0] == '5' else "cn_gf01" url = 'https://hk4e-api.mihoyo.com/event/ys_ledger/monthInfo' - headers = get_headers(q='month='+ str(month) + '&bind_uid=' + uid + '&bind_region=' + server_id, cookie=cookie) + cookie = await get_own_cookie(uid, action='查询每月札记') + if not cookie: + return f'你的uid{uid}没有绑定对应的cookie哦,先用ysb给派蒙绑定吧!' + await update_cookie_cache(cookie['cookie'], uid, 'uid') + headers = get_headers(q=f'month={month}&bind_uid={uid}&bind_region={server_id}', cookie=cookie['cookie']) params = { "month": int(month), "bind_uid": uid, "bind_region": server_id } res = await aiorequests.get(url=url, headers=headers, params=params) - return await res.json() + data = await res.json() + if await check_retcode(data, cookie, uid): + return data async def get_bind_game(cookie): finduid = re.search(r'account_id=(\d{6,12})', cookie) @@ -102,11 +121,13 @@ async def get_bind_game(cookie): return None uid = finduid.group(1) url = 'https://api-takumi.mihoyo.com/game_record/card/wapi/getGameRecordCard' - headers = get_headers(q='uid=' + uid, cookie = cookie) + headers = get_headers(q=f'uid={uid}', cookie = cookie) params = { "uid": uid } res = await aiorequests.get(url=url, headers=headers, params=params) - return await res.json() + return (await res.json()), uid + + \ No newline at end of file diff --git a/hoshino/modules/Genshin_Paimon/monthinfo/__init__.py b/hoshino/modules/Genshin_Paimon/monthinfo/__init__.py index a12ae6e..2f1d8bc 100644 --- a/hoshino/modules/Genshin_Paimon/monthinfo/__init__.py +++ b/hoshino/modules/Genshin_Paimon/monthinfo/__init__.py @@ -2,55 +2,36 @@ import json,os,re,datetime from hoshino import R,MessageSegment,logger, Service from hoshino.typing import CQEvent, Message from hoshino.util import filt_message -from ..util import get_uid_by_qq, get_cookie, check_uid_by_qq, update_last_query_to_qq +from ..util import get_uid_in_msg from ..get_data import get_monthinfo_data from .get_img import draw_monthinfo_card -sv = Service('原神每月札记') +help_msg=''' +[myzj/每月札记/zj (uid) (月份)]查看该月份获得的原石、摩拉数 +*绑定私人cookie之后才能使用,只能查看最近3个月的记录,默认为本月 +''' +sv = Service('派蒙每月札记', bundle='派蒙', help_=help_msg) -@sv.on_prefix(('myzj','每月札记')) +@sv.on_prefix(('myzj', '每月札记', 'zj')) async def main(bot,ev): - msg = ev.message.extract_plain_text().strip().split(' ') - uid = '' - if len(msg[0]) == 9 and msg[0].isdigit(): - uid = msg[0] - if len(msg) >= 2: - month = msg[1] - else: - month = datetime.datetime.now().month + uid, msg, user_id, use_cache = await get_uid_in_msg(ev) + # 札记只能查看最近3个月的,构造正则来获取月份 + month_now = datetime.datetime.now().month + if month_now == 1: + month_list = ['11','12','1'] + elif month_now == 2: + month_list = ['12', '1', '2'] else: - month = msg[0] - if month and not month.isdigit(): - await bot.send(ev,'月份是不是写错了呀,要阿拉伯数字哦',at_sender=True) - return - qq = str(ev.user_id) - if ev.message_type == 'guild': - rm = str(ev.message) + month_list = [str(month_now - 2), str(month_now - 1)] + find_month = '(?P' + '|'.join(month_list) + ')' + match = re.search(find_month, msg) + month = match.group('month') if match else month_now + # try: + data = await get_monthinfo_data(uid, month, use_cache=use_cache) + if isinstance(data, str): + await bot.send(ev, data, at_sender=True) else: - rm = str(ev.raw_message) - match = re.search(r"\[CQ:at,qq=(.*)\]", rm) - if match: - uid = '' - qq = str(match.group(1)) - if uid and not check_uid_by_qq(qq, uid): - await bot.send(ev,'派蒙没有这个uid的绑定信息哦',at_sender=True) - return - if not uid: - uid = get_uid_by_qq(qq) - if not uid: - await bot.send(ev,'你还没把信息绑定给派蒙哦',at_sender=True) - return - if len(uid) != 9 or not uid.isdigit(): - await bot.send(ev,f'uid {filt_message(uid)} 不合规,是不是打错了呀',at_sender=True) - return - cookie = await get_cookie(qq, uid, only_private = True, only_match_uid = True) - update_last_query_to_qq(qq, uid) - if not cookie: - await bot.send(ev,'你没有绑定cookie或者cookie失效了噢!',at_sender=True) - else: - try: - data = await get_monthinfo_data(uid, month, cookie) - monthinfo_card = await draw_monthinfo_card(data) - await bot.send(ev, monthinfo_card, at_sender=True) - except Exception as e: - await bot.send(ev, f'派蒙出现了问题:{e}',at_sender=True) + monthinfo_card = await draw_monthinfo_card(data) + await bot.send(ev, monthinfo_card, at_sender=True) + # except Exception as e: + #  await bot.send(ev, f'派蒙出现了问题:{e}',at_sender=True) diff --git a/hoshino/modules/Genshin_Paimon/player_card/__init__.py b/hoshino/modules/Genshin_Paimon/player_card/__init__.py index dd4de47..23507a5 100644 --- a/hoshino/modules/Genshin_Paimon/player_card/__init__.py +++ b/hoshino/modules/Genshin_Paimon/player_card/__init__.py @@ -2,127 +2,80 @@ import json,os,re from hoshino import R,MessageSegment,logger, Service from hoshino.typing import CQEvent, Message from hoshino.util import filt_message -from ..util import get_uid_by_qq, get_cookie, check_uid_by_qq, update_last_query_to_qq +from ..util import get_uid_in_msg, get_at_target from ..get_data import get_player_card_data, get_chara_detail_data, get_chara_skill_data from .get_img import draw_player_card, draw_all_chara_card, draw_chara_card from ..character_alias import get_id_by_alias -sv = Service('原神信息查询') +help_msg=''' +1.[ys (uid)]查看原神个人卡片(包含宝箱、探索度等数据) +2.[ysa (uid)]查看所有公开的8角色的简略信息 +3.[ysc (uid) 角色名]查看公开的8角色的详细信息 +*绑定私人cookie之后就可以查看所有角色啦 +''' +sv = Service('派蒙原神信息查询', bundle='派蒙', help_=help_msg) @sv.on_prefix('ys') async def player_card(bot,ev): - uid = ev.message.extract_plain_text().strip() - qq = str(ev.user_id) - if ev.message_type == 'guild': - rm = str(ev.message) - else: - rm = str(ev.raw_message) - match = re.search(r"\[CQ:at,qq=(.*)\]", rm) - if match: - uid = '' - qq = str(match.group(1)) + uid, msg, user_id, use_cache = await get_uid_in_msg(ev) if not uid: - uid = get_uid_by_qq(qq) - if not uid: - await bot.send(ev,'请把uid给派蒙哦,比如ys100000001',at_sender=True) - return - if len(uid) != 9 or not uid.isdigit(): - await bot.send(ev,f'uid {filt_message(uid)} 不合规,是不是打错了呀',at_sender=True) - return - cookie = await get_cookie(qq, uid) - update_last_query_to_qq(qq, uid) - if not cookie: - await bot.send(ev,'这个uid的cookie信息好像失效了,请给派蒙重新绑定!',at_sender=True) + await bot.send(ev,'请把正确的uid给派蒙哦,例如sy100123456!',at_sender=True) + return + # try: + data = await get_player_card_data(user_id, uid, use_cache=use_cache) + if isinstance(data, str): + await bot.send(ev, data, at_sender=True) else: - try: - if ev.message_type == 'group': - user_info = await bot.get_group_member_info(group_id=ev.group_id,user_id=int(qq)) - nickname = user_info['card'] or user_info['nickname'] - else: - nickname = ev.sender['nickname'] - data = await get_player_card_data(uid, cookie) - chara_data = await get_chara_detail_data(uid, cookie) or [] - player_card = await draw_player_card(data, chara_data, uid, nickname) - await bot.send(ev, player_card, at_sender=True) - except Exception as e: - await bot.send(ev, f'派蒙出现了问题:{e}',at_sender=True) + if ev.message_type == 'group': + user_info = await bot.get_group_member_info(group_id=ev.group_id,user_id=int(user_id)) + nickname = user_info['card'] or user_info['nickname'] + else: + nickname = ev.sender['nickname'] + chara_data = await get_chara_detail_data(user_id, uid, use_cache=use_cache) + chara_data = None if isinstance(chara_data, str) else chara_data + player_card = await draw_player_card(data, chara_data, uid, nickname) + await bot.send(ev, player_card, at_sender=True) + # except Exception as e: + # await bot.send(ev, f'派蒙出现了问题:{e}',at_sender=True) @sv.on_prefix('ysa') async def all_characters(bot,ev): - uid = ev.message.extract_plain_text().strip() - qq = str(ev.user_id) - if ev.message_type == 'guild': - rm = str(ev.message) - else: - rm = str(ev.raw_message) - match = re.search(r"\[CQ:at,qq=(.*)\]", rm) - if match: - uid = '' - qq = str(match.group(1)) + uid, msg, user_id, use_cache = await get_uid_in_msg(ev) if not uid: - uid = get_uid_by_qq(qq) - if not uid: - await bot.send(ev,'请把uid给派蒙哦,比如ysa100000001',at_sender=True) - return - if len(uid) != 9 or not uid.isdigit(): - await bot.send(ev,f'uid {filt_message(uid)} 不合规,是不是打错了呀',at_sender=True) + await bot.send(ev,'请把正确的uid给派蒙哦,例如sy100123456!',at_sender=True) return - cookie = await get_cookie(qq, uid) - update_last_query_to_qq(qq, uid) - if not cookie: - await bot.send(ev,'这个uid的cookie信息好像失效了,请给派蒙重新绑定!',at_sender=True) + # try: + chara_data = await get_chara_detail_data(user_id, uid, use_cache=use_cache) + if isinstance(chara_data, str): + await bot.send(ev, chara_data, at_sender=True) else: - try: - chara_data = await get_chara_detail_data(uid, cookie) or [] - player_card = await draw_all_chara_card(chara_data, uid) - await bot.send(ev, player_card, at_sender=True) - except Exception as e: - await bot.send(ev, f'派蒙出现了问题:{e}',at_sender=True) - + player_card = await draw_all_chara_card(chara_data, uid) + await bot.send(ev, player_card, at_sender=True) + # except Exception as e: + # await bot.send(ev, f'派蒙出现了问题:{e}',at_sender=True) @sv.on_prefix('ysc') async def my_characters(bot,ev): - msg = ev.message.extract_plain_text().strip().split(' ') - qq = str(ev.user_id) - uid = '' - if len(msg[0]) == 9 and msg[0].isdigit(): - uid = msg[0] - if len(msg) >= 2: - chara = msg[1] - else: - await bot.send(ev,'要把想查询的角色名告诉我哦!',at_sender=True) - return - else: - chara = msg[0] + uid, msg, user_id, use_cache = await get_uid_in_msg(ev) + if not uid: + await bot.send(ev,'请把正确的uid给派蒙哦,例如sy100123456!',at_sender=True) + return + if not msg: + await bot.send(ev,f'要把角色名给派蒙呀!例如ysc100123456钟离',at_sender=True) + return + chara = msg.split(' ')[0] chara_name = get_id_by_alias(chara) if not chara_name: await bot.send(ev,f'没有角色名叫{filt_message(chara)}哦!',at_sender=True) return - if ev.message_type == 'guild': - rm = str(ev.message) + # try: + chara_data = await get_chara_detail_data(user_id, uid, use_cache=use_cache) + if isinstance(chara_data, str): + await bot.send(ev, chara_data, at_sender=True) else: - rm = str(ev.raw_message) - match = re.search(r"\[CQ:at,qq=(.*)\]", rm) - if match: - qq = str(match.group(1)) - if not uid: - uid = get_uid_by_qq(qq) - if not uid: - await bot.send(ev,'请把uid给派蒙哦,比如ysc100000001 钟离',at_sender=True) - return - if len(uid) != 9 or not uid.isdigit(): - await bot.send(ev,f'uid {filt_message(uid)} 不合规,是不是打错了呀',at_sender=True) - return - cookie = await get_cookie(qq, uid) - update_last_query_to_qq(qq, uid) - if not cookie: - await bot.send(ev,'你没有绑定cookie或者cookie失效了噢!',at_sender=True) - else: - try: - chara_data = await get_chara_detail_data(uid, cookie) - skill_data = await get_chara_skill_data(uid, chara_name[0], cookie) - chara_card = await draw_chara_card(chara_data, skill_data, chara_name, uid) - await bot.send(ev, chara_card, at_sender=True) - except Exception as e: - await bot.send(ev, f'派蒙出现了问题:{e}',at_sender=True) + skill_data = await get_chara_skill_data(uid, chara_name[0], use_cache=use_cache) + chara_card = await draw_chara_card(chara_data, skill_data, chara_name, uid) + await bot.send(ev, chara_card, at_sender=True) + # except Exception as e: + # await bot.send(ev, f'派蒙出现了问题:{e}',at_sender=True) diff --git a/hoshino/modules/Genshin_Paimon/player_card/get_img.py b/hoshino/modules/Genshin_Paimon/player_card/get_img.py index 14dc7ba..017a919 100644 --- a/hoshino/modules/Genshin_Paimon/player_card/get_img.py +++ b/hoshino/modules/Genshin_Paimon/player_card/get_img.py @@ -218,7 +218,7 @@ async def draw_player_card(data, chara_data, uid, nickname="旅行者"): # 世界探索 await draw_world_data(bg_draw,data) # 角色 - if chara_data['data']: + if chara_data: chara_data = chara_data['data']['avatars'] w = 1045 i = 0 @@ -429,6 +429,9 @@ async def draw_chara_card(data, skill_data, chara_name, uid): bg_img.alpha_composite(reli_icon, reli_p[i]) i += 1 + if not skill_data: + skill_data = {'retcode' : 'error'} + # 补上三命和五命的技能等级提升 if skill_data['retcode'] == 0 and character['constellations'][2]['is_actived']: skill_name = re.search(r'>(.*)', character['constellations'][2]['effect']) diff --git a/hoshino/modules/Genshin_Paimon/user_data/__init__.py b/hoshino/modules/Genshin_Paimon/user_data/__init__.py index 4c638ac..7939252 100644 --- a/hoshino/modules/Genshin_Paimon/user_data/__init__.py +++ b/hoshino/modules/Genshin_Paimon/user_data/__init__.py @@ -1,42 +1,64 @@ -from hoshino import MessageSegment, Service, trigger, priv, CanceledException +from hoshino import MessageSegment, Service, trigger, priv, CanceledException,logger from hoshino.typing import CQEvent, Message -from ..util import update_last_query_to_qq, bind_cookie, bind_public_cookie from nonebot import message_preprocessor from ..get_data import get_bind_game +from ..db_util import insert_public_cookie, update_private_cookie, delete_cookie_cache, delete_cookie -sv = Service('原神绑定',visible=False,enable_on_default=True) +help_msg=''' +1.[ysb cookie]绑定你的私人cookie以开启高级功能 +2.[删除ck]删除你的私人cookie +3.[添加公共ck cookie]添加公共cookie以供大众查询*仅管理员 +''' +sv = Service('派蒙绑定', visible=False, enable_on_default=True, bundle='派蒙', help_=help_msg) + +cookie_error_msg = '这个cookie无效哦,请旅行者确认是否正确\n1.ck要登录mys帐号后获取\n2.获取ck后不能退出登录\n3.ck至少要包含cookie_token和account_id两个参数\n4.建议在无痕模式下取' @sv.on_prefix(('原神绑定','ysb')) async def bind(bot,ev): cookie = ev.message.extract_plain_text().strip() - qq=str(ev.user_id) if cookie == '': res = '''旅行者好呀,你可以直接用ys/ysa等指令附上uid来使用派蒙\n如果想看全部角色信息和实时便笺等功能,要把cookie给派蒙哦\ncookie获取方法:登录网页版米游社,在地址栏粘贴代码:\njavascript:(function(){prompt(document.domain,document.cookie)})();\n复制弹窗出来的字符串(手机要via或chrome浏览器才行)\n然后添加派蒙私聊发送ysb接刚刚复制的字符串,例如:ysb UM_distinctid=17d131d...\ncookie是账号重要安全信息,请确保机器人持有者可信赖!''' await bot.send(ev,res,at_sender=True) else: - cookie_info = await get_bind_game(cookie) + cookie_info, mys_id = await get_bind_game(cookie) if not cookie_info or cookie_info['retcode'] != 0: - msg = '这cookie没有用哦,检查一下是不是复制错了或者过期了(试试重新登录米游社再获取)' + msg = cookie_error_msg if ev.detail_type != 'private': - msg += '\n当前是在群聊里绑定,建议旅行者添加派蒙好友私聊绑定!' + msg += '\n当前是在群聊里绑定,建议旅行者添加派蒙好友私聊绑定!' await bot.send(ev,msg,at_sender=True) else: for data in cookie_info['data']['list']: if data['game_id'] == 2: uid = data['game_role_id'] nickname = data['nickname'] - # level = data['level'] break if uid: - await bind_cookie(qq,uid,cookie) - msg = f'{nickname}绑定成功啦!使用ys/ysa等指令和派蒙互动吧!' + await update_private_cookie(user_id=str(ev.user_id), uid=uid, mys_id=mys_id, cookie=cookie) + await delete_cookie_cache(uid, key='uid', all=False) + msg = f'{nickname}绑定成功啦!使用ys/ysa等指令和派蒙互动吧!' if ev.detail_type != 'private': - msg += '\n当前是在群聊里绑定,建议旅行者把cookie撤回哦!' + msg += '\n当前是在群聊里绑定,建议旅行者把cookie撤回哦!' await bot.send(ev,msg,at_sender=True) +@sv.on_prefix('删除ck') +async def delete(bot,ev): + user_id = str(ev.user_id) + await delete_private_cookie(str(ev.user_id)) + await bot.send(ev, '派蒙把你的私人cookie都删除啦!', at_sender=True) @sv.on_prefix('添加公共ck') -async def bing_public(bot,ev): +async def bing_public(bot, ev): + if not priv.check_priv(ev, hoshino.priv.ADMIN): + await bot.send(ev, '只有管理员或主人才能添加公共cookie哦!') + return cookie = ev.message.extract_plain_text().strip() - res = await bind_public_cookie(cookie) - await bot.send(ev,res,at_sender=True) \ No newline at end of file + if await check_cookie(cookie): + await insert_public_cookie(cookie) + await bot.send(ev, '公共cookie添加成功啦,派蒙开始工作!') + else: + await bot.send(ev, cookie_error_msg) + +@sv.scheduled_job('cron', hour='0') +async def delete_cache(): + logger.info('---清空今日cookie缓存---') + await delete_cookie_cache(all=True) \ No newline at end of file diff --git a/hoshino/modules/Genshin_Paimon/util.py b/hoshino/modules/Genshin_Paimon/util.py index 7e31ba5..be3e3da 100644 --- a/hoshino/modules/Genshin_Paimon/util.py +++ b/hoshino/modules/Genshin_Paimon/util.py @@ -1,54 +1,78 @@ import os import json -import traceback import hashlib +import re import random import time -import uuid -import requests from hoshino import logger, aiorequests from io import BytesIO import base64 import datetime import functools import inspect +from nonebot import get_bot +from .db_util import get_private_cookie, get_cookie_cache, get_public_cookie, limit_public_cookie, update_cookie_cache,get_last_query,update_last_query,delete_cookie -# user_cookies.json数据文件模版,如没有.json文件就会按这个模版生成文件 -user_cookies_example = { - "通用": [ - { - "cookie": "", - "no": 1 - }, - { - "cookie": "", - "no": 2 - } - ], - "私人":{} - -} -user_cookies = {} -def load_data(): - path = os.path.join(os.path.dirname(__file__), 'user_data','user_cookies.json') - if not os.path.exists(path): - with open(path,'w',encoding='UTF-8') as f: - json.dump(user_cookies_example,f,ensure_ascii=False) - try: - with open(path, encoding='utf8') as f: - data = json.load(f) - for k, v in data.items(): - user_cookies[k] = v - except: - traceback.print_exc() +async def get_use_cookie(user_id, uid='', mys_id='', action=''): + cache_cookie = await get_cookie_cache(uid, 'uid') + if cache_cookie: + if cache_cookie['type'] == 'public': + logger.info(f'---派蒙调用{uid}的缓存公共cookie执行{action}操作---') + else: + logger.info(f'---派蒙调用{uid}的缓存私人cookie执行{action}操作---') + return cache_cookie + cookies = await get_private_cookie(user_id, 'user_id') + if not cookies: + public_cookie = await get_public_cookie() + if not public_cookie: + return None + else: + logger.info(f'---派蒙调用{public_cookie[0]}号公共cookie执行{action}操作---') + return {'type':'public', 'cookie': public_cookie[1], 'no': public_cookie[0]} + else: + for user_id_, cookie, uid_, mys_id_ in cookies: + if (uid and uid_ == uid) or (mys_id and mys_id_ == mys_id): + logger.info(f'---派蒙调用用户{user_id_}的uid{uid_}私人cookie执行{action}操作---') + return {'type':'private', 'user_id': user_id_, 'cookie': cookie, 'uid': uid_, 'mys_id': mys_id_} + use_cookie = random.choice(cookies) + logger.info(f'---派蒙调用用户{use_cookie[0]}的uid{use_cookie[2]}私人cookie执行{action}操作---') + return {'type':'private', 'user_id': use_cookie[0], 'cookie': use_cookie[1], 'uid': use_cookie[2], 'mys_id': use_cookie[3]} + +async def get_own_cookie(uid='', mys_id='', action=''): + if uid: + cookie = (await get_private_cookie(uid, 'uid')) + elif mys_id: + cookie = (await get_private_cookie(mys_id, 'mys_id')) + else: + cookie = None + if not cookie: + return None + else: + cookie = cookie[0] + logger.info(f'---派蒙调用用户{cookie[0]}的uid{cookie[2]}私人cookie执行{action}操作---') + return {'type':'private', 'user_id': cookie[0], 'cookie': cookie[1], 'uid': cookie[2], 'mys_id': cookie[3]} + +# 检查数据返回状态,10001为ck过期了,10101为达到每日30次上线了 +async def check_retcode(data, cookie, uid): + if data['retcode'] == 10001: + # TODO:此处为删除cookie的操作 + await delete_cookie(cookie['cookie'], cookie['type']) + # TODO: 此处为发送cookie删除信息提醒的操作 + await send_cookie_delete_msg(cookie) + return False + elif data['retcode'] == 10101: + # TODO: 此处为设置cookie到30次上限的操作 + if cookie['type'] == 'public': + logger.info(f'{cookie["no"]}号公共cookie达到了每日30次查询上限') + elif cookie['type'] == 'private': + logger.info(f'用户{cookie["user_id"]}的uid{cookie["uid"]}私人cookie达到了每日30次查询上限') + await limit_public_cookie(cookie['cookie']) + return False + else: + # TODO: 此处为更新最后查询的操作 + await update_cookie_cache(cookie['cookie'], uid, 'uid') + return True -def save_data(): - path = os.path.join(os.path.dirname(__file__), 'user_data','user_cookies.json') - try: - with open(path, 'w', encoding='utf8') as f: - json.dump(user_cookies, f, ensure_ascii=False, indent=2) - except: - traceback.print_exc() # 缓存装饰器 ttl为过期时间 参数use_cache决定是否使用缓存,默认为True def cache(ttl=datetime.timedelta(hours=1), **kwargs): @@ -81,6 +105,34 @@ def cache(ttl=datetime.timedelta(hours=1), **kwargs): return wrap +# 获取message中的艾特对象 +async def get_at_target(msg): + for msg_seg in msg: + if msg_seg.type == "at": + return msg_seg.data['qq'] + return None + +# message预处理,获取uid、干净的msg、user_id、是否缓存 +async def get_uid_in_msg(ev): + msg = ev.message + msgt = msg.extract_plain_text().strip() + if not msg: + uid = await get_last_query(str(ev.user_id)) + return uid, '', str(ev.user_id), True + user_id = await get_at_target(msg) or str(ev.user_id) + use_cache = False if '-r' in msgt else True + msgt = msgt.replace('-r', '').strip() + find_uid = r'(?P(1|2|5)\d{8})' + for msg_seg in msg: + if msg_seg.type == 'text': + match = re.search(find_uid, msg_seg.data['text']) + if match: + await update_last_query(user_id, match.group('uid'), 'uid') + return match.group('uid'), msgt.replace(match.group('uid'), '').strip(), user_id, use_cache + uid = await get_last_query(user_id) + return uid, msgt.strip(), user_id, use_cache + + class Dict(dict): __setattr__ = dict.__setitem__ __getattr__ = dict.__getitem__ @@ -123,10 +175,9 @@ def get_headers(cookie, q='',b=None): 'x-rpc-client_type': '5', 'Referer': 'https://webstatic.mihoyo.com/' } - #print(headers) return headers -# 检查cookie是否有效,通过查看个人主页是否返回ok来判断 +# 检查cookie是否有效,通过查看个人主页来判断 async def check_cookie(cookie): url = 'https://bbs-api.mihoyo.com/user/wapi/getUserFullInfo?gids=2' headers ={ @@ -144,91 +195,21 @@ async def check_cookie(cookie): else: return True -# 通过qq号获取最后查询的uid -def get_uid_by_qq(qq): - if qq not in user_cookies['私人']: - return None - return user_cookies['私人'][qq]['last_query'] +# 向超级用户私聊发送cookie删除信息 +async def send_cookie_delete_msg(cookie_info): + msg = '' + if cookie_info['type'] == 'public': + msg = f'公共池的{cookie_info["no"]}号cookie已失效' + elif cookie_info['type'] == 'private': + if cookie_info['uid']: + msg = f'用户{cookie_info["user_id"]}的uid{cookie_info["uid"]}的cookie已失效' + elif cookie_info['mys_id']: + msg = f'用户{cookie_info["user_id"]}的mys_id{cookie_info["mys_id"]}的cookie已失效' + if msg: + logger.info(f'---{msg}---') + for superuser in get_bot().config.SUPERUSERS: + try: + await get_bot().send_private_msg(user_id=superuser,message=msg + ',派蒙帮你删除啦!') + except Exception as e: + logger.error(f'发送cookie删除消息失败: {e}') -# 检查qq号是否绑定uid -def check_uid_by_qq(qq, uid): - if qq not in user_cookies['私人']: - return False - for cookie in user_cookies['私人'][qq]['cookies']: - if cookie['uid'] == uid: - return True - return False - -# 更新qq最后查询的uid -def update_last_query_to_qq(qq, uid): - if qq not in user_cookies['私人']: - user_cookies['私人'][qq] = {} - user_cookies['私人'][qq]['cookies'] = [] - user_cookies['私人'][qq]['last_query'] = uid - save_data() - -# 绑定uid、cookie到qq号 -async def bind_cookie(qq, uid, cookie): - if qq not in user_cookies['私人']: - user_cookies['私人'][qq] = {} - user_cookies['私人'][qq]['cookies'] = [] - f = False - for c in user_cookies['私人'][qq]['cookies']: - if c['uid'] == uid: - c['cookie'] = cookie - f = True - break - if not f: - c = {'cookie':cookie,'uid':uid} - user_cookies['私人'][qq]['cookies'].append(c) - user_cookies['私人'][qq]['last_query'] = uid - save_data() - -# 绑定cookie到公共cookie池 -async def bind_public_cookie(cookie): - if not await check_cookie(cookie): - return '这cookie没有用哦,检查一下是不是复制错了或者过期了(试试重新登录米游社再获取)' - else: - user_cookies['通用'].append({"cookie": cookie, "no": len(user_cookies['通用']) + 1}) - save_data() - return '添加公共cookie成功' - -# 获取公共池可用的cookie -async def get_public_cookie(): - for cookie in user_cookies['通用']: - if await check_cookie(cookie['cookie']): - logger.info(f'--CMgenshin:调用公共cookie池-{cookie["no"]}号执行操作==') - return cookie['cookie'] - else: - logger.info(f'--CMgenshin:公共cookie池-{cookie["no"]}号已失效--') - logger.error('--CMgenshin:原神查询公共cookie池已全部失效--') - return None - -# 获取可用的cookie,优先获取私人cookie,没有则获取公共池cookie -async def get_cookie(qq, uid, only_private = False, only_match_uid = False): - if qq not in user_cookies['私人']: - if only_private: - return None - else: - return await get_public_cookie() - else: - valid_cookie = [] - for cookie in user_cookies['私人'][qq]['cookies']: - if not await check_cookie(cookie['cookie']): - logger.error(f'--CMgenshin:qq{qq}下的cookie-{cookie["uid"]}已失效--') - else: - valid_cookie.append(cookie) - if cookie['uid'] == uid: - logger.info(f'--CMgenshin:调用qq{qq}的cookie-{cookie["uid"]}执行操作--') - return cookie['cookie'] - if valid_cookie and only_match_uid: - logger.info(f'--CMgenshin:调用qq{qq}的cookie-{cookie["uid"]}执行操作--') - return random.choice(valid_cookie)['cookie'] - else: - if only_private: - return None - else: - return await get_public_cookie() - -# 初始化读取cookie数据 -load_data() \ No newline at end of file diff --git a/hoshino/modules/botmanage/group_invite.py b/hoshino/modules/botmanage/group_invite.py index c29f603..3238092 100644 --- a/hoshino/modules/botmanage/group_invite.py +++ b/hoshino/modules/botmanage/group_invite.py @@ -14,7 +14,7 @@ async def handle_group_invite(session: RequestSession): async def handle_unknown_group_invite(session): if session.ctx['user_id'] == session.ctx['self_id']: try: - await nonebot.get_bot().send_private_msg(user_id=nonebot.get_bot().config.SUPERUSERS,message=f'群{session.ctx["group_id"]}未经允许拉了派蒙进群') + await nonebot.get_bot().send_private_msg(user_id=nonebot.get_bot().config.SUPERUSERS[0],message=f'群{session.ctx["group_id"]}未经允许拉了派蒙进群') except Exception as e: print('处理群聊邀请错误:',e) else: