pyxllib 0.3.96__py3-none-any.whl → 0.3.197__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.
Files changed (306) hide show
  1. pyxllib/algo/geo.py +12 -0
  2. pyxllib/algo/intervals.py +1 -1
  3. pyxllib/algo/matcher.py +78 -0
  4. pyxllib/algo/pupil.py +187 -19
  5. pyxllib/algo/specialist.py +2 -1
  6. pyxllib/algo/stat.py +38 -2
  7. {pyxlpr → pyxllib/autogui}/__init__.py +1 -1
  8. pyxllib/autogui/activewin.py +246 -0
  9. pyxllib/autogui/all.py +9 -0
  10. pyxllib/{ext/autogui → autogui}/autogui.py +40 -11
  11. pyxllib/autogui/uiautolib.py +362 -0
  12. pyxllib/autogui/wechat.py +827 -0
  13. pyxllib/autogui/wechat_msg.py +421 -0
  14. pyxllib/autogui/wxautolib.py +84 -0
  15. pyxllib/cv/slidercaptcha.py +137 -0
  16. pyxllib/data/echarts.py +123 -12
  17. pyxllib/data/jsonlib.py +89 -0
  18. pyxllib/data/pglib.py +514 -30
  19. pyxllib/data/sqlite.py +231 -4
  20. pyxllib/ext/JLineViewer.py +14 -1
  21. pyxllib/ext/drissionlib.py +277 -0
  22. pyxllib/ext/kq5034lib.py +0 -1594
  23. pyxllib/ext/robustprocfile.py +497 -0
  24. pyxllib/ext/unixlib.py +6 -5
  25. pyxllib/ext/utools.py +108 -95
  26. pyxllib/ext/webhook.py +32 -14
  27. pyxllib/ext/wjxlib.py +88 -0
  28. pyxllib/ext/wpsapi.py +124 -0
  29. pyxllib/ext/xlwork.py +9 -0
  30. pyxllib/ext/yuquelib.py +1003 -71
  31. pyxllib/file/docxlib.py +1 -1
  32. pyxllib/file/libreoffice.py +165 -0
  33. pyxllib/file/movielib.py +9 -0
  34. pyxllib/file/packlib/__init__.py +112 -75
  35. pyxllib/file/pdflib.py +1 -1
  36. pyxllib/file/pupil.py +1 -1
  37. pyxllib/file/specialist/dirlib.py +1 -1
  38. pyxllib/file/specialist/download.py +10 -3
  39. pyxllib/file/specialist/filelib.py +266 -55
  40. pyxllib/file/xlsxlib.py +205 -50
  41. pyxllib/file/xlsyncfile.py +341 -0
  42. pyxllib/prog/cachetools.py +64 -0
  43. pyxllib/prog/filelock.py +42 -0
  44. pyxllib/prog/multiprogs.py +940 -0
  45. pyxllib/prog/newbie.py +9 -2
  46. pyxllib/prog/pupil.py +129 -60
  47. pyxllib/prog/specialist/__init__.py +176 -2
  48. pyxllib/prog/specialist/bc.py +5 -2
  49. pyxllib/prog/specialist/browser.py +11 -2
  50. pyxllib/prog/specialist/datetime.py +68 -0
  51. pyxllib/prog/specialist/tictoc.py +12 -13
  52. pyxllib/prog/specialist/xllog.py +5 -5
  53. pyxllib/prog/xlosenv.py +7 -0
  54. pyxllib/text/airscript.js +744 -0
  55. pyxllib/text/charclasslib.py +17 -5
  56. pyxllib/text/jiebalib.py +6 -3
  57. pyxllib/text/jinjalib.py +32 -0
  58. pyxllib/text/jsa_ai_prompt.md +271 -0
  59. pyxllib/text/jscode.py +159 -4
  60. pyxllib/text/nestenv.py +1 -1
  61. pyxllib/text/newbie.py +12 -0
  62. pyxllib/text/pupil/common.py +26 -0
  63. pyxllib/text/specialist/ptag.py +2 -2
  64. pyxllib/text/templates/echart_base.html +11 -0
  65. pyxllib/text/templates/highlight_code.html +17 -0
  66. pyxllib/text/templates/latex_editor.html +103 -0
  67. pyxllib/text/xmllib.py +76 -14
  68. pyxllib/xl.py +2 -1
  69. pyxllib-0.3.197.dist-info/METADATA +48 -0
  70. pyxllib-0.3.197.dist-info/RECORD +126 -0
  71. {pyxllib-0.3.96.dist-info → pyxllib-0.3.197.dist-info}/WHEEL +1 -2
  72. pyxllib/ext/autogui/__init__.py +0 -8
  73. pyxllib-0.3.96.dist-info/METADATA +0 -51
  74. pyxllib-0.3.96.dist-info/RECORD +0 -333
  75. pyxllib-0.3.96.dist-info/top_level.txt +0 -2
  76. pyxlpr/ai/__init__.py +0 -5
  77. pyxlpr/ai/clientlib.py +0 -1281
  78. pyxlpr/ai/specialist.py +0 -286
  79. pyxlpr/ai/torch_app.py +0 -172
  80. pyxlpr/ai/xlpaddle.py +0 -655
  81. pyxlpr/ai/xltorch.py +0 -705
  82. pyxlpr/data/__init__.py +0 -11
  83. pyxlpr/data/coco.py +0 -1325
  84. pyxlpr/data/datacls.py +0 -365
  85. pyxlpr/data/datasets.py +0 -200
  86. pyxlpr/data/gptlib.py +0 -1291
  87. pyxlpr/data/icdar/__init__.py +0 -96
  88. pyxlpr/data/icdar/deteval.py +0 -377
  89. pyxlpr/data/icdar/icdar2013.py +0 -341
  90. pyxlpr/data/icdar/iou.py +0 -340
  91. pyxlpr/data/icdar/rrc_evaluation_funcs_1_1.py +0 -463
  92. pyxlpr/data/imtextline.py +0 -473
  93. pyxlpr/data/labelme.py +0 -866
  94. pyxlpr/data/removeline.py +0 -179
  95. pyxlpr/data/specialist.py +0 -57
  96. pyxlpr/eval/__init__.py +0 -85
  97. pyxlpr/paddleocr.py +0 -776
  98. pyxlpr/ppocr/__init__.py +0 -15
  99. pyxlpr/ppocr/configs/rec/multi_language/generate_multi_language_configs.py +0 -226
  100. pyxlpr/ppocr/data/__init__.py +0 -135
  101. pyxlpr/ppocr/data/imaug/ColorJitter.py +0 -26
  102. pyxlpr/ppocr/data/imaug/__init__.py +0 -67
  103. pyxlpr/ppocr/data/imaug/copy_paste.py +0 -170
  104. pyxlpr/ppocr/data/imaug/east_process.py +0 -437
  105. pyxlpr/ppocr/data/imaug/gen_table_mask.py +0 -244
  106. pyxlpr/ppocr/data/imaug/iaa_augment.py +0 -114
  107. pyxlpr/ppocr/data/imaug/label_ops.py +0 -789
  108. pyxlpr/ppocr/data/imaug/make_border_map.py +0 -184
  109. pyxlpr/ppocr/data/imaug/make_pse_gt.py +0 -106
  110. pyxlpr/ppocr/data/imaug/make_shrink_map.py +0 -126
  111. pyxlpr/ppocr/data/imaug/operators.py +0 -433
  112. pyxlpr/ppocr/data/imaug/pg_process.py +0 -906
  113. pyxlpr/ppocr/data/imaug/randaugment.py +0 -143
  114. pyxlpr/ppocr/data/imaug/random_crop_data.py +0 -239
  115. pyxlpr/ppocr/data/imaug/rec_img_aug.py +0 -533
  116. pyxlpr/ppocr/data/imaug/sast_process.py +0 -777
  117. pyxlpr/ppocr/data/imaug/text_image_aug/__init__.py +0 -17
  118. pyxlpr/ppocr/data/imaug/text_image_aug/augment.py +0 -120
  119. pyxlpr/ppocr/data/imaug/text_image_aug/warp_mls.py +0 -168
  120. pyxlpr/ppocr/data/lmdb_dataset.py +0 -115
  121. pyxlpr/ppocr/data/pgnet_dataset.py +0 -104
  122. pyxlpr/ppocr/data/pubtab_dataset.py +0 -107
  123. pyxlpr/ppocr/data/simple_dataset.py +0 -372
  124. pyxlpr/ppocr/losses/__init__.py +0 -61
  125. pyxlpr/ppocr/losses/ace_loss.py +0 -52
  126. pyxlpr/ppocr/losses/basic_loss.py +0 -135
  127. pyxlpr/ppocr/losses/center_loss.py +0 -88
  128. pyxlpr/ppocr/losses/cls_loss.py +0 -30
  129. pyxlpr/ppocr/losses/combined_loss.py +0 -67
  130. pyxlpr/ppocr/losses/det_basic_loss.py +0 -208
  131. pyxlpr/ppocr/losses/det_db_loss.py +0 -80
  132. pyxlpr/ppocr/losses/det_east_loss.py +0 -63
  133. pyxlpr/ppocr/losses/det_pse_loss.py +0 -149
  134. pyxlpr/ppocr/losses/det_sast_loss.py +0 -121
  135. pyxlpr/ppocr/losses/distillation_loss.py +0 -272
  136. pyxlpr/ppocr/losses/e2e_pg_loss.py +0 -140
  137. pyxlpr/ppocr/losses/kie_sdmgr_loss.py +0 -113
  138. pyxlpr/ppocr/losses/rec_aster_loss.py +0 -99
  139. pyxlpr/ppocr/losses/rec_att_loss.py +0 -39
  140. pyxlpr/ppocr/losses/rec_ctc_loss.py +0 -44
  141. pyxlpr/ppocr/losses/rec_enhanced_ctc_loss.py +0 -70
  142. pyxlpr/ppocr/losses/rec_nrtr_loss.py +0 -30
  143. pyxlpr/ppocr/losses/rec_sar_loss.py +0 -28
  144. pyxlpr/ppocr/losses/rec_srn_loss.py +0 -47
  145. pyxlpr/ppocr/losses/table_att_loss.py +0 -109
  146. pyxlpr/ppocr/metrics/__init__.py +0 -44
  147. pyxlpr/ppocr/metrics/cls_metric.py +0 -45
  148. pyxlpr/ppocr/metrics/det_metric.py +0 -82
  149. pyxlpr/ppocr/metrics/distillation_metric.py +0 -73
  150. pyxlpr/ppocr/metrics/e2e_metric.py +0 -86
  151. pyxlpr/ppocr/metrics/eval_det_iou.py +0 -274
  152. pyxlpr/ppocr/metrics/kie_metric.py +0 -70
  153. pyxlpr/ppocr/metrics/rec_metric.py +0 -75
  154. pyxlpr/ppocr/metrics/table_metric.py +0 -50
  155. pyxlpr/ppocr/modeling/architectures/__init__.py +0 -32
  156. pyxlpr/ppocr/modeling/architectures/base_model.py +0 -88
  157. pyxlpr/ppocr/modeling/architectures/distillation_model.py +0 -60
  158. pyxlpr/ppocr/modeling/backbones/__init__.py +0 -54
  159. pyxlpr/ppocr/modeling/backbones/det_mobilenet_v3.py +0 -268
  160. pyxlpr/ppocr/modeling/backbones/det_resnet_vd.py +0 -246
  161. pyxlpr/ppocr/modeling/backbones/det_resnet_vd_sast.py +0 -285
  162. pyxlpr/ppocr/modeling/backbones/e2e_resnet_vd_pg.py +0 -265
  163. pyxlpr/ppocr/modeling/backbones/kie_unet_sdmgr.py +0 -186
  164. pyxlpr/ppocr/modeling/backbones/rec_mobilenet_v3.py +0 -138
  165. pyxlpr/ppocr/modeling/backbones/rec_mv1_enhance.py +0 -258
  166. pyxlpr/ppocr/modeling/backbones/rec_nrtr_mtb.py +0 -48
  167. pyxlpr/ppocr/modeling/backbones/rec_resnet_31.py +0 -210
  168. pyxlpr/ppocr/modeling/backbones/rec_resnet_aster.py +0 -143
  169. pyxlpr/ppocr/modeling/backbones/rec_resnet_fpn.py +0 -307
  170. pyxlpr/ppocr/modeling/backbones/rec_resnet_vd.py +0 -286
  171. pyxlpr/ppocr/modeling/heads/__init__.py +0 -54
  172. pyxlpr/ppocr/modeling/heads/cls_head.py +0 -52
  173. pyxlpr/ppocr/modeling/heads/det_db_head.py +0 -118
  174. pyxlpr/ppocr/modeling/heads/det_east_head.py +0 -121
  175. pyxlpr/ppocr/modeling/heads/det_pse_head.py +0 -37
  176. pyxlpr/ppocr/modeling/heads/det_sast_head.py +0 -128
  177. pyxlpr/ppocr/modeling/heads/e2e_pg_head.py +0 -253
  178. pyxlpr/ppocr/modeling/heads/kie_sdmgr_head.py +0 -206
  179. pyxlpr/ppocr/modeling/heads/multiheadAttention.py +0 -163
  180. pyxlpr/ppocr/modeling/heads/rec_aster_head.py +0 -393
  181. pyxlpr/ppocr/modeling/heads/rec_att_head.py +0 -202
  182. pyxlpr/ppocr/modeling/heads/rec_ctc_head.py +0 -88
  183. pyxlpr/ppocr/modeling/heads/rec_nrtr_head.py +0 -826
  184. pyxlpr/ppocr/modeling/heads/rec_sar_head.py +0 -402
  185. pyxlpr/ppocr/modeling/heads/rec_srn_head.py +0 -280
  186. pyxlpr/ppocr/modeling/heads/self_attention.py +0 -406
  187. pyxlpr/ppocr/modeling/heads/table_att_head.py +0 -246
  188. pyxlpr/ppocr/modeling/necks/__init__.py +0 -32
  189. pyxlpr/ppocr/modeling/necks/db_fpn.py +0 -111
  190. pyxlpr/ppocr/modeling/necks/east_fpn.py +0 -188
  191. pyxlpr/ppocr/modeling/necks/fpn.py +0 -138
  192. pyxlpr/ppocr/modeling/necks/pg_fpn.py +0 -314
  193. pyxlpr/ppocr/modeling/necks/rnn.py +0 -92
  194. pyxlpr/ppocr/modeling/necks/sast_fpn.py +0 -284
  195. pyxlpr/ppocr/modeling/necks/table_fpn.py +0 -110
  196. pyxlpr/ppocr/modeling/transforms/__init__.py +0 -28
  197. pyxlpr/ppocr/modeling/transforms/stn.py +0 -135
  198. pyxlpr/ppocr/modeling/transforms/tps.py +0 -308
  199. pyxlpr/ppocr/modeling/transforms/tps_spatial_transformer.py +0 -156
  200. pyxlpr/ppocr/optimizer/__init__.py +0 -61
  201. pyxlpr/ppocr/optimizer/learning_rate.py +0 -228
  202. pyxlpr/ppocr/optimizer/lr_scheduler.py +0 -49
  203. pyxlpr/ppocr/optimizer/optimizer.py +0 -160
  204. pyxlpr/ppocr/optimizer/regularizer.py +0 -52
  205. pyxlpr/ppocr/postprocess/__init__.py +0 -55
  206. pyxlpr/ppocr/postprocess/cls_postprocess.py +0 -33
  207. pyxlpr/ppocr/postprocess/db_postprocess.py +0 -234
  208. pyxlpr/ppocr/postprocess/east_postprocess.py +0 -143
  209. pyxlpr/ppocr/postprocess/locality_aware_nms.py +0 -200
  210. pyxlpr/ppocr/postprocess/pg_postprocess.py +0 -52
  211. pyxlpr/ppocr/postprocess/pse_postprocess/__init__.py +0 -15
  212. pyxlpr/ppocr/postprocess/pse_postprocess/pse/__init__.py +0 -29
  213. pyxlpr/ppocr/postprocess/pse_postprocess/pse/setup.py +0 -14
  214. pyxlpr/ppocr/postprocess/pse_postprocess/pse_postprocess.py +0 -118
  215. pyxlpr/ppocr/postprocess/rec_postprocess.py +0 -654
  216. pyxlpr/ppocr/postprocess/sast_postprocess.py +0 -355
  217. pyxlpr/ppocr/tools/__init__.py +0 -14
  218. pyxlpr/ppocr/tools/eval.py +0 -83
  219. pyxlpr/ppocr/tools/export_center.py +0 -77
  220. pyxlpr/ppocr/tools/export_model.py +0 -129
  221. pyxlpr/ppocr/tools/infer/predict_cls.py +0 -151
  222. pyxlpr/ppocr/tools/infer/predict_det.py +0 -300
  223. pyxlpr/ppocr/tools/infer/predict_e2e.py +0 -169
  224. pyxlpr/ppocr/tools/infer/predict_rec.py +0 -414
  225. pyxlpr/ppocr/tools/infer/predict_system.py +0 -204
  226. pyxlpr/ppocr/tools/infer/utility.py +0 -629
  227. pyxlpr/ppocr/tools/infer_cls.py +0 -83
  228. pyxlpr/ppocr/tools/infer_det.py +0 -134
  229. pyxlpr/ppocr/tools/infer_e2e.py +0 -122
  230. pyxlpr/ppocr/tools/infer_kie.py +0 -153
  231. pyxlpr/ppocr/tools/infer_rec.py +0 -146
  232. pyxlpr/ppocr/tools/infer_table.py +0 -107
  233. pyxlpr/ppocr/tools/program.py +0 -596
  234. pyxlpr/ppocr/tools/test_hubserving.py +0 -117
  235. pyxlpr/ppocr/tools/train.py +0 -163
  236. pyxlpr/ppocr/tools/xlprog.py +0 -748
  237. pyxlpr/ppocr/utils/EN_symbol_dict.txt +0 -94
  238. pyxlpr/ppocr/utils/__init__.py +0 -24
  239. pyxlpr/ppocr/utils/dict/ar_dict.txt +0 -117
  240. pyxlpr/ppocr/utils/dict/arabic_dict.txt +0 -162
  241. pyxlpr/ppocr/utils/dict/be_dict.txt +0 -145
  242. pyxlpr/ppocr/utils/dict/bg_dict.txt +0 -140
  243. pyxlpr/ppocr/utils/dict/chinese_cht_dict.txt +0 -8421
  244. pyxlpr/ppocr/utils/dict/cyrillic_dict.txt +0 -163
  245. pyxlpr/ppocr/utils/dict/devanagari_dict.txt +0 -167
  246. pyxlpr/ppocr/utils/dict/en_dict.txt +0 -63
  247. pyxlpr/ppocr/utils/dict/fa_dict.txt +0 -136
  248. pyxlpr/ppocr/utils/dict/french_dict.txt +0 -136
  249. pyxlpr/ppocr/utils/dict/german_dict.txt +0 -143
  250. pyxlpr/ppocr/utils/dict/hi_dict.txt +0 -162
  251. pyxlpr/ppocr/utils/dict/it_dict.txt +0 -118
  252. pyxlpr/ppocr/utils/dict/japan_dict.txt +0 -4399
  253. pyxlpr/ppocr/utils/dict/ka_dict.txt +0 -153
  254. pyxlpr/ppocr/utils/dict/korean_dict.txt +0 -3688
  255. pyxlpr/ppocr/utils/dict/latin_dict.txt +0 -185
  256. pyxlpr/ppocr/utils/dict/mr_dict.txt +0 -153
  257. pyxlpr/ppocr/utils/dict/ne_dict.txt +0 -153
  258. pyxlpr/ppocr/utils/dict/oc_dict.txt +0 -96
  259. pyxlpr/ppocr/utils/dict/pu_dict.txt +0 -130
  260. pyxlpr/ppocr/utils/dict/rs_dict.txt +0 -91
  261. pyxlpr/ppocr/utils/dict/rsc_dict.txt +0 -134
  262. pyxlpr/ppocr/utils/dict/ru_dict.txt +0 -125
  263. pyxlpr/ppocr/utils/dict/ta_dict.txt +0 -128
  264. pyxlpr/ppocr/utils/dict/table_dict.txt +0 -277
  265. pyxlpr/ppocr/utils/dict/table_structure_dict.txt +0 -2759
  266. pyxlpr/ppocr/utils/dict/te_dict.txt +0 -151
  267. pyxlpr/ppocr/utils/dict/ug_dict.txt +0 -114
  268. pyxlpr/ppocr/utils/dict/uk_dict.txt +0 -142
  269. pyxlpr/ppocr/utils/dict/ur_dict.txt +0 -137
  270. pyxlpr/ppocr/utils/dict/xi_dict.txt +0 -110
  271. pyxlpr/ppocr/utils/dict90.txt +0 -90
  272. pyxlpr/ppocr/utils/e2e_metric/Deteval.py +0 -574
  273. pyxlpr/ppocr/utils/e2e_metric/polygon_fast.py +0 -83
  274. pyxlpr/ppocr/utils/e2e_utils/extract_batchsize.py +0 -87
  275. pyxlpr/ppocr/utils/e2e_utils/extract_textpoint_fast.py +0 -457
  276. pyxlpr/ppocr/utils/e2e_utils/extract_textpoint_slow.py +0 -592
  277. pyxlpr/ppocr/utils/e2e_utils/pgnet_pp_utils.py +0 -162
  278. pyxlpr/ppocr/utils/e2e_utils/visual.py +0 -162
  279. pyxlpr/ppocr/utils/en_dict.txt +0 -95
  280. pyxlpr/ppocr/utils/gen_label.py +0 -81
  281. pyxlpr/ppocr/utils/ic15_dict.txt +0 -36
  282. pyxlpr/ppocr/utils/iou.py +0 -54
  283. pyxlpr/ppocr/utils/logging.py +0 -69
  284. pyxlpr/ppocr/utils/network.py +0 -84
  285. pyxlpr/ppocr/utils/ppocr_keys_v1.txt +0 -6623
  286. pyxlpr/ppocr/utils/profiler.py +0 -110
  287. pyxlpr/ppocr/utils/save_load.py +0 -150
  288. pyxlpr/ppocr/utils/stats.py +0 -72
  289. pyxlpr/ppocr/utils/utility.py +0 -80
  290. pyxlpr/ppstructure/__init__.py +0 -13
  291. pyxlpr/ppstructure/predict_system.py +0 -187
  292. pyxlpr/ppstructure/table/__init__.py +0 -13
  293. pyxlpr/ppstructure/table/eval_table.py +0 -72
  294. pyxlpr/ppstructure/table/matcher.py +0 -192
  295. pyxlpr/ppstructure/table/predict_structure.py +0 -136
  296. pyxlpr/ppstructure/table/predict_table.py +0 -221
  297. pyxlpr/ppstructure/table/table_metric/__init__.py +0 -16
  298. pyxlpr/ppstructure/table/table_metric/parallel.py +0 -51
  299. pyxlpr/ppstructure/table/table_metric/table_metric.py +0 -247
  300. pyxlpr/ppstructure/table/tablepyxl/__init__.py +0 -13
  301. pyxlpr/ppstructure/table/tablepyxl/style.py +0 -283
  302. pyxlpr/ppstructure/table/tablepyxl/tablepyxl.py +0 -118
  303. pyxlpr/ppstructure/utility.py +0 -71
  304. pyxlpr/xlai.py +0 -10
  305. /pyxllib/{ext/autogui → autogui}/virtualkey.py +0 -0
  306. {pyxllib-0.3.96.dist-info → pyxllib-0.3.197.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,421 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # @Author : 陈坤泽
4
+ # @Email : 877362867@qq.com
5
+ # @Date : 2024/11/05
6
+
7
+ """
8
+ 微信 消息框中不同消息结构的解析器 工具
9
+
10
+ todo 这个文件很多值可以写成枚举类型更规范?
11
+ """
12
+
13
+ import re
14
+ import json
15
+
16
+ # 定义全局字典
17
+ msg_parsers = {}
18
+
19
+
20
+ # 定义装饰器
21
+ def register_tool(key):
22
+ def decorator(func):
23
+ # 将函数添加到全局字典中,键为传入的key参数
24
+ msg_parsers[key] = func
25
+ return func
26
+
27
+ return decorator
28
+
29
+
30
+ def fmt_text(text):
31
+ return text.replace('\n', ' ')
32
+
33
+
34
+ def parse_time_string(time_str):
35
+ """
36
+ 解析不同格式的时间字符串为标准的 Python datetime 类型。
37
+
38
+ 支持以下格式:
39
+ 1. 完整的日期和时间,如 "2024年10月31日 22:03",解析为对应的 datetime 对象。
40
+ 2. 仅包含时间的字符串,如 "19:18",会被解析为当天的时间点,日期部分设为当前日期。
41
+ 3. 特殊字符串 "以下为新消息",解析为当前时间的 datetime 对象。
42
+
43
+ 示例输入:
44
+ - "2024年10月31日 22:03"
45
+ - "2024年11月1日 20:09"
46
+ - "2024年11月1日 21:08"
47
+ - "19:18"
48
+ - "20:16"
49
+ - "以下为新消息"
50
+ """
51
+ from datetime import datetime
52
+
53
+ # 定义当前日期
54
+ now = datetime.now()
55
+
56
+ # 定义不同的正则表达式模式
57
+ full_date_pattern = r"(\d{4})年(\d{1,2})月(\d{1,2})日\s+(\d{1,2}):(\d{2})"
58
+ partial_time_pattern = r"^(\d{1,2}):(\d{2})$"
59
+ new_message_pattern = r"^以下为新消息$"
60
+
61
+ # 解析完整的日期时间
62
+ match = re.match(full_date_pattern, time_str)
63
+ if match:
64
+ year, month, day, hour, minute = map(int, match.groups())
65
+ return datetime(year, month, day, hour, minute)
66
+
67
+ # 解析只有时间(小时:分钟),默认当天的日期
68
+ match = re.match(partial_time_pattern, time_str)
69
+ if match:
70
+ hour, minute = map(int, match.groups())
71
+ return datetime(now.year, now.month, now.day, hour, minute)
72
+
73
+ # 解析 "以下为新消息" 为当前时间
74
+ if re.match(new_message_pattern, time_str):
75
+ return now
76
+
77
+ # 如果未匹配到任何模式,返回 None 表示无法解析
78
+ return None
79
+
80
+
81
+ @register_tool("1b2p3p3b3p")
82
+ def 系统_查看更多消息(node):
83
+ node.msg_type = 'system'
84
+ node.content_type = 'button_more'
85
+ node.render_text = f'⚙️: 点击此处可 {node.text}'
86
+
87
+
88
+ @register_tool("1l2t")
89
+ def 系统_时间标签(node):
90
+ node.msg_type = 'system'
91
+ node.content_type = 'time'
92
+ node.time = parse_time_string(node.text)
93
+ node.render_text = f'⚙️: {node.text}'
94
+
95
+
96
+ # 刚刚发送的特殊时间标签
97
+ @register_tool("1l2p3p3p3t3p3p")
98
+ def 系统_时间标签2(node):
99
+ node.msg_type = 'system'
100
+ node.content_type = 'time'
101
+ node.time = parse_time_string(node.text)
102
+ node.render_text = f'⚙️: {node.text}'
103
+
104
+
105
+ @register_tool("1l2p3p3t3p")
106
+ def 系统_撤回消息(node):
107
+ node.msg_type = 'system'
108
+
109
+ if node.text.endswith('撤回了一条消息'):
110
+ node.content_type = 'recall'
111
+ if node.text == '你撤回了一条消息':
112
+ node.user = node.user2 = '你'
113
+ else:
114
+ node.user = re.search(r'"(.+)"', node.text).group(1)
115
+ node.user2 = node.user
116
+ node.render_text = f'⚙️: {node.text}'
117
+ elif node.text.endswith('现在可以开始聊天了。'):
118
+ node.content_type = 'add_friend'
119
+ node.user = re.search(r'你已添加了(.+?),现在可以开始聊天了。', node.text).group(1)
120
+ node.user2 = node.user
121
+ node.render_text = f'⚙️: {node.text}'
122
+ else:
123
+ raise ValueError
124
+
125
+
126
+ @register_tool("1l2p3p3p4p5p6p7t3b")
127
+ def 发送_文本(node):
128
+ node.msg_type = 'send'
129
+ node.content_type = 'text'
130
+ node.user = node[0][2].text
131
+ node.render_text = f'↑{node.user}: {fmt_text(node.text)}'
132
+
133
+
134
+ @register_tool("1l2p3p3p4p5p6p7p7p8p8b3b")
135
+ def 发送_图片(node):
136
+ node.msg_type = 'send'
137
+ node.content_type = 'image'
138
+ node.user = node[0][2].text
139
+
140
+
141
+ @register_tool("1l2p3p3p4p5p6p7p7p8p9t9p9b8b3b")
142
+ def 发送_视频(node):
143
+ node.msg_type = 'send'
144
+ node.content_type = 'video'
145
+ node.user = node[0][2].text
146
+
147
+
148
+ @register_tool("1l2p3p3p4p5p6p7p7p8p9p10p11t6p7p8p9p10p11t7b3b")
149
+ def 发送_文本_引用文本(node):
150
+ node.msg_type = 'send'
151
+ node.content_type = 'text'
152
+ node.user = node[0][2].text
153
+ quoted_node = node[0][1][0][0] # 引用的层级结构
154
+ node.text = quoted_node[0][1][0][0][0][0].text
155
+ node.cite_text = quoted_node[1][0][0][0][0][0].text
156
+
157
+
158
+ @register_tool("1l2p3p3p4p5p6p7p7p8p9p10p11t6p7p8p9p10p11t11t11p12b7b3b")
159
+ def 发送_文本_引用图片(node):
160
+ node.msg_type = 'send'
161
+ node.content_type = 'text'
162
+ node.user = node[0][2].text
163
+ quoted_node = node[0][1][0][0]
164
+ node.text = quoted_node[0][1][0][0][0][0].text
165
+ node.cite_text = f'{quoted_node[1][0][0][0][0][0].text} : [图片]'
166
+
167
+
168
+ # 引用文件
169
+ @register_tool("1l2p3p3p4p5p6p7p7p8p9p10p11t6p7p8p9p10p11p12t11p12b7b3b")
170
+ def 发送_文本_引用文件(node):
171
+ node.msg_type = 'send'
172
+ node.content_type = 'text'
173
+ node.user = node[0][2].text
174
+ quoted_node = node[0][1][0][0]
175
+ node.text = quoted_node[0][1][0][0][0][0].text
176
+ node.cite_text = f'[文件] {quoted_node[1][0][0][0][0][0][0].text}'
177
+
178
+
179
+ # 发送文件
180
+ @register_tool("1l2p3p3p4p5p6p7p8p9p10t10p11t11t9p10p10p8p9t7b7b3b")
181
+ def 发送_文本_引用文件(node):
182
+ node.msg_type = 'send'
183
+ node.content_type = 'file'
184
+ node.user = node[0][2].text
185
+ file_node = node[0][1][0][0][0][0]
186
+ desc = {
187
+ 'name': file_node[0][0][0].text,
188
+ 'size': file_node[0][0][1][0].text,
189
+ 'platform': file_node[1][0].text
190
+ }
191
+ node.text = json.dumps(desc, ensure_ascii=False)
192
+
193
+
194
+ # 发送链接
195
+ @register_tool("1l2p3p3p4p5p6p7p8t8p9t9p9b8p9b9t7b3b")
196
+ def 发送_链接(node):
197
+ node.msg_type = 'send'
198
+ node.content_type = 'link'
199
+ node.user = node[0][2].text
200
+ link_node = node[0][1][0][0]
201
+ desc = {
202
+ 'title': link_node[0][0][0].text,
203
+ 'head': link_node[0][0][1][0].text,
204
+ 'author': link_node[0][0][2][1].text
205
+ }
206
+ node.text = json.dumps(desc, ensure_ascii=False)
207
+
208
+
209
+ # 转发消息
210
+ @register_tool("1l2p3p3p4p5p6p7p8t8p9t9t9t8p9p9t7b3b")
211
+ def 发送_转发消息(node):
212
+ node.msg_type = 'send'
213
+ node.content_type = 'messages'
214
+ node.user = node[0][2].text
215
+
216
+
217
+ @register_tool("1l2p3b3p4p5p6p7t3p")
218
+ def 接收_文本(node):
219
+ node.msg_type = 'receive'
220
+ node.content_type = 'text'
221
+ node.user = node[0][0].text
222
+ node.render_text = f'↓{node.user}: {fmt_text(node.text)}'
223
+
224
+
225
+ @register_tool("1l2p3b3p4p5p6p7p7b3p")
226
+ def 接收_图片(node):
227
+ node.msg_type = 'receive'
228
+ node.content_type = 'image'
229
+ node.user = node[0][0].text
230
+ node.render_text = f'↓{node.user}: [图片]'
231
+
232
+
233
+ @register_tool("1l2p3b3p4p5p6p7p8p9p10t10p11t11t9p10p10p8p9t7b7b3p")
234
+ def 接收_文件(node):
235
+ node.msg_type = 'receive'
236
+ node.content_type = 'file'
237
+ node.user = node[0][0].text
238
+ node.render_text = f'↓{node.user}: {fmt_text(node.text)}'
239
+
240
+
241
+ @register_tool("1l2p3b3p4p5p6p7p8b8p8t7b3p")
242
+ def 接收_语音(node):
243
+ node.msg_type = 'receive'
244
+ node.content_type = 'voice'
245
+ node.user = node[0][0].text
246
+
247
+
248
+ @register_tool("1l2p3b3p4p5p6p7p8t8p9t9p9b8p9b9t7b3p")
249
+ def 接收_链接(node):
250
+ node.msg_type = 'receive'
251
+ node.content_type = 'link'
252
+ node.user = node[0][0].text
253
+ link_node = node[0][1][0][0][0][0] # 链接内容的层级结构
254
+ desc = {
255
+ 'title': link_node[0].text,
256
+ 'head': link_node[1][0].text, # 文章开头部分
257
+ 'author': link_node[2][1].text
258
+ }
259
+ node.text = json.dumps(desc, ensure_ascii=False)
260
+
261
+
262
+ @register_tool("1l2p3b3p4p5t4p5p6p7t3p")
263
+ def 群接收_文本(node):
264
+ node.msg_type = 'receive'
265
+ node.content_type = 'text'
266
+ node.user = node[0][0].text
267
+ node.user2 = node[0][1][0][0].text
268
+
269
+
270
+ @register_tool("1l2p3b3p4p5t4p5p6p7p7b3p")
271
+ def 群接收_图片(node):
272
+ node.msg_type = 'receive'
273
+ node.content_type = 'image'
274
+ node.user = node[0][0].text
275
+ node.user2 = node[0][1][0][0].text
276
+
277
+
278
+ @register_tool("1l2p3b3p4p5t4p5p6p7p8p9p10t10p11t11t9p10p10p7b7b3p")
279
+ def 群接收_文件(node):
280
+ node.msg_type = 'receive'
281
+ node.content_type = 'file'
282
+ node.user = node[0][0].text
283
+ file_node = node[0][1] # 文件信息的节点
284
+ node.user2 = file_node[0][0].text
285
+ file_info_node = file_node[1][0][0][0][0]
286
+ desc = {
287
+ 'name': file_info_node[0][0].text,
288
+ 'size': file_info_node[0][1][0].text
289
+ }
290
+ node.text = json.dumps(desc, ensure_ascii=False)
291
+
292
+
293
+ @register_tool("1l2p3b3p4p5t4p5p6p7p8p9p10t10p11t11t9p10p10p8p9t7b7b3p")
294
+ def 群接收_文件2(node):
295
+ node.msg_type = 'receive'
296
+ node.content_type = 'file'
297
+ node.user = node[0][0].text
298
+ node.user2 = node[0][1][0][0].text
299
+ file_info_node = node[0][1][1][0][0][0]
300
+ desc = {
301
+ 'name': file_info_node[0][0][0].text,
302
+ 'size': file_info_node[0][0][1][0].text,
303
+ 'platform': file_info_node[1][0].text
304
+ }
305
+ node.text = json.dumps(desc, ensure_ascii=False)
306
+
307
+
308
+ @register_tool("1l2p3b3p4p5t4p5p6p7b3p")
309
+ def 群接收_动画表情(node):
310
+ node.msg_type = 'receive'
311
+ node.content_type = 'emoji'
312
+ node.user = node[0][0].text
313
+ node.user2 = node[0][1][0][0].text
314
+
315
+
316
+ @register_tool("1l2p3b3p4p5t4p5p6p7p8t8p9t9p9b8p9b9t7b3p")
317
+ def 群接收_链接_有公众号作者名(node):
318
+ node.msg_type = 'receive'
319
+ node.content_type = 'link'
320
+ node.user = node[0][0].text
321
+ link_node = node[0][1]
322
+ node.user2 = link_node[0][0].text
323
+ link_info_node = link_node[1][0][0][0]
324
+ desc = {
325
+ 'title': link_info_node[0].text,
326
+ 'head': link_info_node[1][0].text, # 文章开头的一小部分
327
+ 'author': link_info_node[2][0].text
328
+ }
329
+ node.text = json.dumps(desc, ensure_ascii=False)
330
+
331
+
332
+ @register_tool("1l2p3b3p4p5t4p5p6p7p8t8p9t9p9b7b3p")
333
+ def 群接收_链接_无公众号作者名(node):
334
+ node.msg_type = 'receive'
335
+ node.content_type = 'link'
336
+ node.user = node[0][0].text
337
+ link_node = node[0][1]
338
+ node.user2 = link_node[0][0].text
339
+ link_info_node = link_node[1][0][0][0]
340
+ desc = {
341
+ 'title': link_info_node[0].text,
342
+ 'head': link_info_node[1][0].text # 文章开头的一小部分
343
+ }
344
+ node.text = json.dumps(desc, ensure_ascii=False)
345
+
346
+
347
+ @register_tool("1l2p3b3p4p5t4p5p6p7p8b8p9p8p9p10p10t8p9p7b3p")
348
+ def 群接收_视频(node):
349
+ node.msg_type = 'receive'
350
+ node.content_type = 'video'
351
+ node.user = node[0][0].text
352
+ video_node = node[0][1]
353
+ node.user2 = video_node[0][0].text
354
+ video_info_node = video_node[1][0][0][0]
355
+ desc = {
356
+ 'author': video_info_node[2][0][1].text
357
+ }
358
+ node.text = json.dumps(desc, ensure_ascii=False)
359
+
360
+
361
+ @register_tool("1l2p3b3p4p5t4p5p6p7p8t8p9t8t7b3p")
362
+ def 群接收_视频2(node):
363
+ node.msg_type = 'receive'
364
+ node.content_type = 'video'
365
+ node.user = node[0][0].text
366
+ video_node = node[0][1]
367
+ node.user2 = video_node[0][0].text
368
+ video_info_node = video_node[1][0][0][0]
369
+ desc = {
370
+ 'title': video_info_node[0].text,
371
+ 'head': video_info_node[1][0].text,
372
+ 'duration': video_info_node[2].text
373
+ }
374
+ node.text = json.dumps(desc, ensure_ascii=False)
375
+
376
+
377
+ @register_tool("1l2p3b3p4p5t4p5p6p7p8p9b9t8t8b8p9t9t7b3p")
378
+ def 群接收_小程序(node):
379
+ node.msg_type = 'receive'
380
+ node.content_type = 'applet'
381
+ node.user = node[0][0].text
382
+ applet_node = node[0][1]
383
+ node.user2 = applet_node[0][0].text
384
+ applet_info_node = applet_node[1][0][0][0]
385
+ desc = {
386
+ 'name': applet_info_node[0][1].text,
387
+ 'title': applet_info_node[1].text,
388
+ 'footnote': applet_info_node[3][1].text
389
+ }
390
+ node.text = json.dumps(desc, ensure_ascii=False)
391
+
392
+
393
+ @register_tool("1l2p3p3p4p5l6p7p7t7p3p")
394
+ def 群接收_拍一拍(node):
395
+ node.msg_type = 'receive'
396
+ node.content_type = 'shake'
397
+ node.text = node[0][1][0][0].text
398
+ node.user = re.search(r'"(.+?)" 拍了拍 "', node.text).group(1)
399
+
400
+
401
+ @register_tool("1l2p3b3p4p5t4p5p6p7p8t4p5p6p7p8p9t5b3p")
402
+ def 群接收_引用消息(node):
403
+ node.msg_type = 'receive'
404
+ node.content_type = 'text'
405
+ node.user = node[0][0].text
406
+ quoted_node = node[0][1]
407
+ node.user2 = quoted_node[0][0].text
408
+ node.text = quoted_node[1][0][0][0][0].text
409
+ node.cite_text = quoted_node[2][0][0][0][0][0].text
410
+
411
+
412
+ @register_tool("1l2p3b3p4p5t4p5p6p7p8t4p5p6p7p8p9p10t9p10b5b3p")
413
+ def 群接收_引用文件(node):
414
+ node.msg_type = 'receive'
415
+ node.content_type = 'text'
416
+ node.user = node[0][0].text
417
+ quoted_node = node[0][1]
418
+ node.user2 = quoted_node[0][0].text
419
+ node.text = quoted_node[1][0][0][0][0].text
420
+ file_node = quoted_node[2][0][0][0][0][0][0]
421
+ node.cite_text = f'[文件] {file_node.text}'
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # @Author : 陈坤泽
4
+ # @Email : 877362867@qq.com
5
+ # @Date : 2024/12/16
6
+
7
+ import sys
8
+
9
+ from loguru import logger
10
+
11
+ if sys.platform == 'win32':
12
+ try: # 尝试加载VIP版
13
+ from wxautox import WeChat
14
+ from wxautox.elements import WxParam # WxParam.DEFALUT_SAVEPATH 可以用来配置数据自动保存位置
15
+ except ModuleNotFoundError: # 否则用贫民版
16
+ from wxauto import WeChat
17
+ from wxauto.elements import WxParam
18
+
19
+ from pyxllib.prog.filelock import get_autoui_lock
20
+
21
+
22
+ class WeChatSingletonLock:
23
+ """ 基于 get_autoui_lock 的微信全局唯一单例控制器,确保同一时间仅有一个微信自动化程序在操作 """
24
+
25
+ def __init__(self, lock_timeout=-1, *, init=True):
26
+ # 初始化全局锁
27
+ self.lock = get_autoui_lock(timeout=lock_timeout)
28
+ self.wx = WeChat() if init else None
29
+
30
+ def __enter__(self):
31
+ # 获取锁并激活微信窗口
32
+ self.lock.acquire()
33
+ if self.wx:
34
+ self.wx._show()
35
+ return self.wx
36
+
37
+ def __exit__(self, exc_type, exc_value, traceback):
38
+ # 释放锁
39
+ self.lock.release()
40
+
41
+
42
+ def wechat_lock_send(user, text=None, files=None, *, timeout=-1):
43
+ """ 使用全局唯一单例锁,确保同一时间仅有一个微信自动化程序在操作 """
44
+ with WeChatSingletonLock(timeout) as we:
45
+ # 241223周一12:27,今天可被这个默认2秒坑惨了,往错误群一直发骚扰消息
46
+ # 22:07,但我复测,感觉不可能找不到啊,为什么会找到禅宗考勤管理群呢,太离谱了
47
+ status = we.ChatWith(user, timeout=5)
48
+
49
+ if status != user:
50
+ raise ValueError(f'无法找到用户:{user}')
51
+
52
+ if text:
53
+ we.SendMsg(text, user)
54
+ if files:
55
+ we.SendFiles(files, user)
56
+
57
+
58
+ def wechat_handler(message):
59
+ # 获取群名,如果没有指定,不使用此微信发送功能
60
+ user = message.record["extra"].get("wechat_user")
61
+ if user:
62
+ wechat_lock_send(user, message)
63
+
64
+
65
+ if sys.platform == 'win32':
66
+ # 创建专用的微信日志记录器,不绑定默认群名
67
+ wechat_logger = logger.bind(wechat_user='文件传输助手')
68
+
69
+ # 添加专用的微信处理器
70
+ wechat_logger.add(wechat_handler,
71
+ format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {name}:{function}:{line} - {message}")
72
+
73
+ """ 往微信发送特殊的日志格式报告
74
+ 用法:wechat_logger.bind(wechat_user='文件传输助手').info(message)
75
+
76
+ 或者:
77
+ # 先做好默认群名绑定
78
+ wechat_logger = wechat_logger.bind(wechat_user='考勤管理')
79
+ # 然后就能普通logger用法发送了
80
+ wechat_logger.info('测试')
81
+ """
82
+ else:
83
+ # 降级为普通logger
84
+ wechat_logger = logger
@@ -0,0 +1,137 @@
1
+ import os
2
+ import cv2
3
+ import numpy as np
4
+ from xlproject.code4101 import XlPath
5
+
6
+ from pyxllib.file.specialist import download_file
7
+ from pyxllib.prog.pupil import is_url
8
+
9
+
10
+ class SliderCaptchaLocator:
11
+ """ 滑动验证码的位置定位
12
+
13
+ 由于缺口一般是一个黑底图,整体颜色是偏暗的,可以利用这个特性来定位列所在位置
14
+ """
15
+
16
+ def __init__(self, background_image_path, slider_image_path=None):
17
+ """
18
+ :param background_image_path: 背景主图片
19
+ :param slider_image_path: 滑动条图片
20
+ """
21
+ # 支持输入是url的情况
22
+ if is_url(background_image_path):
23
+ background_image_path = download_file(background_image_path, temp=True, ext='.png')
24
+ if is_url(slider_image_path):
25
+ slider_image_path = download_file(slider_image_path, temp=True, ext='.png')
26
+ # 初始化图片等数据
27
+ self.image_path = background_image_path
28
+ self.image = cv2.imread(self.image_path)
29
+ self.darkest_rows = None
30
+ self.column_averages = None
31
+ self.enhanced_averages = None
32
+ self.radius = 24
33
+ if slider_image_path is not None:
34
+ # 先读取图片
35
+ template_image = cv2.imread(slider_image_path)
36
+ # 取图片宽度的40%作为radius
37
+ self.radius = int(template_image.shape[1] * 0.4)
38
+
39
+ def select_darkest_part_rows(self, fraction=0.1):
40
+ """
41
+ 选择最暗的部分行,供下游计算列的平均暗度
42
+
43
+ :param float fraction: 最暗行的比例
44
+ :return: 最暗行的图像部分
45
+ """
46
+ if self.darkest_rows is None:
47
+ average_row_values = np.mean(self.image, axis=(1, 2))
48
+ threshold_index = int(len(average_row_values) * fraction)
49
+ darkest_indices = np.argsort(average_row_values)[:threshold_index]
50
+ self.darkest_rows = self.image[darkest_indices]
51
+ return self.darkest_rows
52
+
53
+ def calculate_column_averages(self):
54
+ """
55
+ 计算列的平均强度。
56
+
57
+ :return: 列的平均强度
58
+ """
59
+ if self.column_averages is None:
60
+ rows = self.select_darkest_part_rows()
61
+ column_values = np.mean(rows, axis=0)
62
+ column_intensity = np.mean(column_values, axis=1)
63
+ min_val, max_val = np.min(column_intensity), np.max(column_intensity)
64
+ self.column_averages = ((column_intensity - min_val) / (max_val - min_val) * 255).astype(
65
+ np.uint8) if max_val != min_val else np.zeros_like(column_intensity)
66
+ return self.column_averages
67
+
68
+ def enhance_column_averages(self):
69
+ """
70
+ 增强列平均值,利用验证码的黑度图的对称性,进行一个增强,使得其他非验证码的零散黑条变白
71
+
72
+ :param int radius: 对比半径,这个建议设成模版图宽度的40%~50%
73
+ :return: 增强后的列平均值
74
+ """
75
+ if self.enhanced_averages is None:
76
+ averages = self.calculate_column_averages()
77
+ length = len(averages)
78
+ enhanced = np.zeros_like(averages)
79
+ for i in range(length):
80
+ max_value = averages[i]
81
+ for j in range(1, self.radius // 2 + 1):
82
+ if i - j >= 0 and i + j < length:
83
+ max_value = max(max_value, abs(int(averages[i - j]) - int(averages[i + j])))
84
+ enhanced[i] = max_value
85
+ self.enhanced_averages = enhanced
86
+ return self.enhanced_averages
87
+
88
+ def find_captcha_position(self, threshold=50):
89
+ """
90
+ 查找验证码的位置。
91
+
92
+ :param int threshold: 达到多少像素后,开始计算中位数
93
+ :return: 验证码的列中心位置
94
+ """
95
+ intensities = self.enhance_column_averages()
96
+ indices = []
97
+ for intensity in range(256):
98
+ new_indices = np.where(intensities == intensity)[0]
99
+ if len(indices) + len(new_indices) > threshold:
100
+ indices.extend(new_indices[:threshold - len(indices)])
101
+ break
102
+ indices.extend(new_indices)
103
+ return int(np.median(indices))
104
+
105
+ def draw_red_line(self, position, line_thickness=3):
106
+ """
107
+ 在图像上绘制红线。
108
+
109
+ :param int position: 红线位置
110
+ :param int line_thickness: 线宽
111
+ :return: 绘制红线后的图像
112
+ """
113
+ gray_image = np.tile(self.calculate_column_averages(), (100, 1)).astype(np.uint8)
114
+ color_image = cv2.cvtColor(gray_image, cv2.COLOR_GRAY2BGR)
115
+ cv2.line(color_image, (position, 0), (position, gray_image.shape[0]), (0, 0, 255), line_thickness)
116
+ return color_image
117
+
118
+ def debug(self):
119
+ position = self.find_captcha_position()
120
+ final_image = self.draw_red_line(position)
121
+ cv2.imshow('Image with Red Line', final_image)
122
+ cv2.waitKey(0)
123
+ cv2.destroyAllWindows()
124
+
125
+
126
+ def main():
127
+ os.chdir(XlPath.desktop())
128
+ locator = SliderCaptchaLocator('cap_union_new_getcapbysig.png')
129
+ position = locator.find_captcha_position()
130
+ final_image = locator.draw_red_line(position)
131
+ cv2.imshow('Image with Red Line', final_image)
132
+ cv2.waitKey(0)
133
+ cv2.destroyAllWindows()
134
+
135
+
136
+ if __name__ == '__main__':
137
+ main()