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 0000000..163e916
--- /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 0000000..004e4a1
--- /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 0000000..d975790
--- /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 0000000..4653e89
--- /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 0000000..3b3df4e
--- /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 6ae1c8e..e69de29 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 0000000..152ce90
--- /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 0000000..983e679
--- /dev/null
+++ b/resources/genshin/gacha_count/example.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
ID: 10001
+
抽卡统计-角色祈愿
+
+
+
+
+
+
2022-08-02 - 2022-08-02
+
+
+
+
+
20
+
+
+
+
+
*完整数据请私聊查看
+
Template By Yunzai-Bot
+
+
+
diff --git a/resources/genshin/gacha_count/gacha_count.css b/resources/genshin/gacha_count/gacha_count.css
new file mode 100644
index 0000000..191d032
--- /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 0000000..0a834eb
--- /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 %}
+
Template By Yunzai-Bot
+
+
+
diff --git a/resources/genshin/gacha_log/example.html b/resources/genshin/gacha_log/example.html
new file mode 100644
index 0000000..ac1af95
--- /dev/null
+++ b/resources/genshin/gacha_log/example.html
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ID: 10001
+
+
+ 81抽
+ 角色祈愿池
+
+
+
+
+
+
数据总览
+
+
+
+
+ 五星历史 2022-10-07 01:10 ~ 2022-10-07 23:10
+
+
+
+
+
+
+
UP
+
+
+
80
+
+
+
+
+
+ 四星最近历史
+
+
+
+
+
+
+
+
10
+
+
+
+
Template By Yunzai-Bot
+
+
+
+
\ 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 0000000..3fc5887
--- /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 0000000..0f2b45b
--- /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 %}
+
+
+
Template By Yunzai-Bot
+
+
+
+
\ 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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