From 7f2bb53c7fd9cffea8c3f08a9370a2a173426674 Mon Sep 17 00:00:00 2001 From: omg-xtao <100690902+omg-xtao@users.noreply.github.com> Date: Sat, 8 Oct 2022 16:50:02 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20=E6=94=AF=E6=8C=81=E6=8A=BD?= =?UTF-8?q?=E5=8D=A1=E8=AE=B0=E5=BD=95=E5=AF=BC=E5=85=A5/=E5=AF=BC?= =?UTF-8?q?=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- metadata/pool/pool.py | 12 + metadata/pool/pool_200.py | 11 + metadata/pool/pool_301.py | 480 +++++++++++++++ metadata/pool/pool_302.py | 562 ++++++++++++++++++ modules/apihelper/gacha_log.py | 531 +++++++++++++++++ plugins/genshin/gacha/__init__.py | 5 - plugins/genshin/gacha/gacha_log.py | 229 +++++++ resources/genshin/gacha_count/example.html | 47 ++ resources/genshin/gacha_count/gacha_count.css | 214 +++++++ .../genshin/gacha_count/gacha_count.html | 55 ++ resources/genshin/gacha_log/example.html | 77 +++ resources/genshin/gacha_log/gacha_log.css | 341 +++++++++++ resources/genshin/gacha_log/gacha_log.html | 88 +++ resources/genshin/gacha_log/img/提纳里.png | Bin 0 -> 47915 bytes 14 files changed, 2647 insertions(+), 5 deletions(-) create mode 100644 metadata/pool/pool.py create mode 100644 metadata/pool/pool_200.py create mode 100644 metadata/pool/pool_301.py create mode 100644 metadata/pool/pool_302.py create mode 100644 modules/apihelper/gacha_log.py create mode 100644 plugins/genshin/gacha/gacha_log.py create mode 100644 resources/genshin/gacha_count/example.html create mode 100644 resources/genshin/gacha_count/gacha_count.css create mode 100644 resources/genshin/gacha_count/gacha_count.html create mode 100644 resources/genshin/gacha_log/example.html create mode 100644 resources/genshin/gacha_log/gacha_log.css create mode 100644 resources/genshin/gacha_log/gacha_log.html create mode 100644 resources/genshin/gacha_log/img/提纳里.png diff --git a/metadata/pool/pool.py b/metadata/pool/pool.py new file mode 100644 index 00000000..163e9169 --- /dev/null +++ b/metadata/pool/pool.py @@ -0,0 +1,12 @@ +from metadata.pool.pool_200 import POOL_200 +from metadata.pool.pool_301 import POOL_301 +from metadata.pool.pool_302 import POOL_302 + + +def get_pool_by_id(pool_type): + if pool_type == 200: + return POOL_200 + elif pool_type == 301: + return POOL_301 + elif pool_type == 302: + return POOL_302 diff --git a/metadata/pool/pool_200.py b/metadata/pool/pool_200.py new file mode 100644 index 00000000..004e4a13 --- /dev/null +++ b/metadata/pool/pool_200.py @@ -0,0 +1,11 @@ +POOL_200 = [ + { + "five": [ + "常驻池" + ], + "four": [], + "from": "2020-09-15 06:00:00", + "name": "常驻池", + "to": "2050-09-15 17:59:59" + } +] \ No newline at end of file diff --git a/metadata/pool/pool_301.py b/metadata/pool/pool_301.py new file mode 100644 index 00000000..d9757902 --- /dev/null +++ b/metadata/pool/pool_301.py @@ -0,0 +1,480 @@ +POOL_301 = [ + { + "five": [ + "赛诺", + "温迪" + ], + "four": [ + "久岐忍", + "早柚", + "坎蒂丝" + ], + "from": "2022-09-28 06:00:00", + "name": "劈裁冥昭|杯装之诗", + "to": "2022-10-14 17:59:59" + }, + { + "five": [ + "甘雨", + "心海" + ], + "four": [ + "行秋", + "砂糖", + "多莉" + ], + "from": "2022-09-09 18:00:00", + "name": "浮生孰来|浮岳虹珠", + "to": "2022-09-27 14:59:59" + }, + { + "five": [ + "提纳里", + "钟离" + ], + "four": [ + "云堇", + "辛焱", + "班尼特" + ], + "from": "2022-08-24 06:00:00", + "name": "巡御蘙荟|陵薮市朝", + "to": "2022-09-09 17:59:59" + }, + { + "five": [ + "宵宫" + ], + "four": [ + "云堇", + "辛焱", + "班尼特" + ], + "from": "2022-08-02 18:00:00", + "name": "焰色天河", + "to": "2022-08-23 14:59:59" + }, + { + "five": [ + "枫原万叶", + "可莉" + ], + "four": [ + "凝光", + "鹿野院平藏", + "托马" + ], + "from": "2022-07-13 06:00:00", + "name": "红叶逐荒波", + "to": "2022-08-02 17:59:59" + }, + { + "five": [ + "荒泷一斗" + ], + "four": [ + "烟绯", + "芭芭拉", + "诺艾尔" + ], + "from": "2022-06-21 18:00:00", + "name": "鬼门斗宴", + "to": "2022-07-12 14:59:59" + }, + { + "five": [ + "夜兰", + "魈" + ], + "four": [ + "烟绯", + "芭芭拉", + "诺艾尔" + ], + "from": "2022-05-31 06:00:00", + "name": "素霓伣天|烟火之邀", + "to": "2022-06-21 17:59:59" + }, + { + "five": [ + "神里绫华" + ], + "four": [ + "罗莎莉亚", + "早柚", + "雷泽" + ], + "from": "2022-04-19 17:59:59", + "name": "白鹭之庭", + "to": "2022-05-31 05:59:59" + }, + { + "five": [ + "神里绫人", + "温迪" + ], + "four": [ + "香菱", + "砂糖", + "云堇" + ], + "from": "2022-03-30 06:00:00", + "name": "苍流踏花|杯装之诗", + "to": "2022-04-19 17:59:59" + }, + { + "five": [ + "雷电将军", + "珊瑚宫心海" + ], + "four": [ + "辛焱", + "九条裟罗", + "班尼特" + ], + "from": "2022-03-08 18:00:00", + "name": "影寂天下人|浮岳虹珠", + "to": "2022-03-29 14:59:59" + }, + { + "five": [ + "八重神子" + ], + "four": [ + "菲谢尔", + "迪奥娜", + "托马" + ], + "from": "2022-02-16 06:00:00", + "name": "华紫樱绯", + "to": "2022-03-08 17:59:59" + }, + { + "five": [ + "甘雨", + "钟离" + ], + "four": [ + "行秋", + "北斗", + "烟绯" + ], + "from": "2022-01-25 18:00:00", + "name": "浮生孰来|陵薮市朝", + "to": "2022-02-15 14:59:59" + }, + { + "five": [ + "申鹤", + "魈" + ], + "four": [ + "云堇", + "凝光", + "重云" + ], + "from": "2022-01-05 06:00:00", + "name": "出尘入世|烟火之邀", + "to": "2022-01-25 17:59:59" + }, + { + "five": [ + "荒泷一斗" + ], + "four": [ + "五郎", + "芭芭拉", + "香菱" + ], + "from": "2021-12-14 18:00:00", + "name": "鬼门斗宴", + "to": "2022-01-04 14:59:59" + }, + { + "five": [ + "阿贝多", + "优菈" + ], + "four": [ + "班尼特", + "诺艾尔", + "罗莎莉亚" + ], + "from": "2021-11-24 06:00:00", + "name": "深秘之息|浪涌之瞬", + "to": "2021-12-14 17:59:59" + }, + { + "five": [ + "胡桃" + ], + "four": [ + "托马", + "迪奥娜", + "早柚" + ], + "from": "2021-11-02 18:00:00", + "name": "赤团开时", + "to": "2021-11-23 14:59:59" + }, + { + "five": [ + "达达利亚" + ], + "four": [ + "凝光", + "重云", + "烟绯" + ], + "from": "2021-10-13 06:00:00", + "name": "暂别冬都", + "to": "2021-11-02 17:59:59" + }, + { + "five": [ + "珊瑚宫心海" + ], + "four": [ + "罗莎莉亚", + "北斗", + "行秋" + ], + "from": "2021-09-21 18:00:00", + "name": "浮岳虹珠", + "to": "2021-10-12 14:59:59" + }, + { + "five": [ + "雷电将军" + ], + "four": [ + "九条裟罗", + "香菱", + "砂糖" + ], + "from": "2021-09-01 06:00:00", + "name": "影寂天下人", + "to": "2021-09-21 17:59:59" + }, + { + "five": [ + "宵宫" + ], + "four": [ + "早柚", + "迪奥娜", + "辛焱" + ], + "from": "2021-08-10 18:00:00", + "name": "焰色天河", + "to": "2021-08-31 14:59:59" + }, + { + "five": [ + "神里绫华" + ], + "four": [ + "凝光", + "重云", + "烟绯" + ], + "from": "2021-07-21 06:00:00", + "name": "白鹭之庭", + "to": "2021-08-10 17:59:59" + }, + { + "five": [ + "枫原万叶" + ], + "four": [ + "罗莎莉亚", + "班尼特", + "雷泽" + ], + "from": "2021-06-29 18:00:00", + "name": "红叶逐荒波", + "to": "2021-07-20 14:59:59" + }, + { + "five": [ + "可莉" + ], + "four": [ + "芭芭拉", + "砂糖", + "菲谢尔" + ], + "from": "2021-06-09 06:00:00", + "name": "逃跑的太阳", + "to": "2021-06-29 17:59:59" + }, + { + "five": [ + "优菈" + ], + "four": [ + "辛焱", + "行秋", + "北斗" + ], + "from": "2021-05-18 18:00:00", + "name": "浪沫的旋舞", + "to": "2021-06-08 14:59:59" + }, + { + "five": [ + "钟离" + ], + "four": [ + "烟绯", + "诺艾尔", + "迪奥娜" + ], + "from": "2021-04-28 06:00:00", + "name": "陵薮市朝", + "to": "2021-05-18 17:59:59" + }, + { + "five": [ + "达达利亚" + ], + "four": [ + "罗莎莉亚", + "芭芭拉", + "菲谢尔" + ], + "from": "2021-04-06 18:00:00", + "name": "暂别冬都", + "to": "2021-04-27 14:59:59" + }, + { + "five": [ + "温迪" + ], + "four": [ + "砂糖", + "雷泽", + "诺艾尔" + ], + "from": "2021-03-17 06:00:00", + "name": "杯装之诗", + "to": "2021-04-06 15:59:59" + }, + { + "five": [ + "胡桃" + ], + "four": [ + "行秋", + "香菱", + "重云" + ], + "from": "2021-03-02 18:00:00", + "name": "赤团开时", + "to": "2021-03-16 14:59:59" + }, + { + "five": [ + "刻晴" + ], + "four": [ + "凝光", + "班尼特", + "芭芭拉" + ], + "from": "2021-02-17 18:00:00", + "name": "鱼龙灯昼", + "to": "2021-03-02 15:59:59" + }, + { + "five": [ + "魈" + ], + "four": [ + "迪奥娜", + "北斗", + "辛焱" + ], + "from": "2021-02-03 06:00:00", + "name": "烟火之邀", + "to": "2021-02-17 15:59:59" + }, + { + "five": [ + "甘雨" + ], + "four": [ + "香菱", + "行秋", + "诺艾尔" + ], + "from": "2021-01-12 18:00:00", + "name": "浮生孰来", + "to": "2021-02-02 14:59:59" + }, + { + "five": [ + "阿贝多" + ], + "four": [ + "菲谢尔", + "砂糖", + "班尼特" + ], + "from": "2020-12-23 06:00:00", + "name": "深秘之息", + "to": "2021-01-12 15:59:59" + }, + { + "five": [ + "钟离" + ], + "four": [ + "辛焱", + "雷泽", + "重云" + ], + "from": "2020-12-01 18:00:00", + "name": "陵薮市朝", + "to": "2020-12-22 14:59:59" + }, + { + "five": [ + "达达利亚" + ], + "four": [ + "迪奥娜", + "北斗", + "凝光" + ], + "from": "2020-11-11 06:00:00", + "name": "暂别冬都", + "to": "2020-12-01 15:59:59" + }, + { + "five": [ + "可莉" + ], + "four": [ + "行秋", + "诺艾尔", + "砂糖" + ], + "from": "2020-10-20 18:00:00", + "name": "闪焰的驻足", + "to": "2020-11-10 14:59:59" + }, + { + "five": [ + "温迪" + ], + "four": [ + "芭芭拉", + "菲谢尔", + "香菱" + ], + "from": "2020-9-28 06:00:00", + "name": "杯装之诗", + "to": "2020-10-18 17:59:59" + } +] \ No newline at end of file diff --git a/metadata/pool/pool_302.py b/metadata/pool/pool_302.py new file mode 100644 index 00000000..4653e896 --- /dev/null +++ b/metadata/pool/pool_302.py @@ -0,0 +1,562 @@ +POOL_302 = [ + { + "five": [ + "赤沙之杖", + "终末嗟叹之诗" + ], + "four": [ + "匣里龙吟", + "玛海菈的水色", + "西风长枪", + "祭礼残章", + "西风猎弓" + ], + "from": "2022-09-28 06:00:00", + "name": "神铸赋形", + "to": "2022-10-14 17:59:59" + }, + { + "five": [ + "阿莫斯之弓", + "不灭月华" + ], + "four": [ + "祭礼剑", + "西风大剑", + "匣里灭辰", + "昭心", + "弓藏" + ], + "from": "2022-09-09 18:00:00", + "name": "神铸赋形", + "to": "2022-09-27 14:59:59" + }, + { + "five": [ + "猎人之径", + "贯虹之槊" + ], + "four": [ + "西风剑", + "钟剑", + "西风长枪", + "西风秘典", + "绝弦" + ], + "from": "2022-08-24 06:00:00", + "name": "神铸赋形", + "to": "2022-09-09 17:59:59" + }, + { + "five": [ + "飞雷之弦振", + "斫峰之刃" + ], + "four": [ + "暗巷的酒与诗", + "暗巷猎手", + "笛剑", + "祭礼大剑", + "匣里灭辰" + ], + "from": "2022-08-02 18:00:00", + "name": "神铸赋形", + "to": "2022-08-23 14:59:59" + }, + { + "five": [ + "苍古自由之誓", + "四风原典" + ], + "four": [ + "千岩古剑", + "匣里龙吟", + "匣里灭辰", + "祭礼残章", + "绝弦" + ], + "from": "2022-07-13 06:00:00", + "name": "神铸赋形", + "to": "2022-08-02 17:59:59" + }, + { + "five": [ + "赤角石溃杵", + "尘世之锁" + ], + "four": [ + "千岩古剑", + "匣里龙吟", + "匣里灭辰", + "祭礼残章", + "绝弦" + ], + "from": "2022-06-21 18:00:00", + "name": "神铸赋形", + "to": "2022-07-12 14:59:59" + }, + { + "five": [ + "若水", + "和璞鸢" + ], + "four": [ + "千岩长枪", + "祭礼剑", + "西风大剑", + "昭心", + "祭礼弓" + ], + "from": "2022-05-31 06:00:00", + "name": "神铸赋形", + "to": "2022-06-21 17:59:59" + }, + { + "five": [ + "雾切之回光", + "无工之剑" + ], + "four": [ + "西风剑", + "钟剑", + "西风长枪", + "西风秘典", + "西风猎弓" + ], + "from": "2022-04-19 17:59:59", + "name": "神铸赋形", + "to": "2022-05-31 05:59:59" + }, + { + "five": [ + "波乱月白经津", + "终末嗟叹之诗" + ], + "four": [ + "弓藏", + "笛剑", + "流浪乐章", + "匣里灭辰", + "祭礼大剑" + ], + "from": "2022-03-30 06:00:00", + "name": "神铸赋形", + "to": "2022-04-19 17:59:59" + }, + { + "five": [ + "薙草之稻光", + "不灭月华" + ], + "four": [ + "恶王丸", + "曚云之月", + "匣里龙吟", + "西风长枪", + "祭礼残章" + ], + "from": "2022-03-08 18:00:00", + "name": "神铸赋形", + "to": "2022-03-29 14:59:59" + }, + { + "five": [ + "神乐之真意", + "磐岩结绿" + ], + "four": [ + "祭礼剑", + "雨裁", + "断浪长鳍", + "昭心", + "绝弦" + ], + "from": "2022-02-16 06:00:00", + "name": "神铸赋形", + "to": "2022-03-08 17:59:59" + }, + { + "five": [ + "贯虹之槊", + "阿莫斯之弓" + ], + "four": [ + "西风剑", + "千岩古剑", + "匣里灭辰", + "西风秘典", + "祭礼弓" + ], + "from": "2022-01-25 18:00:00", + "name": "神铸赋形", + "to": "2022-02-15 14:59:59" + }, + { + "five": [ + "息灾", + "和璞鸢" + ], + "four": [ + "笛剑", + "西风大剑", + "千岩长枪", + "流浪乐章", + "西风猎弓" + ], + "from": "2022-01-05 06:00:00", + "name": "神铸赋形", + "to": "2022-01-25 17:59:59" + }, + { + "five": [ + "赤角石溃杵", + "天空之翼" + ], + "four": [ + "暗巷闪光", + "钟剑", + "西风长枪", + "祭礼残章", + "幽夜华尔兹" + ], + "from": "2021-12-14 18:00:00", + "name": "神铸赋形", + "to": "2022-01-04 14:59:59" + }, + { + "five": [ + "苍古自由之誓", + "松籁响起之时" + ], + "four": [ + "匣里龙吟", + "祭礼大剑", + "匣里灭辰", + "暗巷的酒与诗", + "暗巷猎手" + ], + "from": "2021-11-24 06:00:00", + "name": "神铸赋形", + "to": "2021-12-14 17:59:59" + }, + { + "five": [ + "护摩之杖", + "终末嗟叹之诗" + ], + "four": [ + "祭礼剑", + "雨裁", + "断浪长鳍", + "流浪乐章", + "曚云之月" + ], + "from": "2021-11-02 18:00:00", + "name": "神铸赋形", + "to": "2021-11-23 14:59:59" + }, + { + "five": [ + "冬极白星", + "尘世之锁" + ], + "four": [ + "西风剑", + "恶王丸", + "西风长枪", + "昭心", + "弓藏" + ], + "from": "2021-10-13 06:00:00", + "name": "神铸赋形", + "to": "2021-11-02 17:59:59" + }, + { + "five": [ + "不灭月华", + "磐岩结绿" + ], + "four": [ + "笛剑", + "西风大剑", + "匣里灭辰", + "西风秘典", + "绝弦" + ], + "from": "2021-09-21 18:00:00", + "name": "神铸赋形", + "to": "2021-10-12 14:59:59" + }, + { + "five": [ + "薙草之稻光", + "无工之剑" + ], + "four": [ + "匣里龙吟", + "钟剑", + "西风长枪", + "流浪乐章", + "祭礼弓" + ], + "from": "2021-09-01 06:00:00", + "name": "神铸赋形", + "to": "2021-09-21 17:59:59" + }, + { + "five": [ + "飞雷之弦振", + "天空之刃" + ], + "four": [ + "祭礼剑", + "雨裁", + "匣里灭辰", + "祭礼残章", + "西风猎弓" + ], + "from": "2021-08-10 18:00:00", + "name": "神铸赋形", + "to": "2021-08-31 14:59:59" + }, + { + "five": [ + "雾切之回光", + "天空之脊" + ], + "four": [ + "西风剑", + "祭礼大剑", + "西风长枪", + "西风秘典", + "绝弦" + ], + "from": "2021-07-21 06:00:00", + "name": "神铸赋形", + "to": "2021-08-10 17:59:59" + }, + { + "five": [ + "苍古自由之誓", + "天空之卷" + ], + "four": [ + "暗巷闪光", + "西风大剑", + "匣里灭辰", + "暗巷的酒与诗", + "暗巷猎手" + ], + "from": "2021-06-29 18:00:00", + "name": "神铸赋形", + "to": "2021-07-20 14:59:59" + }, + { + "five": [ + "天空之傲", + "四风原典" + ], + "four": [ + "匣里龙吟", + "钟剑", + "西风长枪", + "流浪乐章", + "幽夜华尔兹" + ], + "from": "2021-06-09 06:00:00", + "name": "神铸赋形", + "to": "2021-06-29 17:59:59" + }, + { + "five": [ + "松籁响起之时", + "风鹰剑" + ], + "four": [ + "祭礼剑", + "雨裁", + "匣里灭辰", + "祭礼残章", + "弓藏" + ], + "from": "2021-05-18 18:00:00", + "name": "神铸赋形", + "to": "2021-06-08 14:59:59" + }, + { + "five": [ + "斫峰之刃", + "尘世之锁" + ], + "four": [ + "笛剑", + "千岩古剑", + "祭礼弓", + "昭心", + "千岩长枪" + ], + "from": "2021-04-28 06:00:00", + "name": "神铸赋形", + "to": "2021-05-18 17:59:59" + }, + { + "five": [ + "天空之翼", + "四风原典" + ], + "four": [ + "西风剑", + "祭礼大剑", + "暗巷猎手", + "西风秘典", + "西风长枪" + ], + "from": "2021-04-06 18:00:00", + "name": "神铸赋形", + "to": "2021-04-27 14:59:59" + }, + { + "five": [ + "终末嗟叹之诗", + "天空之刃" + ], + "four": [ + "暗巷闪光", + "西风大剑", + "西风猎弓", + "暗巷的酒与诗", + "匣里灭辰" + ], + "from": "2021-03-17 06:00:00", + "name": "神铸赋形", + "to": "2021-04-06 15:59:59" + }, + { + "five": [ + "护摩之杖", + "狼的末路" + ], + "four": [ + "匣里龙吟", + "千岩古剑", + "祭礼弓", + "流浪乐章", + "千岩长枪" + ], + "from": "2021-02-23 18:00:00", + "name": "神铸赋形", + "to": "2021-03-16 14:59:59" + }, + { + "five": [ + "磐岩结绿", + "和璞鸢" + ], + "four": [ + "笛剑", + "祭礼大剑", + "弓藏", + "昭心", + "西风长枪" + ], + "from": "2021-02-03 06:00:00", + "name": "神铸赋形", + "to": "2021-02-23 15:59:59" + }, + { + "five": [ + "阿莫斯之弓", + "天空之傲" + ], + "four": [ + "祭礼剑", + "钟剑", + "匣里灭辰", + "昭心", + "西风猎弓" + ], + "from": "2021-01-12 18:00:00", + "name": "神铸赋形", + "to": "2021-02-02 14:59:59" + }, + { + "five": [ + "斫峰之刃", + "天空之卷" + ], + "four": [ + "西风剑", + "西风大剑", + "西风长枪", + "祭礼残章", + "绝弦" + ], + "from": "2020-12-23 06:00:00", + "name": "神铸赋形", + "to": "2021-01-12 15:59:59" + }, + { + "five": [ + "贯虹之槊", + "无工之剑" + ], + "four": [ + "匣里龙吟", + "钟剑", + "西风秘典", + "西风猎弓", + "匣里灭辰" + ], + "from": "2020-12-01 18:00:00", + "name": "神铸赋形", + "to": "2020-12-22 14:59:59" + }, + { + "five": [ + "天空之翼", + "尘世之锁" + ], + "four": [ + "笛剑", + "雨裁", + "昭心", + "弓藏", + "西风长枪" + ], + "from": "2020-11-11 06:00:00", + "name": "神铸赋形", + "to": "2020-12-01 15:59:59" + }, + { + "five": [ + "四风原典", + "狼的末路" + ], + "four": [ + "祭礼剑", + "祭礼大剑", + "祭礼残章", + "祭礼弓", + "匣里灭辰" + ], + "from": "2020-10-20 18:00:00", + "name": "神铸赋形", + "to": "2020-11-10 14:59:59" + }, + { + "five": [ + "风鹰剑", + "阿莫斯之弓" + ], + "four": [ + "祭礼剑", + "祭礼大剑", + "祭礼残章", + "祭礼弓", + "匣里灭辰" + ], + "from": "2020-09-28 06:00:00", + "name": "神铸赋形", + "to": "2020-10-18 17:59:59" + } +] \ No newline at end of file diff --git a/modules/apihelper/gacha_log.py b/modules/apihelper/gacha_log.py new file mode 100644 index 00000000..3b3df4e7 --- /dev/null +++ b/modules/apihelper/gacha_log.py @@ -0,0 +1,531 @@ +import contextlib +import datetime +import json +import time +from pathlib import Path +from typing import List, Dict, Tuple, Optional, Union + +import aiofiles +from genshin import Client, InvalidAuthkey +from genshin.models import BannerType +from pydantic import BaseModel + +from core.base.assets import AssetsService +from metadata.pool.pool import get_pool_by_id +from metadata.shortname import roleToId, weaponToId +from utils.const import PROJECT_ROOT + +GACHA_LOG_PATH = PROJECT_ROOT.joinpath("data", "apihelper", "gacha_log") +GACHA_LOG_PATH.mkdir(parents=True, exist_ok=True) +GACHA_TYPE_LIST = { + BannerType.NOVICE: '新手祈愿', + BannerType.PERMANENT: '常驻祈愿', + BannerType.WEAPON: '武器祈愿', + BannerType.CHARACTER1: '角色祈愿', + BannerType.CHARACTER2: '角色祈愿' +} + + +class FiveStarItem(BaseModel): + name: str + icon: str + count: int + type: str + isUp: bool + isBig: bool + time: datetime.datetime + + +class FourStarItem(BaseModel): + name: str + icon: str + count: int + type: str + time: datetime.datetime + + +class GachaItem(BaseModel): + id: str + name: str + gacha_type: str + item_type: str + rank_type: str + time: datetime.datetime + + +class GachaLogInfo(BaseModel): + user_id: str + uid: str + update_time: datetime.datetime + item_list: Dict[str, List[GachaItem]] = { + '角色祈愿': [], + '武器祈愿': [], + '常驻祈愿': [], + '新手祈愿': [], + } + + +class Pool: + def __init__(self, five: List[str], four: List[str], name: str, to: str, **kwargs): + self.five = five + self.real_name = name + self.name = "、".join(self.five) + self.four = four + self.from_ = kwargs.get("from") + self.to = to + self.from_time = datetime.datetime.strptime(self.from_, "%Y-%m-%d %H:%M:%S") + self.to_time = datetime.datetime.strptime(self.to, "%Y-%m-%d %H:%M:%S") + self.start = self.from_time + self.start_init = False + self.end = self.to_time + self.dict = {} + self.count = 0 + + def parse(self, item: Union[FiveStarItem, FourStarItem]): + if self.from_time <= item.time <= self.to_time: + if self.dict.get(item.name): + self.dict[item.name]["count"] += 1 + else: + self.dict[item.name] = { + "name": item.name, + "icon": item.icon, + "count": 1, + "rank_type": 5 if isinstance(item, FiveStarItem) else 4, + } + + def count_item(self, item: List[GachaItem]): + for i in item: + if self.from_time <= i.time <= self.to_time: + self.count += 1 + if not self.start_init: + self.start = i.time + self.end = i.time + + def to_list(self): + return list(self.dict.values()) + + +class GachaLog: + @staticmethod + async def load_json(path): + async with aiofiles.open(path, 'r', encoding='utf-8') as f: + return json.loads(await f.read()) + + @staticmethod + async def save_json(path, data): + async with aiofiles.open(path, 'w', encoding='utf-8') as f: + if isinstance(data, dict): + return await f.write(json.dumps(data, ensure_ascii=False, indent=4)) + await f.write(data) + + @staticmethod + async def load_history_info(user_id: str, uid: str) -> Tuple[GachaLogInfo, bool]: + """读取历史抽卡记录数据 + :param user_id: 用户id + :param uid: 原神uid + :return: 抽卡记录数据 + """ + file_path = GACHA_LOG_PATH / f'{user_id}-{uid}.json' + if file_path.exists(): + return GachaLogInfo.parse_obj(await GachaLog.load_json(file_path)), True + else: + return GachaLogInfo(user_id=user_id, + uid=uid, + update_time=datetime.datetime.now()), False + + @staticmethod + async def save_gacha_log_info(user_id: str, uid: str, info: GachaLogInfo): + """保存抽卡记录数据 + :param user_id: 用户id + :param uid: 原神uid + :param info: 抽卡记录数据 + """ + save_path = GACHA_LOG_PATH / f'{user_id}-{uid}.json' + save_path_bak = GACHA_LOG_PATH / f'{user_id}-{uid}.json.bak' + # 将旧数据备份一次 + with contextlib.suppress(PermissionError): + if save_path.exists(): + if save_path_bak.exists(): + save_path_bak.unlink() + save_path.rename(save_path.parent / f'{save_path.name}.bak') + # 写入数据 + await GachaLog.save_json(save_path, info.json()) + + @staticmethod + async def gacha_log_to_uigf(user_id: str, uid: str) -> Tuple[bool, str, Optional[Path]]: + """抽卡日记转换为 UIGF 格式 + :param user_id: 用户ID + :param uid: 游戏UID + :return: 转换是否成功、转换信息、UIGF文件目录 + """ + data, state = await GachaLog.load_history_info(user_id, uid) + if not state: + return False, f'UID{uid} 还没有导入任何抽卡记录数据。', None + save_path = GACHA_LOG_PATH / f'{user_id}-{uid}-uigf.json' + uigf_dict = { + 'info': { + 'uid': uid, + 'lang': 'zh-cn', + 'export_time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + 'export_timestamp': int(time.time()), + 'export_app': 'TGPaimonBot', + 'export_app_version': "v3", + 'uigf_version': 'v2.2' + }, + 'list': [] + } + for items in data.item_list.values(): + for item in items: + uigf_dict['list'].append({ + 'gacha_type': item.gacha_type, + 'item_id': '', + 'count': '1', + 'time': item.time.strftime('%Y-%m-%d %H:%M:%S'), + 'name': item.name, + 'item_type': item.item_type, + 'rank_type': item.rank_type, + 'id': item.id, + 'uigf_gacha_type': item.gacha_type + }) + await GachaLog.save_json(save_path, uigf_dict) + return True, '', save_path + + @staticmethod + async def import_gacha_log_data(user_id: int, data: dict): + new_num = 0 + try: + uid = data['info']['uid'] + gacha_log, _ = await GachaLog.load_history_info(str(user_id), uid) + for item in data['list']: + pool_name = GACHA_TYPE_LIST[BannerType(int(item['gacha_type']))] + item_info = GachaItem.parse_obj(item) + if item_info not in gacha_log.item_list[pool_name]: + gacha_log.item_list[pool_name].append(item_info) + new_num += 1 + for i in gacha_log.item_list.values(): + i.sort(key=lambda x: (x.time, x.id)) + gacha_log.update_time = datetime.datetime.now() + await GachaLog.save_gacha_log_info(str(user_id), uid, gacha_log) + return "导入完成,本次没有新增数据" if new_num == 0 else f"导入完成,本次共新增{new_num}条抽卡记录" + except Exception: + return "导入失败,数据格式错误" + + @staticmethod + async def get_gacha_log_data(user_id: int, client: Client, authkey: str) -> str: + """ + 使用authkey获取抽卡记录数据,并合并旧数据 + :param user_id: 用户id + :param client: genshin client + :param authkey: authkey + :return: 更新结果 + """ + new_num = 0 + gacha_log, _ = await GachaLog.load_history_info(str(user_id), str(client.uid)) + try: + for pool_id, pool_name in GACHA_TYPE_LIST.items(): + async for data in client.wish_history(pool_id, authkey=authkey): + item = GachaItem( + id=str(data.id), + name=data.name, + gacha_type=str(data.banner_type.value), + item_type=data.type, + rank_type=str(data.rarity), + time=datetime.datetime(data.time.year, + data.time.month, + data.time.day, + data.time.hour, + data.time.minute, + data.time.second) + ) + + if item not in gacha_log.item_list[pool_name]: + gacha_log.item_list[pool_name].append(item) + new_num += 1 + except InvalidAuthkey: + return "更新数据失败,authkey 无效" + for i in gacha_log.item_list.values(): + i.sort(key=lambda x: (x.time, x.id)) + gacha_log.update_time = datetime.datetime.now() + await GachaLog.save_gacha_log_info(str(user_id), str(client.uid), gacha_log) + return '更新完成,本次没有新增数据' if new_num == 0 else f'更新完成,本次共新增{new_num}条抽卡记录' + + @staticmethod + def check_avatar_up(name: str, gacha_time: datetime.datetime) -> bool: + if name in {'莫娜', '七七', '迪卢克', '琴'}: + return False + elif name == "刻晴": + start_time = datetime.datetime.strptime("2021-02-17 18:00:00", "%Y-%m-%d %H:%M:%S") + end_time = datetime.datetime.strptime("2021-03-02 15:59:59", "%Y-%m-%d %H:%M:%S") + if not (start_time < gacha_time < end_time): + return False + elif name == "提纳里": + start_time = datetime.datetime.strptime("2022-08-24 06:00:00", "%Y-%m-%d %H:%M:%S") + end_time = datetime.datetime.strptime("2022-09-09 17:59:59", "%Y-%m-%d %H:%M:%S") + if not (start_time < gacha_time < end_time): + return False + return True + + @staticmethod + async def get_all_5_star_items(data: List[GachaItem], assets: AssetsService, pool_name: str = "角色祈愿"): + """ + 获取所有5星角色 + :param data: 抽卡记录 + :param assets: 资源服务 + :param pool_name: 池子名称 + :return: 5星角色列表 + """ + count = 0 + result = [] + for item in data: + count += 1 + if item.rank_type == '5': + if item.item_type == "角色" and pool_name in {"角色祈愿", "常驻祈愿"}: + result.append( + FiveStarItem( + name=item.name, + icon=(await assets.avatar(roleToId(item.name)).icon()).as_uri(), + count=count, + type="角色", + isUp=GachaLog.check_avatar_up(item.name, item.time) if pool_name == "角色祈愿" else False, + isBig=(not result[-1].isUp) if result and pool_name == "角色祈愿" else False, + time=item.time, + ) + ) + elif item.item_type == "武器" and pool_name in {"武器祈愿", "常驻祈愿"}: + result.append( + FiveStarItem( + name=item.name, + icon=(await assets.weapon(weaponToId(item.name)).icon()).as_uri(), + count=count, + type="武器", + isUp=False, + isBig=False, + time=item.time, + ) + ) + count = 0 + result.reverse() + return result, count + + @staticmethod + async def get_all_4_star_items(data: List[GachaItem], assets: AssetsService): + """ + 获取 no_fout_star + :param data: 抽卡记录 + :param assets: 资源服务 + :return: no_fout_star + """ + count = 0 + result = [] + for item in data: + count += 1 + if item.rank_type == '4': + if item.item_type == "角色": + result.append( + FourStarItem( + name=item.name, + icon=(await assets.avatar(roleToId(item.name)).icon()).as_uri(), + count=count, + type="角色", + time=item.time, + ) + ) + elif item.item_type == "武器": + result.append( + FourStarItem( + name=item.name, + icon=(await assets.weapon(weaponToId(item.name)).icon()).as_uri(), + count=count, + type="武器", + time=item.time, + ) + ) + count = 0 + result.reverse() + return result, count + + @staticmethod + def get_301_pool_data(total: int, + all_five: List[FiveStarItem], + no_five_star: int, + no_four_star: int): + # 总共五星 + five_star = len(all_five) + five_star_up = len([i for i in all_five if i.isUp]) + five_star_big = len([i for i in all_five if i.isBig]) + # 五星平均 + five_star_avg = round(total / five_star, 2) if five_star != 0 else 0 + # 小保底不歪 + small_protect = round((five_star_up - five_star_big) / (five_star - five_star_big) * 100.0, 1) if \ + five_star - five_star_big != 0 else "0.0" + # 五星常驻 + five_star_const = five_star - five_star_up + # UP 平均 + up_avg = round(total / five_star_up, 2) if five_star_up != 0 else 0 + # UP 花费原石 + up_cost = sum(i.count * 160 for i in all_five if i.isUp) + up_cost = f"{round(up_cost / 10000, 2)}w" if up_cost >= 10000 else up_cost + return [ + [ + {"num": no_five_star, "unit": "抽", "lable": "未出五星"}, + {"num": five_star, "unit": "个", "lable": "五星"}, + {"num": five_star_avg, "unit": "抽", "lable": "五星平均"}, + {"num": small_protect, "unit": "%", "lable": "小保底不歪"}, + ], + [ + {"num": no_four_star, "unit": "抽", "lable": "未出四星"}, + {"num": five_star_const, "unit": "个", "lable": "五星常驻"}, + {"num": up_avg, "unit": "抽", "lable": "UP平均"}, + {"num": up_cost, "unit": "", "lable": "UP花费原石"}, + ] + ] + + @staticmethod + def get_200_pool_data(total: int, all_five: List[FiveStarItem], all_four: List[FourStarItem], + no_five_star: int, no_four_star: int): + # 总共五星 + five_star = len(all_five) + # 五星平均 + five_star_avg = round(total / five_star, 2) if five_star != 0 else 0 + # 五星武器 + five_star_weapon = len([i for i in all_five if i.type == "武器"]) + # 总共四星 + four_star = len(all_four) + # 四星平均 + four_star_avg = round(total / four_star, 2) if four_star != 0 else 0 + # 四星最多 + four_star_name_list = [i.name for i in all_four] + four_star_max = max(four_star_name_list, key=four_star_name_list.count) + four_star_max_count = four_star_name_list.count(four_star_max) + return [ + [ + {"num": no_five_star, "unit": "抽", "lable": "未出五星"}, + {"num": five_star, "unit": "个", "lable": "五星"}, + {"num": five_star_avg, "unit": "抽", "lable": "五星平均"}, + {"num": five_star_weapon, "unit": "个", "lable": "五星武器"}, + ], + [ + {"num": no_four_star, "unit": "抽", "lable": "未出四星"}, + {"num": four_star, "unit": "个", "lable": "四星"}, + {"num": four_star_avg, "unit": "抽", "lable": "四星平均"}, + {"num": four_star_max_count, "unit": four_star_max, "lable": "四星最多"}, + ] + ] + + @staticmethod + def get_302_pool_data(total: int, all_five: List[FiveStarItem], all_four: List[FourStarItem], + no_five_star: int, no_four_star: int): + # 总共五星 + five_star = len(all_five) + # 五星平均 + five_star_avg = round(total / five_star, 2) if five_star != 0 else 0 + # 四星武器 + four_star_weapon = len([i for i in all_four if i.type == "武器"]) + # 总共四星 + four_star = len(all_four) + # 四星平均 + four_star_avg = round(total / four_star, 2) if four_star != 0 else 0 + # 四星最多 + four_star_name_list = [i.name for i in all_four] + four_star_max = max(four_star_name_list, key=four_star_name_list.count) + four_star_max_count = four_star_name_list.count(four_star_max) + return [ + [ + {"num": no_five_star, "unit": "抽", "lable": "未出五星"}, + {"num": five_star, "unit": "个", "lable": "五星"}, + {"num": five_star_avg, "unit": "抽", "lable": "五星平均"}, + {"num": four_star_weapon, "unit": "个", "lable": "四星武器"}, + ], + [ + {"num": no_four_star, "unit": "抽", "lable": "未出四星"}, + {"num": four_star, "unit": "个", "lable": "四星"}, + {"num": four_star_avg, "unit": "抽", "lable": "四星平均"}, + {"num": four_star_max_count, "unit": four_star_max, "lable": "四星最多"}, + ] + ] + + @staticmethod + async def get_analysis(user_id: int, client: Client, pool: BannerType, assets: AssetsService): + """ + 获取抽卡记录分析数据 + :param user_id: 用户id + :param client: genshin client + :param pool: 池子类型 + :param assets: 资源服务 + :return: 分析数据 + """ + gacha_log, status = await GachaLog.load_history_info(str(user_id), str(client.uid)) + if not status: + return "获取数据失败,未找到抽卡记录" + pool_name = GACHA_TYPE_LIST[pool] + data = gacha_log.item_list[pool_name] + total = len(data) + if total == 0: + return "获取数据失败,未找到抽卡记录" + all_five, no_five_star = await GachaLog.get_all_5_star_items(data, assets, pool_name) + all_four, no_four_star = await GachaLog.get_all_4_star_items(data, assets) + summon_data = None + if pool == BannerType.CHARACTER1: + summon_data = GachaLog.get_301_pool_data(total, all_five, no_five_star, no_four_star) + elif pool == BannerType.WEAPON: + summon_data = GachaLog.get_302_pool_data(total, all_five, all_four, no_five_star, no_four_star) + elif pool == BannerType.PERMANENT: + summon_data = GachaLog.get_200_pool_data(total, all_five, all_four, no_five_star, no_four_star) + last_time = data[0].time.strftime("%Y-%m-%d %H:%M") + first_time = data[-1].time.strftime("%Y-%m-%d %H:%M") + return { + "uid": client.uid, + "allNum": total, + "type": pool.value, + "typeName": pool_name, + "line": summon_data, + "firstTime": first_time, + "lastTime": last_time, + "fiveLog": all_five, + "fourLog": all_four[:18], + } + + @staticmethod + async def get_pool_analysis(user_id: int, client: Client, pool: BannerType, assets: AssetsService, group: bool): + """ + 获取抽卡记录分析数据 + :param user_id: 用户id + :param client: genshin client + :param pool: 池子类型 + :param assets: 资源服务 + :param group: 是否群组 + :return: 分析数据 + """ + gacha_log, status = await GachaLog.load_history_info(str(user_id), str(client.uid)) + if not status: + return "获取数据失败,未找到抽卡记录" + pool_name = GACHA_TYPE_LIST[pool] + data = gacha_log.item_list[pool_name] + total = len(data) + if total == 0: + return "获取数据失败,未找到抽卡记录" + all_five, _ = await GachaLog.get_all_5_star_items(data, assets, pool_name) + all_four, _ = await GachaLog.get_all_4_star_items(data, assets) + pool_data = [] + up_pool_data = [Pool(**i) for i in get_pool_by_id(pool.value)] + for up_pool in up_pool_data: + for item in all_five: + up_pool.parse(item) + for item in all_four: + up_pool.parse(item) + up_pool.count_item(data) + for up_pool in up_pool_data: + pool_data.append({ + "count": up_pool.count, + "list": up_pool.to_list(), + "name": up_pool.name, + "start": up_pool.start.strftime("%Y-%m-%d"), + "end": up_pool.end.strftime("%Y-%m-%d"), + }) + pool_data = [i for i in pool_data if i["count"] > 0] + return { + "uid": client.uid, + "typeName": pool_name, + "pool": pool_data[:6] if group else pool_data, + "hasMore": len(pool_data) > 6, + } diff --git a/plugins/genshin/gacha/__init__.py b/plugins/genshin/gacha/__init__.py index 6ae1c8ec..e69de29b 100644 --- a/plugins/genshin/gacha/__init__.py +++ b/plugins/genshin/gacha/__init__.py @@ -1,5 +0,0 @@ -from .gacha import Gacha - - -class GachaPlugins(Gacha): - pass diff --git a/plugins/genshin/gacha/gacha_log.py b/plugins/genshin/gacha/gacha_log.py new file mode 100644 index 00000000..152ce907 --- /dev/null +++ b/plugins/genshin/gacha/gacha_log.py @@ -0,0 +1,229 @@ +import json +import os +from io import BytesIO + +from genshin.models import BannerType +from pyppeteer import launch +from telegram import Update, User +from telegram.constants import ChatAction +from telegram.ext import CallbackContext, CommandHandler, MessageHandler, filters, ConversationHandler + +from core.base.assets import AssetsService +from core.baseplugin import BasePlugin +from core.cookies.error import CookiesNotFoundError +from core.plugin import Plugin, handler, conversation +from core.template import TemplateService +from core.user.error import UserNotFoundError +from modules.apihelper.gacha_log import GachaLog as GachaLogService +from utils.bot import get_all_args +from utils.decorators.error import error_callable +from utils.decorators.restricts import restricts +from utils.helpers import get_genshin_client +from utils.log import logger + +INPUT_URL, INPUT_FILE = 10100, 10101 + + +class GachaLog(Plugin.Conversation, BasePlugin.Conversation): + """ 抽卡记录导入/导出/分析""" + + def __init__(self, template_service: TemplateService = None, assets: AssetsService = None): + self.template_service = template_service + self.browser: launch = None + self.current_dir = os.getcwd() + self.resources_dir = os.path.join(self.current_dir, "resources") + self.character_gacha_card = {} + self.user_time = {} + self.assets_service = assets + + @staticmethod + def from_url_get_authkey(url: str) -> str: + """从 UEL 解析 authkey + :param url: URL + :return: authkey + """ + try: + return url.split("authkey=")[1].split("&")[0] + except IndexError: + return url + + @staticmethod + async def _refresh_user_data(user: User, data: dict = None, authkey: str = None) -> str: + """刷新用户数据 + :param user: 用户 + :param data: 数据 + :param authkey: 认证密钥 + :return: 返回信息 + """ + try: + logger.debug("尝试获取已绑定的原神账号") + client = await get_genshin_client(user.id) + if authkey: + return await GachaLogService.get_gacha_log_data(user.id, client, authkey) + if data: + return await GachaLogService.import_gacha_log_data(user.id, data) + except (UserNotFoundError, CookiesNotFoundError): + logger.info(f"未查询到用户({user.full_name} {user.id}) 所绑定的账号信息") + return "未查询到您所绑定的账号信息,请先私聊派蒙绑定账号" + + @conversation.entry_point + @handler(CommandHandler, command="gacha_log_refresh", filters=filters.ChatType.PRIVATE, block=True) + @handler(MessageHandler, filters=filters.Regex("^更新抽卡记录(.*)") & filters.ChatType.PRIVATE, block=True) + @restricts() + @error_callable + async def command_start(self, update: Update, context: CallbackContext) -> int: + message = update.effective_message + user = update.effective_user + args = get_all_args(context) + if not args: + await message.reply_text("请发送从游戏中获取到的抽卡记录链接\n\n" + "获取抽卡记录链接教程:https://paimon.moe/wish/import") + return INPUT_URL + authkey = self.from_url_get_authkey(args[0]) + data = await self._refresh_user_data(user, authkey=authkey) + await message.reply_text(data) + + @conversation.state(state=INPUT_URL) + @handler.message(filters=filters.TEXT & ~filters.COMMAND, + block=True) + @restricts() + @error_callable + async def import_data_from_url(self, update: Update, _: CallbackContext) -> int: + message = update.effective_message + user = update.effective_user + authkey = self.from_url_get_authkey(message.text) + reply = await message.reply_text("正在从米哈游服务器获取数据,请稍后") + text = await self._refresh_user_data(user, authkey=authkey) + await reply.edit_text(text) + return ConversationHandler.END + + @handler(CommandHandler, command="gacha_log_import", filters=filters.ChatType.PRIVATE, block=True) + @handler(MessageHandler, filters=filters.Regex("^导入抽卡记录(.*)") & filters.ChatType.PRIVATE, block=True) + @restricts() + @error_callable + async def command_start_import(self, update: Update, _: CallbackContext) -> None: + message = update.effective_message + user = update.effective_user + if message.reply_to_message: + document = message.reply_to_message.document + else: + document = message.document + if not document: + await message.reply_text("请回复符合 UIGF 标准的抽卡记录文件") + return + if not document.file_name.endswith(".json"): + await message.reply_text("文件格式错误,请发送符合 UIGF 标准的抽卡记录文件") + return + if document.file_size > 50 * 1024 * 1024: + await message.reply_text("文件过大,请发送小于 50MB 的文件") + return + try: + data = BytesIO() + await (await document.get_file()).download(out=data) + # bytesio to json + data = data.getvalue().decode("utf-8") + data = json.loads(data) + except Exception as exc: + logger.error(f"文件解析失败:{repr(exc)}") + await message.reply_text("文件解析失败,请检查文件是否符合 UIGF 标准") + return + reply = await message.reply_text("文件解析成功,正在导入数据") + try: + text = await self._refresh_user_data(user, data=data) + except Exception as exc: + logger.error(f"文件解析失败:{repr(exc)}") + await reply.edit_text("文件解析失败,请检查文件是否符合 UIGF 标准") + return + await reply.edit_text(text) + return + + @handler(CommandHandler, command="gacha_log_export", filters=filters.ChatType.PRIVATE, block=True) + @handler(MessageHandler, filters=filters.Regex("^导出抽卡记录(.*)") & filters.ChatType.PRIVATE, block=True) + @restricts() + @error_callable + async def command_start_export(self, update: Update, _: CallbackContext) -> None: + message = update.effective_message + user = update.effective_user + try: + client = await get_genshin_client(user.id) + state, text, path = await GachaLogService.gacha_log_to_uigf(str(user.id), str(client.uid)) + if state: + await message.reply_document(document=open(path, "rb+"), caption="抽卡记录导出文件") + else: + await message.reply_text(text) + except (UserNotFoundError, CookiesNotFoundError): + logger.info(f"未查询到用户({user.full_name} {user.id}) 所绑定的账号信息") + await message.reply_text("未查询到您所绑定的账号信息,请先私聊派蒙绑定账号") + return + + @handler(CommandHandler, command="gacha_log", block=True) + @handler(MessageHandler, filters=filters.Regex("^抽卡记录(.*)"), block=True) + @restricts() + @error_callable + async def command_start_analysis(self, update: Update, context: CallbackContext) -> None: + message = update.effective_message + user = update.effective_user + pool_type = BannerType.CHARACTER1 + if args := get_all_args(context): + if "武器" in args: + pool_type = BannerType.WEAPON + elif "常驻" in args: + pool_type = BannerType.STANDARD + logger.info(f"用户 {user.full_name}[{user.id}] 抽卡记录命令请求 || 参数 {pool_type.name}") + try: + client = await get_genshin_client(user.id) + data = await GachaLogService.get_analysis(user.id, client, pool_type, self.assets_service) + if isinstance(data, str): + reply_message = await message.reply_text(data) + else: + await message.reply_chat_action(ChatAction.UPLOAD_PHOTO) + png_data = await self.template_service.render('genshin/gacha_log', "gacha_log.html", data, + full_page=True, query_selector=".body_box") + reply_message = await message.reply_photo(png_data) + if filters.ChatType.GROUPS.filter(message): + self._add_delete_message_job(context, reply_message.chat_id, reply_message.message_id, 300) + self._add_delete_message_job(context, message.chat_id, message.message_id, 300) + except (UserNotFoundError, CookiesNotFoundError): + logger.info(f"未查询到用户({user.full_name} {user.id}) 所绑定的账号信息") + await message.reply_text("未查询到您所绑定的账号信息,请先私聊派蒙绑定账号") + return + + @handler(CommandHandler, command="gacha_count", block=True) + @handler(MessageHandler, filters=filters.Regex("^抽卡统计(.*)"), block=True) + @restricts() + @error_callable + async def command_start_count(self, update: Update, context: CallbackContext) -> None: + message = update.effective_message + user = update.effective_user + pool_type = BannerType.CHARACTER1 + if args := get_all_args(context): + if "武器" in args: + pool_type = BannerType.WEAPON + elif "常驻" in args: + pool_type = BannerType.STANDARD + logger.info(f"用户 {user.full_name}[{user.id}] 抽卡统计命令请求 || 参数 {pool_type.name}") + try: + client = await get_genshin_client(user.id) + group = filters.ChatType.GROUPS.filter(message) + data = await GachaLogService.get_pool_analysis(user.id, client, pool_type, self.assets_service, group) + if isinstance(data, str): + reply_message = await message.reply_text(data) + else: + await message.reply_chat_action(ChatAction.UPLOAD_PHOTO) + document = False + if data["hasMore"] and not group: + document = True + data["hasMore"] = False + png_data = await self.template_service.render('genshin/gacha_count', "gacha_count.html", data, + full_page=True, query_selector=".body_box") + if document: + reply_message = await message.reply_document(png_data, filename="抽卡统计.png") + else: + reply_message = await message.reply_photo(png_data) + if filters.ChatType.GROUPS.filter(message): + self._add_delete_message_job(context, reply_message.chat_id, reply_message.message_id, 300) + self._add_delete_message_job(context, message.chat_id, message.message_id, 300) + except (UserNotFoundError, CookiesNotFoundError): + logger.info(f"未查询到用户({user.full_name} {user.id}) 所绑定的账号信息") + await message.reply_text("未查询到您所绑定的账号信息,请先私聊派蒙绑定账号") + return diff --git a/resources/genshin/gacha_count/example.html b/resources/genshin/gacha_count/example.html new file mode 100644 index 00000000..983e6798 --- /dev/null +++ b/resources/genshin/gacha_count/example.html @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + +
+
+
ID: 10001
+

