pymecli 0.2.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- api/__init__.py +0 -0
- api/v1/__init__.py +9 -0
- api/v1/clash.py +34 -0
- api/v1/redis.py +26 -0
- cli/__init__.py +0 -0
- cli/bitget.py +55 -0
- cli/example.py +32 -0
- cli/fast.py +169 -0
- cli/gate.py +16 -0
- cli/util.py +112 -0
- core/__init__.py +0 -0
- core/clash.py +267 -0
- core/config.py +34 -0
- core/redis_client.py +137 -0
- crypto/__init__.py +0 -0
- crypto/bitget.py +124 -0
- crypto/gate.py +35 -0
- data/__init__.py +0 -0
- data/dou_dict.py +172 -0
- data/dou_list.py +93 -0
- data/main.py +5 -0
- data/template.yaml +150 -0
- models/__init__.py +0 -0
- models/douzero_model.py +45 -0
- models/ocr_model.py +57 -0
- models/response.py +37 -0
- pymecli-0.2.5.dist-info/METADATA +45 -0
- pymecli-0.2.5.dist-info/RECORD +39 -0
- pymecli-0.2.5.dist-info/WHEEL +4 -0
- pymecli-0.2.5.dist-info/entry_points.txt +6 -0
- utils/__init__.py +0 -0
- utils/elapsed.py +18 -0
- utils/helper.py +9 -0
- utils/logger.py +26 -0
- utils/mysql.py +79 -0
- utils/pd.py +20 -0
- utils/sleep.py +16 -0
- utils/text.py +33 -0
- utils/toml.py +6 -0
data/dou_dict.py
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Dict
|
|
3
|
+
|
|
4
|
+
text_map: Dict[str, str] = dict(
|
|
5
|
+
D="JOKER", # 大王
|
|
6
|
+
X="JOKER",
|
|
7
|
+
dizhu="地主",
|
|
8
|
+
jiaodizhu_btn="叫地主",
|
|
9
|
+
qiangdizhu_btn="抢地主",
|
|
10
|
+
jiabei_btn="加倍",
|
|
11
|
+
chaojijiabei_btn="超级加倍",
|
|
12
|
+
bujiabei_btn="不加倍",
|
|
13
|
+
liantian_btn="聊天",
|
|
14
|
+
start_btn="开始游戏",
|
|
15
|
+
chat_btn="聊天",
|
|
16
|
+
chupai_btn="出牌",
|
|
17
|
+
buchu_btn="不出",
|
|
18
|
+
yaobuqi_btn="要不起",
|
|
19
|
+
mingpai_btn="明牌",
|
|
20
|
+
mingpaistart_btn="明牌开始",
|
|
21
|
+
jixuyouxi_btn="继续游戏",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
region_map: Dict[str, tuple] = dict(
|
|
25
|
+
general_btn=(210, 450, 1000, 120), # 叫地主、抢地主、加倍按钮截图区域
|
|
26
|
+
# pass_btn=(200, 450, 1000, 120), # 要不起截图区域
|
|
27
|
+
# # 出牌区
|
|
28
|
+
my_hand_cards=(210, 560, 1000, 180), # 我的手牌截图区域
|
|
29
|
+
left_played_cards=(320, 320, 400, 120), # 左边出牌截图区域
|
|
30
|
+
right_played_cards=(720, 320, 400, 120), # 右边出牌截图区域
|
|
31
|
+
my_played_cards=(320, 422, 800, 120), # 我的出牌截图区域
|
|
32
|
+
# # 过牌区
|
|
33
|
+
three_cards=(600, 33, 220, 103), # 地主底牌截图区域
|
|
34
|
+
# left_pass=(360, 360, 120, 80), # 左边不出截图区域
|
|
35
|
+
# right_pass=(940, 360, 120, 80), # 右边不出截图区域
|
|
36
|
+
# my_pass=(636, 469, 152, 87), # 我的不出截图区域
|
|
37
|
+
# 豆子区域
|
|
38
|
+
# bean=(308, 204, 254, 60),
|
|
39
|
+
# bean1=(295, 474, 264, 60),
|
|
40
|
+
# bean2=(882, 203, 230, 60),
|
|
41
|
+
chat_btn=(1302, 744, 117, 56),
|
|
42
|
+
# landlord_cards=(602, 88, 218, 104),
|
|
43
|
+
end_btn=(800, 610, 510, 80),
|
|
44
|
+
landlord_flag_left=(114, 236, 70, 70),
|
|
45
|
+
landlord_flag_right=(1226, 236, 70, 70),
|
|
46
|
+
cards_num_left=(273, 388, 33, 42),
|
|
47
|
+
cards_num_right=(1117, 387, 33, 44),
|
|
48
|
+
# my_pass=(636, 469, 152, 87),
|
|
49
|
+
right_animation=(720, 210, 360, 100), # 下家动画位置
|
|
50
|
+
left_animation=(360, 210, 360, 100), # 上家动画位置
|
|
51
|
+
my_animation=(540, 340, 350, 120), # 自己上方动画位置
|
|
52
|
+
# (1440, 810)
|
|
53
|
+
weile_test=(130, 600, 2000, 380),
|
|
54
|
+
weile_test_three=(990, 16, 300, 80),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
draw_rect_map = dict(
|
|
58
|
+
# c1=(270, 600, 1900, 380),
|
|
59
|
+
weile_test=region_map["weile_test"],
|
|
60
|
+
weile_test_three=region_map["weile_test_three"],
|
|
61
|
+
# left_played_cards=rect_map["left_played_cards"],
|
|
62
|
+
# right_played_cards=rect_map["right_played_cards"],
|
|
63
|
+
# my_played_cards=rect_map["my_played_cards"],
|
|
64
|
+
# three_cards=rect_map["three_cards"],
|
|
65
|
+
# cards_num_left=rect_map["cards_num_left"],
|
|
66
|
+
# cards_num_right=rect_map["cards_num_right"],
|
|
67
|
+
# general_btn=rect_map["general_btn"],
|
|
68
|
+
# rect_map["right_animation"], # 下家动画位置
|
|
69
|
+
# rect_map["left_animation"], # 上家动画位置
|
|
70
|
+
# rect_map["my_animation"], # 自己上方动画位置
|
|
71
|
+
# right_animation=rect_map["right_animation"],
|
|
72
|
+
# left_animation=rect_map["left_animation"],
|
|
73
|
+
# my_animation=rect_map["my_animation"],
|
|
74
|
+
# all=(5, 5, 1430, 800),
|
|
75
|
+
# my_pass=rect_map["my_pass"],
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
threshold_map: Dict[str, float | tuple[float, float]] = dict(
|
|
79
|
+
jiaodizhu=-0.68, # 叫地主阈值
|
|
80
|
+
fanqiangdizhu=0.3, # 反抢地主阈值
|
|
81
|
+
qiangdizhu=0.12, # 抢地主阈值
|
|
82
|
+
landlord_jiabei=(0.4, 0.01),
|
|
83
|
+
# landlord_jiabei=(0.37, -0.4),
|
|
84
|
+
qiang_landlord_jiabei=(0.45, 0.12),
|
|
85
|
+
farmer_jaibei=(2, 1.22),
|
|
86
|
+
farmer_jaibei_low=(1.22, 0.45),
|
|
87
|
+
mingpai=1.14,
|
|
88
|
+
# jiabei1=0.37,
|
|
89
|
+
# jiabei2=-0.4,
|
|
90
|
+
# jiabei3=0.45,
|
|
91
|
+
# jiabei4=0.12,
|
|
92
|
+
# jiabei5=2,
|
|
93
|
+
# jiabei6=1.22,
|
|
94
|
+
# jiabei7=1.22,
|
|
95
|
+
# jiabei8=0.45,
|
|
96
|
+
)
|
|
97
|
+
to_card_map = {
|
|
98
|
+
30: "D",
|
|
99
|
+
20: "X",
|
|
100
|
+
17: "2",
|
|
101
|
+
14: "A",
|
|
102
|
+
13: "K",
|
|
103
|
+
12: "Q",
|
|
104
|
+
11: "J",
|
|
105
|
+
10: "T",
|
|
106
|
+
9: "9",
|
|
107
|
+
8: "8",
|
|
108
|
+
7: "7",
|
|
109
|
+
6: "6",
|
|
110
|
+
5: "5",
|
|
111
|
+
4: "4",
|
|
112
|
+
3: "3",
|
|
113
|
+
}
|
|
114
|
+
to_env_card_map = {
|
|
115
|
+
"D": 30,
|
|
116
|
+
"X": 20,
|
|
117
|
+
"2": 17,
|
|
118
|
+
"A": 14,
|
|
119
|
+
"K": 13,
|
|
120
|
+
"Q": 12,
|
|
121
|
+
"J": 11,
|
|
122
|
+
"T": 10,
|
|
123
|
+
"9": 9,
|
|
124
|
+
"8": 8,
|
|
125
|
+
"7": 7,
|
|
126
|
+
"6": 6,
|
|
127
|
+
"5": 5,
|
|
128
|
+
"4": 4,
|
|
129
|
+
"3": 3,
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
module_dir = Path(__file__).resolve().parent.parent
|
|
133
|
+
|
|
134
|
+
model_path_map = {
|
|
135
|
+
"landlord": str(module_dir / "baselines/resnet/resnet_landlord.ckpt"),
|
|
136
|
+
"landlord_up": str(module_dir / "baselines/resnet/resnet_landlord_up.ckpt"),
|
|
137
|
+
"landlord_down": str(module_dir / "baselines/resnet/resnet_landlord_down.ckpt"),
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
weights_path_map = {
|
|
141
|
+
"bid_weights": str(module_dir / "douzero/weights/bid_weights.pkl"),
|
|
142
|
+
"farmer_weights": str(module_dir / "douzero/weights/farmer_weights.pkl"),
|
|
143
|
+
"landlord_down_weights": str(
|
|
144
|
+
module_dir / "douzero/weights/landlord_down_weights.pkl"
|
|
145
|
+
),
|
|
146
|
+
"landlord_up_weights": str(module_dir / "douzero/weights/landlord_up_weights.pkl"),
|
|
147
|
+
"landlord_weights": str(module_dir / "douzero/weights/landlord_weights.pkl"),
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
position_name_map = {
|
|
151
|
+
"landlord": "地主",
|
|
152
|
+
"landlord_up": "地主上家",
|
|
153
|
+
"landlord_down": "地主下家",
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
TYPE_PRIORITY = {
|
|
157
|
+
0: 0, # PASS
|
|
158
|
+
1: 1, # SINGLE
|
|
159
|
+
2: 1, # PAIR
|
|
160
|
+
3: 1, # TRIPLE
|
|
161
|
+
4: 2, # BOMB
|
|
162
|
+
5: 3, # KING_BOMB
|
|
163
|
+
6: 1, # 3+1
|
|
164
|
+
7: 1, # 3+2
|
|
165
|
+
8: 1, # SERIAL_SINGLE
|
|
166
|
+
9: 1, # SERIAL_PAIR
|
|
167
|
+
10: 1, # SERIAL_TRIPLE
|
|
168
|
+
11: 1, # SERIAL_3+1
|
|
169
|
+
12: 1, # SERIAL_3+2
|
|
170
|
+
13: 1, # 4+2
|
|
171
|
+
14: 1, # 4+2+2
|
|
172
|
+
}
|
data/dou_list.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# card识别列表
|
|
2
|
+
card_rec_list: list[str] = [
|
|
3
|
+
"JOKER",
|
|
4
|
+
"JOK",
|
|
5
|
+
"JO", # 表示JOKER
|
|
6
|
+
"2",
|
|
7
|
+
"A",
|
|
8
|
+
"K",
|
|
9
|
+
"Q",
|
|
10
|
+
"J",
|
|
11
|
+
"10",
|
|
12
|
+
"9",
|
|
13
|
+
"8",
|
|
14
|
+
"7",
|
|
15
|
+
"6",
|
|
16
|
+
"5",
|
|
17
|
+
"4",
|
|
18
|
+
"3",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
bombs = [
|
|
22
|
+
[3, 3, 3, 3],
|
|
23
|
+
[4, 4, 4, 4],
|
|
24
|
+
[5, 5, 5, 5],
|
|
25
|
+
[6, 6, 6, 6],
|
|
26
|
+
[7, 7, 7, 7],
|
|
27
|
+
[8, 8, 8, 8],
|
|
28
|
+
[9, 9, 9, 9],
|
|
29
|
+
[10, 10, 10, 10],
|
|
30
|
+
[11, 11, 11, 11],
|
|
31
|
+
[12, 12, 12, 12],
|
|
32
|
+
[13, 13, 13, 13],
|
|
33
|
+
[14, 14, 14, 14],
|
|
34
|
+
[17, 17, 17, 17],
|
|
35
|
+
[20, 30],
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
all_env_card = [
|
|
39
|
+
3,
|
|
40
|
+
3,
|
|
41
|
+
3,
|
|
42
|
+
3,
|
|
43
|
+
4,
|
|
44
|
+
4,
|
|
45
|
+
4,
|
|
46
|
+
4,
|
|
47
|
+
5,
|
|
48
|
+
5,
|
|
49
|
+
5,
|
|
50
|
+
5,
|
|
51
|
+
6,
|
|
52
|
+
6,
|
|
53
|
+
6,
|
|
54
|
+
6,
|
|
55
|
+
7,
|
|
56
|
+
7,
|
|
57
|
+
7,
|
|
58
|
+
7,
|
|
59
|
+
8,
|
|
60
|
+
8,
|
|
61
|
+
8,
|
|
62
|
+
8,
|
|
63
|
+
9,
|
|
64
|
+
9,
|
|
65
|
+
9,
|
|
66
|
+
9,
|
|
67
|
+
10,
|
|
68
|
+
10,
|
|
69
|
+
10,
|
|
70
|
+
10,
|
|
71
|
+
11,
|
|
72
|
+
11,
|
|
73
|
+
11,
|
|
74
|
+
11,
|
|
75
|
+
12,
|
|
76
|
+
12,
|
|
77
|
+
12,
|
|
78
|
+
12,
|
|
79
|
+
13,
|
|
80
|
+
13,
|
|
81
|
+
13,
|
|
82
|
+
13,
|
|
83
|
+
14,
|
|
84
|
+
14,
|
|
85
|
+
14,
|
|
86
|
+
14,
|
|
87
|
+
17,
|
|
88
|
+
17,
|
|
89
|
+
17,
|
|
90
|
+
17,
|
|
91
|
+
20,
|
|
92
|
+
30,
|
|
93
|
+
]
|
data/main.py
ADDED
data/template.yaml
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
mixed-port: 7890
|
|
2
|
+
allow-lan: false
|
|
3
|
+
mode: rule
|
|
4
|
+
log-level: warning
|
|
5
|
+
external-controller: "127.0.0.1:9097"
|
|
6
|
+
global-client-fingerprint: chrome
|
|
7
|
+
unified-delay: true
|
|
8
|
+
ipv6: false
|
|
9
|
+
profile:
|
|
10
|
+
store-selected: true
|
|
11
|
+
tun:
|
|
12
|
+
mtu: 1500
|
|
13
|
+
dns:
|
|
14
|
+
enable: true
|
|
15
|
+
ipv6: false
|
|
16
|
+
use-system-hosts: false
|
|
17
|
+
listen: "127.0.0.1:5335"
|
|
18
|
+
enhanced-mode: fake-ip
|
|
19
|
+
fake-ip-range: 198.18.0.1/16
|
|
20
|
+
fake-ip-filter:
|
|
21
|
+
[
|
|
22
|
+
"*.lan",
|
|
23
|
+
"stun.*.*.*",
|
|
24
|
+
"stun.*.*",
|
|
25
|
+
time.windows.com,
|
|
26
|
+
time.nist.gov,
|
|
27
|
+
time.apple.com,
|
|
28
|
+
time.asia.apple.com,
|
|
29
|
+
"*.ntp.org.cn",
|
|
30
|
+
"*.openwrt.pool.ntp.org",
|
|
31
|
+
time1.cloud.tencent.com,
|
|
32
|
+
time.ustc.edu.cn,
|
|
33
|
+
pool.ntp.org,
|
|
34
|
+
ntp.ubuntu.com,
|
|
35
|
+
ntp.aliyun.com,
|
|
36
|
+
ntp1.aliyun.com,
|
|
37
|
+
ntp2.aliyun.com,
|
|
38
|
+
ntp3.aliyun.com,
|
|
39
|
+
ntp4.aliyun.com,
|
|
40
|
+
ntp5.aliyun.com,
|
|
41
|
+
ntp6.aliyun.com,
|
|
42
|
+
ntp7.aliyun.com,
|
|
43
|
+
time1.aliyun.com,
|
|
44
|
+
time2.aliyun.com,
|
|
45
|
+
time3.aliyun.com,
|
|
46
|
+
time4.aliyun.com,
|
|
47
|
+
time5.aliyun.com,
|
|
48
|
+
time6.aliyun.com,
|
|
49
|
+
time7.aliyun.com,
|
|
50
|
+
"*.time.edu.cn",
|
|
51
|
+
time1.apple.com,
|
|
52
|
+
time2.apple.com,
|
|
53
|
+
time3.apple.com,
|
|
54
|
+
time4.apple.com,
|
|
55
|
+
time5.apple.com,
|
|
56
|
+
time6.apple.com,
|
|
57
|
+
time7.apple.com,
|
|
58
|
+
time1.google.com,
|
|
59
|
+
time2.google.com,
|
|
60
|
+
time3.google.com,
|
|
61
|
+
time4.google.com,
|
|
62
|
+
music.163.com,
|
|
63
|
+
"*.music.163.com",
|
|
64
|
+
"*.126.net",
|
|
65
|
+
musicapi.taihe.com,
|
|
66
|
+
music.taihe.com,
|
|
67
|
+
songsearch.kugou.com,
|
|
68
|
+
trackercdn.kugou.com,
|
|
69
|
+
"*.kuwo.cn",
|
|
70
|
+
api-jooxtt.sanook.com,
|
|
71
|
+
api.joox.com,
|
|
72
|
+
joox.com,
|
|
73
|
+
y.qq.com,
|
|
74
|
+
"*.y.qq.com",
|
|
75
|
+
streamoc.music.tc.qq.com,
|
|
76
|
+
mobileoc.music.tc.qq.com,
|
|
77
|
+
isure.stream.qqmusic.qq.com,
|
|
78
|
+
dl.stream.qqmusic.qq.com,
|
|
79
|
+
aqqmusic.tc.qq.com,
|
|
80
|
+
amobile.music.tc.qq.com,
|
|
81
|
+
"*.xiami.com",
|
|
82
|
+
"*.music.migu.cn",
|
|
83
|
+
music.migu.cn,
|
|
84
|
+
"*.msftconnecttest.com",
|
|
85
|
+
"*.msftncsi.com",
|
|
86
|
+
localhost.ptlogin2.qq.com,
|
|
87
|
+
"*.*.*.srv.nintendo.net",
|
|
88
|
+
"*.*.stun.playstation.net",
|
|
89
|
+
"xbox.*.*.microsoft.com",
|
|
90
|
+
"*.ipv6.microsoft.com",
|
|
91
|
+
"*.*.xboxlive.com",
|
|
92
|
+
speedtest.cros.wr.pvp.net,
|
|
93
|
+
]
|
|
94
|
+
default-nameserver:
|
|
95
|
+
[
|
|
96
|
+
system,
|
|
97
|
+
180.76.76.76,
|
|
98
|
+
182.254.118.118,
|
|
99
|
+
8.8.8.8,
|
|
100
|
+
180.184.2.2,
|
|
101
|
+
"2400:3200::1",
|
|
102
|
+
]
|
|
103
|
+
nameserver:
|
|
104
|
+
[
|
|
105
|
+
"https://223.5.5.5/dns-query#skip-cert-verify=true",
|
|
106
|
+
"https://doh.pub/dns-query#skip-cert-verify=true",
|
|
107
|
+
"https://dns.alidns.com/dns-query#skip-cert-verify=true",
|
|
108
|
+
"tls://223.5.5.5#skip-cert-verify=true",
|
|
109
|
+
"tls://dot.pub#skip-cert-verify=true",
|
|
110
|
+
"tls://dns.alidns.com#skip-cert-verify=true",
|
|
111
|
+
"https://223.6.6.6/dns-query#skip-cert-verify=true&h3=true",
|
|
112
|
+
"https://cloudflare-dns.com/dns-query#skip-cert-verify=true",
|
|
113
|
+
]
|
|
114
|
+
proxy-server-nameserver:
|
|
115
|
+
[
|
|
116
|
+
"https://223.5.5.5/dns-query#skip-cert-verify=true",
|
|
117
|
+
"https://doh.pub/dns-query#skip-cert-verify=true",
|
|
118
|
+
"https://dns.alidns.com/dns-query#skip-cert-verify=true",
|
|
119
|
+
"tls://223.5.5.5#skip-cert-verify=true",
|
|
120
|
+
"tls://dot.pub#skip-cert-verify=true",
|
|
121
|
+
"tls://dns.alidns.com#skip-cert-verify=true",
|
|
122
|
+
"https://223.6.6.6/dns-query#skip-cert-verify=true&h3=true",
|
|
123
|
+
"https://cloudflare-dns.com/dns-query#skip-cert-verify=true",
|
|
124
|
+
]
|
|
125
|
+
fallback-filter:
|
|
126
|
+
{
|
|
127
|
+
geoip: true,
|
|
128
|
+
ipcidr: [240.0.0.0/4, 0.0.0.0/32, 127.0.0.1/32],
|
|
129
|
+
domain:
|
|
130
|
+
[
|
|
131
|
+
+.google.com,
|
|
132
|
+
+.facebook.com,
|
|
133
|
+
+.twitter.com,
|
|
134
|
+
+.youtube.com,
|
|
135
|
+
+.xn--ngstr-lra8j.com,
|
|
136
|
+
+.google.cn,
|
|
137
|
+
+.googleapis.cn,
|
|
138
|
+
+.googleapis.com,
|
|
139
|
+
+.gvt1.com,
|
|
140
|
+
],
|
|
141
|
+
}
|
|
142
|
+
external-controller-cors:
|
|
143
|
+
allow-private-network: true
|
|
144
|
+
allow-origins:
|
|
145
|
+
- "*"
|
|
146
|
+
proxies: []
|
|
147
|
+
proxy-providers: {}
|
|
148
|
+
proxy-groups: []
|
|
149
|
+
rule-providers: {}
|
|
150
|
+
rules: []
|
models/__init__.py
ADDED
|
File without changes
|
models/douzero_model.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field, field_validator
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BidScoreRequest(BaseModel):
|
|
7
|
+
cards: str = Field(..., min_length=17, max_length=17, description="cards str")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PreGameScoreRequest(BaseModel):
|
|
11
|
+
cards: str = Field(..., description="cards str")
|
|
12
|
+
three: str = Field(..., min_length=3, max_length=3, description="三张底牌")
|
|
13
|
+
position_code: Literal["0", "1", "2"] = Field(
|
|
14
|
+
...,
|
|
15
|
+
description="0:我是地主上家,1:我是地主,2:我是地主下家",
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
@field_validator("cards")
|
|
19
|
+
def validate_cards_length(cls, v):
|
|
20
|
+
if len(v) not in [17, 20]:
|
|
21
|
+
raise ValueError("cards length must be either 17 or 20")
|
|
22
|
+
return v
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class PlayRequest(BaseModel):
|
|
26
|
+
cards: str = Field(..., description="开局前的手牌,17或20张")
|
|
27
|
+
other_cards: str | None = Field("", description="开局前其他玩家手牌,34张或37张")
|
|
28
|
+
played_list: list[str] = Field([], description="played list")
|
|
29
|
+
three: str = Field(..., min_length=3, max_length=3, description="三张底牌")
|
|
30
|
+
position_code: Literal[0, 1, 2] = Field(
|
|
31
|
+
...,
|
|
32
|
+
description="0:地主在右边;1:我是地主;2:地主在左边",
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
@field_validator("cards")
|
|
36
|
+
def validate_cards_length(cls, v):
|
|
37
|
+
if len(v) not in [17, 20]:
|
|
38
|
+
raise ValueError("cards length must be either 17 or 20")
|
|
39
|
+
return v
|
|
40
|
+
|
|
41
|
+
@field_validator("other_cards")
|
|
42
|
+
def validate_other_cards_length(cls, v):
|
|
43
|
+
if len(v) not in [37, 34, 0] and v is not None:
|
|
44
|
+
raise ValueError("cards length must be either 37 or 34")
|
|
45
|
+
return v
|
models/ocr_model.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from typing import List, Literal, Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FindCardsRequest(BaseModel):
|
|
7
|
+
img: str = Field(..., min_length=100, description="img base64 string")
|
|
8
|
+
region: Optional[List[int]] = Field(
|
|
9
|
+
None, min_length=4, max_length=4, description="识别区域[x,y,w,h]"
|
|
10
|
+
)
|
|
11
|
+
is_align_y: bool = Field(False, description="是否对齐Y轴")
|
|
12
|
+
is_preprocess: bool = Field(
|
|
13
|
+
True, description="是否预处理图片,如何识别效果不好,可以预处理"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class FindOneRequest(BaseModel):
|
|
18
|
+
img: str = Field(..., min_length=100, description="img base64 string")
|
|
19
|
+
target: str | None = Field(None, description="需识别的目标文本")
|
|
20
|
+
region: Optional[List[int]] = Field(
|
|
21
|
+
None, min_length=4, max_length=4, description="识别区域[x,y,w,h]"
|
|
22
|
+
)
|
|
23
|
+
is_preprocess: bool = Field(
|
|
24
|
+
True, description="是否预处理图片,如何识别效果不好,可以预处理"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class FindNRequest(BaseModel):
|
|
29
|
+
img: str = Field(..., min_length=100, description="img base64 string")
|
|
30
|
+
target_list: List[str] = Field(..., description="需识别的N个目标列表")
|
|
31
|
+
region: Optional[List[int]] = Field(
|
|
32
|
+
None, min_length=4, max_length=4, description="识别区域[x,y,w,h]"
|
|
33
|
+
)
|
|
34
|
+
is_preprocess: bool = Field(
|
|
35
|
+
True, description="是否预处理图片,如何识别效果不好,可以预处理"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Region(BaseModel):
|
|
40
|
+
region: List[int] = Field(
|
|
41
|
+
..., min_length=4, max_length=4, description="识别区域[x,y,w,h]"
|
|
42
|
+
)
|
|
43
|
+
type: Literal["list", "text", "cards"] = Field(..., description="识别类型")
|
|
44
|
+
is_align_y: bool = Field(False, description="是否对齐Y轴")
|
|
45
|
+
target_list: List[str] = Field([], description="需识别的目标文本列表")
|
|
46
|
+
target: str = Field("", description="需识别的目标文本")
|
|
47
|
+
is_preprocess: bool = Field(
|
|
48
|
+
False, description="是否预处理图片,如何识别效果不好,可以预处理"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class FindRegionsRequest(BaseModel):
|
|
53
|
+
img: str = Field(..., min_length=100, description="img base64 string")
|
|
54
|
+
regions: List[Region] = Field(..., description="识别区域配置")
|
|
55
|
+
is_preprocess: bool = Field(
|
|
56
|
+
False, description="是否预处理图片,如何识别效果不好,可以预处理"
|
|
57
|
+
)
|
models/response.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from typing import Any, Generic, Optional, TypeVar
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
T = TypeVar("T")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class StandardResponse(BaseModel, Generic[T]):
|
|
9
|
+
"""标准API响应模型"""
|
|
10
|
+
|
|
11
|
+
success: bool = True
|
|
12
|
+
data: Optional[T] = None
|
|
13
|
+
error: Optional[str] = None
|
|
14
|
+
|
|
15
|
+
class Config:
|
|
16
|
+
json_schema_extra = {
|
|
17
|
+
"example": {
|
|
18
|
+
"success": True,
|
|
19
|
+
"data": {},
|
|
20
|
+
"error": None,
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# 定义常用的具体响应类型
|
|
26
|
+
class SuccessResponse(StandardResponse[T]):
|
|
27
|
+
"""成功响应"""
|
|
28
|
+
|
|
29
|
+
success: bool = True
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ErrorResponse(StandardResponse):
|
|
33
|
+
"""错误响应"""
|
|
34
|
+
|
|
35
|
+
success: bool = False
|
|
36
|
+
error: Optional[str] = None
|
|
37
|
+
data: Optional[Any] = None
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pymecli
|
|
3
|
+
Version: 0.2.5
|
|
4
|
+
Summary: My CLI
|
|
5
|
+
Project-URL: Homepage, https://pypi.org/project/pymecli
|
|
6
|
+
Project-URL: Repository, https://github.com/meme2046/pymecli
|
|
7
|
+
License: MIT
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Requires-Python: <=3.12,>=3.10
|
|
12
|
+
Requires-Dist: arrow>=1.4.0
|
|
13
|
+
Requires-Dist: babel>=2.17.0
|
|
14
|
+
Requires-Dist: cowsay>=6.1
|
|
15
|
+
Requires-Dist: dotenv>=0.9.9
|
|
16
|
+
Requires-Dist: fastapi>=0.127.0
|
|
17
|
+
Requires-Dist: numpy<2.0.0
|
|
18
|
+
Requires-Dist: pandas>=2.3.3
|
|
19
|
+
Requires-Dist: pydantic-settings>=2.12.0
|
|
20
|
+
Requires-Dist: pymysql>=1.1.2
|
|
21
|
+
Requires-Dist: pytz>=2025.2
|
|
22
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
23
|
+
Requires-Dist: redis[hiredis]>=7.1.0
|
|
24
|
+
Requires-Dist: requests[socks]>=2.32.5
|
|
25
|
+
Requires-Dist: sqlalchemy>=2.0.45
|
|
26
|
+
Requires-Dist: toml>=0.10.2
|
|
27
|
+
Requires-Dist: typer>=0.20.1
|
|
28
|
+
Requires-Dist: uvicorn>=0.40.0
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# typer example
|
|
32
|
+
|
|
33
|
+
```shell
|
|
34
|
+
uv run example hello
|
|
35
|
+
uv run example hello Xiaoming
|
|
36
|
+
uv run example goodbye
|
|
37
|
+
uv run example goodbye Xiaoming
|
|
38
|
+
uv run example goodbye Xiaoming -f
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
# fastapi server
|
|
42
|
+
|
|
43
|
+
```shell
|
|
44
|
+
uv run fast --port 8877
|
|
45
|
+
```
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
api/v1/__init__.py,sha256=qaYn2kAw041YfY2rQEu6l3n_WlKaxi2ON-dJy5wxaW8,277
|
|
3
|
+
api/v1/clash.py,sha256=S-QpEhCq-9WI_rvS5Hk74noCBVnzzgAIzKyTihrF7k0,1238
|
|
4
|
+
api/v1/redis.py,sha256=Ble6mrt23nx-4hdccMta-8ATVolrWyTvNPui-x_6VAQ,625
|
|
5
|
+
cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
cli/bitget.py,sha256=J5dwhYY3AivYQ24SufbMHoMUZMRpU2R6VegQhlW6-HY,1447
|
|
7
|
+
cli/example.py,sha256=eV-vuFfjy0K-AUiJNzcjqt-tcDsW1NIypCzB0Gpwphw,617
|
|
8
|
+
cli/fast.py,sha256=rSlouCDbzY1Kwn7MRbxIvYuPMxqBFUYaMZYrRIT1urg,4545
|
|
9
|
+
cli/gate.py,sha256=m9eDevnQVWoMXbZDZruDm1H4frHblW69FBx-tQgLlNM,410
|
|
10
|
+
cli/util.py,sha256=nGEQDbDnNc74Ce0te7uaI3dazahSevWFQRfkywXHvFw,2600
|
|
11
|
+
core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
+
core/clash.py,sha256=qoolygkEK4jqhTZdgq8K7S4G8BvQ7bTKh2-mPHdO32A,9648
|
|
13
|
+
core/config.py,sha256=HQidRndTPnVXMt1Q54Mydb8i1LTsemKxZ_ZOuWtXe18,957
|
|
14
|
+
core/redis_client.py,sha256=FWVkbzjrNy98yPSUOhyjtxQF9mS31OM4FOW4q9Qgfl0,3886
|
|
15
|
+
crypto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
crypto/bitget.py,sha256=96M3n7NOP0H7L_wbUaVczis1bGEA-LKgY6upsblF8Co,4293
|
|
17
|
+
crypto/gate.py,sha256=PnoFjag5LQ1i9oKaZsi2IKcI9bNE5W1BB5XI40E4brc,1537
|
|
18
|
+
data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
|
+
data/dou_dict.py,sha256=CPqAfjx0YgBnRvy2aiJZl9Hcqe5gbRDVz3oarVId3OA,5287
|
|
20
|
+
data/dou_list.py,sha256=DKAs0YyFQ7izNacaWmaX9Tjh-IP3h6IE7gPP9maQvNw,1019
|
|
21
|
+
data/main.py,sha256=r3JVMaI_wi_NnnlyM8nlfIDtc_Cjpnz3HUlFIKlAxtw,136
|
|
22
|
+
data/template.yaml,sha256=t7l0sIZyBOb0zMNsRaKRCZc0ADnFQ9I2kXQgCQjSeJA,3953
|
|
23
|
+
models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
+
models/douzero_model.py,sha256=XmM1QIP6rnfoSBcQsZvdCUjhWB3uEjlEA_GqagOHgGU,1670
|
|
25
|
+
models/ocr_model.py,sha256=UZP1S2hvl0K3eEoRMh6gRHo8xVBp9PTfRtE0_zd09Mw,2358
|
|
26
|
+
models/response.py,sha256=itUfkxm9W6XC6YuGAzZdkuKr7jxTibuph5OBeebsaQQ,781
|
|
27
|
+
utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
|
+
utils/elapsed.py,sha256=pCPQcf1U9gx4gLPqLf32HDLmQqVcy5yqGzplD5spf2A,410
|
|
29
|
+
utils/helper.py,sha256=787H45n_zZdjKe-HnSRSuqDAKoXRSyfob6kM0_TszDw,240
|
|
30
|
+
utils/logger.py,sha256=yscf2nU8XO1LqgvU31m1ipcmfZj5b6BaQtEqaLCm03Y,1084
|
|
31
|
+
utils/mysql.py,sha256=P2-gkdOuaa2aZ0ElRPqexfCEHozmjbe0oGkTtV9aIy4,2315
|
|
32
|
+
utils/pd.py,sha256=IRI9t7N_Vdbeylx_9iDxLuAwe-26AJ57-JJ6CWszJvg,582
|
|
33
|
+
utils/sleep.py,sha256=l-e316xdAS5ha83C4bm8anieeut7qaA_a7C2yoXzB00,438
|
|
34
|
+
utils/text.py,sha256=9_pMIrRzIfEOoj7TZpt1eZfTWI_1g8D5a6joaWEBUXw,877
|
|
35
|
+
utils/toml.py,sha256=8bN_At6Ajlq6frqk67LqXctU-Hw0YmpRTh0jlT4SbA4,119
|
|
36
|
+
pymecli-0.2.5.dist-info/METADATA,sha256=Vz3fBENCs8zFk-Eydz2_7XZFXwS__TxqNlMXcO1lI8A,1166
|
|
37
|
+
pymecli-0.2.5.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
38
|
+
pymecli-0.2.5.dist-info/entry_points.txt,sha256=sIgdM6PVsx6ZBaTecV3oA0NO8_kiEHzih9SRY8fK64A,134
|
|
39
|
+
pymecli-0.2.5.dist-info/RECORD,,
|
utils/__init__.py
ADDED
|
File without changes
|
utils/elapsed.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from functools import wraps
|
|
3
|
+
|
|
4
|
+
from utils.logger import get_logger
|
|
5
|
+
|
|
6
|
+
logger = get_logger(__name__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def timeit(func):
|
|
10
|
+
@wraps(func)
|
|
11
|
+
def wrapper(*args, **kwargs):
|
|
12
|
+
t0 = time.perf_counter()
|
|
13
|
+
res = func(*args, **kwargs)
|
|
14
|
+
t1 = time.perf_counter()
|
|
15
|
+
logger.info(f"{func.__name__} 执行时间: {t1 - t0:.3f} 秒")
|
|
16
|
+
return res
|
|
17
|
+
|
|
18
|
+
return wrapper
|