抽卡统计-角色祈愿

+ +
+
+
+
+

「枫原万叶、可莉」

+ 98抽 +
+ 2022-08-02 - 2022-08-02 +
+
+
+
+ 20 + +
+
+
+
*完整数据请私聊查看
+ +
+ + diff --git a/resources/genshin/gacha_count/gacha_count.css b/resources/genshin/gacha_count/gacha_count.css new file mode 100644 index 00000000..191d032d --- /dev/null +++ b/resources/genshin/gacha_count/gacha_count.css @@ -0,0 +1,214 @@ +@font-face { + font-family: "tttgbnumber"; + src: url("./../../fonts/tttgbnumber.ttf"); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: "HYWenHei-55W"; + src: url("./../../fonts/汉仪文黑-85W.ttf"); + font-weight: normal; + font-style: normal; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; + user-select: none; +} + +body { + font-size: 16px; + width: 530px; + color: #1e1f20; + transform: scale(1.5); + transform-origin: 0 0; +} + +.container { + width: 530px; + padding: 20px 15px 10px 15px; + background-color: #f5f6fb; +} + +.head_box { + border-radius: 15px; + font-family: tttgbnumber, serif; + padding: 10px 20px; + position: relative; + box-shadow: 0 5px 10px 0 rgb(0 0 0 / 15%); + +} + +.head_box .id_text { + font-size: 24px; +} + +.head_box .day_text { + font-size: 20px; +} + +.head_box .genshin_logo { + position: absolute; + top: 1px; + right: 15px; + width: 97px; +} + +.base_info { + position: relative; + padding-left: 10px; +} + +.uid { + font-family: tttgbnumber, serif; +} + +.pool_box { + font-family: HYWenHei-55W, serif; + border-radius: 12px; + margin-top: 20px; + margin-bottom: 20px; + padding: 10px 5px 5px 5px; + background: #fff; + box-shadow: 0 5px 10px 0 rgb(0 0 0 / 15%); + position: relative; +} + +.title_box { + display: flex; + align-items: center; + margin-bottom: 10px; +} + +.title { + white-space: nowrap; + max-width: 210px; + overflow: hidden; +} + +.name_box { + display: flex; + align-items: center; + flex: 1; +} + +.title_box .date { + margin-right: 10px; +} + +.list_box { + display: flex; + flex-wrap: wrap; +} + +.item { + margin: 0 0 10px 10px; + border-radius: 7px; + overflow: hidden; + box-shadow: 0 2px 6px 0 rgb(132 93 90 / 30%); + height: 70px; + width: 70px; + background: #e9e5dc; + position: relative; +} + +.item .role_img { + width: 100%; + overflow: hidden; + background-size: 100%; + background-repeat: no-repeat; + position: absolute; + top: 0; + /* filter: contrast(95%); */ +} + +.item .num { + position: absolute; + top: 0; + right: 0; + z-index: 9; + font-size: 18px; + text-align: center; + color: #fff; + border-radius: 3px; + padding: 1px 5px; + background: rgb(0 0 0 / 50%); + font-family: "tttgbnumber", serif; +} + +.label_301 { + background-color: rgb(235 106 75); +} + +.label_302 { + background-color: #e69449; +} + +.label_200 { + background-color: #757cc8; +} + +.label { + color: #fff; + border-radius: 10px; + font-size: 16px; + padding: 2px 7px; + vertical-align: 2px; +} + +.bg5 { + background-image: url(./../abyss/background/roleStarBg5.png); + width: 100%; + height: 70px; + /* filter: brightness(1.1); */ + background-size: 100%; + background-repeat: no-repeat; +} + +.bg4 { + width: 100%; + height: 70px; + background-image: url(./../abyss/background/roleStarBg4.png); + background-size: 100%; + background-repeat: no-repeat; +} + +.list_box .item .life1 { + background-color: #62a8ea; +} + +.list_box .item .life2 { + background-color: #62a8ea; +} + +.list_box .item .life3 { + background-color: #45b97c; +} + +.list_box .item .life4 { + background-color: #45b97c; +} + +.list_box .item .life5 { + background-color: #ff5722; +} + +.list_box .item .life6 { + background-color: #ff5722; +} + +.logo { + font-size: 14px; + font-family: "tttgbnumber", serif; + text-align: center; + color: #7994a7; +} + +.hasMore { + font-size: 12px; + margin: -6px 0 10px 6px; + color: #7f858a; +} diff --git a/resources/genshin/gacha_count/gacha_count.html b/resources/genshin/gacha_count/gacha_count.html new file mode 100644 index 00000000..0a834ebb --- /dev/null +++ b/resources/genshin/gacha_count/gacha_count.html @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + +
+
+
ID: {{uid}}
+

抽卡统计-{{typeName}}

+ +
+ {% for val in pool %} +
+
+
+

「{{val.name}}」

+ {{val.count}}抽 +
+ {% if typeName != "常驻祈愿" %} + {{val.start}} - {{val.end}} + {% endif %} +
+
+ {% for v in val.list %} +
+
+ {{v.count}} + +
+ {% endfor %} +
+
+ {% endfor %} + {% if hasMore %} +
*完整数据请私聊查看
+ {% endif %} + +
+ + diff --git a/resources/genshin/gacha_log/example.html b/resources/genshin/gacha_log/example.html new file mode 100644 index 00000000..ac1af953 --- /dev/null +++ b/resources/genshin/gacha_log/example.html @@ -0,0 +1,77 @@ + + + + + + + + + + + + + +
+
+ +
+
+ ID: 10001 +
+

+ 81抽 + 角色祈愿池 +

+ +
+ +
+
数据总览
+
+
+
1
+
未出五星
+
+
+ +
+ + 五星历史 2022-10-07 01:10 ~ 2022-10-07 23:10 + +
+ + +
+
+ UP + + +
80
+
+
+ +
+ + 四星最近历史 + +
+ +
+
+ + +
10
+
+
+
+ +
+
+ + \ No newline at end of file diff --git a/resources/genshin/gacha_log/gacha_log.css b/resources/genshin/gacha_log/gacha_log.css new file mode 100644 index 00000000..3fc58878 --- /dev/null +++ b/resources/genshin/gacha_log/gacha_log.css @@ -0,0 +1,341 @@ +@font-face { + font-family: "tttgbnumber"; + src: url("./../../fonts/tttgbnumber.ttf"); + font-weight: normal; + font-style: normal; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; + user-select: none; +} + +body { + font-size: 18px; + color: #1e1f20; + font-family: PingFangSC-Medium, PingFang SC, sans-serif; + transform: scale(1.5); + transform-origin: 0 0; + width: 510px; +} + +.container { + width: 510px; + padding: 20px 15px 10px 15px; + background-color: #f5f6fb; +} + +.head_box { + border-radius: 15px; + font-family: tttgbnumber, sans-serif; + padding: 10px 20px; + position: relative; + box-shadow: 0 5px 10px 0 rgb(0 0 0 / 15%); +} + +.head_box .id_text { + font-size: 24px; +} + +.head_box .day_text { + font-size: 20px; +} + +.head_box .genshin_logo { + position: absolute; + top: 1px; + right: 15px; + width: 97px; +} + +.logo { + font-size: 12px; + font-family: "tttgbnumber", serif; + text-align: center; + color: #7994a7; + position: relative; + padding-left: 10px; +} + +.data_box { + border-radius: 15px; + margin-top: 20px; + margin-bottom: 10px; + padding: 20px 0 5px 10px; + background: #fff; + box-shadow: 0 5px 10px 0 rgb(0 0 0 / 15%); + position: relative; +} + +.tab_lable { + position: absolute; + top: -10px; + left: -8px; + background: #d4b98c; + color: #fff; + font-size: 14px; + padding: 3px 10px; + border-radius: 15px 0 15px 15px; + z-index: 20; +} + +.data_line { + display: flex; + justify-content: space-around; + margin-bottom: 14px; + padding-right: 10px; +} + +.data_line_item { + width: 100px; + text-align: center; + + /* margin: 0 20px; */ +} + +.num { + font-family: tttgbnumber, serif; + font-size: 24px; +} + +.num .unit { + font-size: 12px; +} + +.data_box .lable { + font-size: 14px; + color: #7f858a; + line-height: 1; + margin-top: 3px; +} + +.info_box_border { + border-radius: 15px; + + /* margin-top: 20px; */ + margin-bottom: 20px; + padding: 6px 0 5px 10px; + background: #fff; + box-shadow: 0 5px 10px 0 rgb(0 0 0 / 15%); + position: relative; +} + +.card_list { + display: flex; + flex-wrap: wrap; + justify-content: flex-start; +} + +.card_list .item { + margin: 0 8px 10px 0; + border-radius: 7px; + box-shadow: 0 2px 6px 0 rgb(132 93 90 / 30%); + height: 90px; + position: relative; + overflow: hidden; + background: #e7e5d9; +} + +.card_list .item img { + width: 70px; + height: 70px; + border-radius: 7px 7px 20px 0; +} + +.card_list .item.star5 img { + background-image: url(./../abyss/background/roleStarBg5.png); + width: 100%; + height: 70px; + /* filter: brightness(1.1); */ + background-size: 100%; + background-repeat: no-repeat; +} + +.card_list .item.star4 img { + width: 100%; + height: 70px; + background-image: url(./../abyss/background/roleStarBg4.png); + background-size: 100%; + background-repeat: no-repeat; +} + +.card_list .item .num { + position: absolute; + top: 0; + right: 0; + z-index: 9; + font-size: 18px; + text-align: center; + color: #fff; + border-radius: 3px; + padding: 1px 5px; + background: rgb(0 0 0 / 50%); + font-family: "tttgbnumber", serif; +} + +.card_list .item .name, +.card_list .item .num_name { + position: absolute; + top: 71px; + left: 0; + z-index: 9; + font-size: 12px; + text-align: center; + width: 100%; + height: 16px; + line-height: 18px; +} + +.card_list .item .num_name { + font-family: "tttgbnumber", serif; + font-size: 16px; +} + +.base_info { + position: relative; + padding-left: 10px; + margin: 5px 10px; +} + +.uid::before { + content: " "; + position: absolute; + width: 5px; + height: 24px; + border-radius: 1px; + left: 0; + top: 0; + background: #d3bc8d; +} + +.label_301 { + background-color: rgb(235 106 75); +} + +.label_302 { + background-color: #e69449; +} + +.label_200 { + background-color: #757cc8; +} + +.label { + color: #fff; + border-radius: 10px; + font-size: 12px; + padding: 2px 7px; + vertical-align: 2px; +} + +.ritem { + display: flex; + font-size: 12px; + margin-bottom: 5px; +} + +.info_role { + display: flex; + flex-wrap: wrap; + padding: 0 0 5px 9px; +} + +.ritem .role { + width: 20px; + height: 20px; + background-color: #ffb285; + border-radius: 100%; +} + +.ritem .weapon_box { + overflow: hidden; + width: 20px; + height: 20px; + border-radius: 100%; +} + +.ritem .weapon { + width: 20px; + height: 20px; + background-color: #ffb285; + border-radius: 100%; + transform: scale(1.5); + -webkit-transform: scale(1.5); + +} + +.ritem .role_text { + margin: 2px 3px 0 2px; + display: flex; + align-items: baseline; +} + +.ritem .role_name { + width: 24px; + white-space: nowrap; + overflow: hidden; +} + +.ritem .role_num { + width: 24px; +} + +.line_box { + height: 32px; + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + font-size: 12px; + color: #7d7d7d; + padding-bottom: 5px; +} + +.line_box .line { + height: 2px; + flex-grow: 1; + background-color: #ebebeb; + margin: 0 10px; +} + +.red { + color: #f21000; +} + +.orange { + color: #ff8d00; +} + +.green { + color: #12d88c; +} + +.blue { + color: #4169e1; +} + +.purple { + color: #7500ff; +} + +.minimum { + position: absolute; + top: 0; + right: 0; + z-index: 9; + font-size: 12px; + text-align: center; + color: #fff; + border-radius: 3px; + padding: 1px 3px; + background-color: rgb(0 0 0 / 80%); + font-family: "tttgbnumber", serif; +} + +.hasMore { + font-size: 12px; + margin: 6px 0; + color: #7f858a; +} diff --git a/resources/genshin/gacha_log/gacha_log.html b/resources/genshin/gacha_log/gacha_log.html new file mode 100644 index 00000000..0f2b45b2 --- /dev/null +++ b/resources/genshin/gacha_log/gacha_log.html @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + +
+
+ +
+
+ ID: {{ uid }} +
+

+ {{ allNum }}抽 + {{ typeName }}池 +

+ +
+ +
+
数据总览
+ {% for val in line %} +
+ {% for item in val %} +
+
{{item.num}}{{item.unit}}
+
{{item.lable}}
+
+ {% endfor %} +
+ {% endfor %} + +
+ + 五星历史 {{firstTime}} ~ {{lastTime}} + +
+ + +
+ {% for val in fiveLog %} +
+ {% if val.isUp %} + UP + {% endif %} + + +
{{ val.count }}
+
+ {% endfor %} +
+ +
+ + 四星最近历史 + +
+ +
+ {% for val in fourLog %} +
+ + +
{{ val.count }}
+
+ {% endfor %} +
+
+ +
+
+ + \ No newline at end of file diff --git a/resources/genshin/gacha_log/img/提纳里.png b/resources/genshin/gacha_log/img/提纳里.png new file mode 100644 index 0000000000000000000000000000000000000000..fb923acba960da288c7370e3f7c4a1e3db807e1b GIT binary patch literal 47915 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