nonebot-plugin-latex 0.0.2.2__py3-none-any.whl → 0.0.3__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.
- nonebot_plugin_latex/__init__.py +16 -5
- nonebot_plugin_latex/converter.py +8 -1
- nonebot_plugin_latex/data.py +91 -65
- nonebot_plugin_latex/main.py +4 -3
- {nonebot_plugin_latex-0.0.2.2.dist-info → nonebot_plugin_latex-0.0.3.dist-info}/METADATA +10 -9
- nonebot_plugin_latex-0.0.3.dist-info/RECORD +10 -0
- nonebot_plugin_latex-0.0.2.2.dist-info/RECORD +0 -10
- {nonebot_plugin_latex-0.0.2.2.dist-info → nonebot_plugin_latex-0.0.3.dist-info}/LICENSE +0 -0
- {nonebot_plugin_latex-0.0.2.2.dist-info → nonebot_plugin_latex-0.0.3.dist-info}/WHEEL +0 -0
- {nonebot_plugin_latex-0.0.2.2.dist-info → nonebot_plugin_latex-0.0.3.dist-info}/top_level.txt +0 -0
nonebot_plugin_latex/__init__.py
CHANGED
@@ -13,13 +13,13 @@ MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
13
13
|
See the Mulan PSL v2 for more details.
|
14
14
|
"""
|
15
15
|
|
16
|
-
from nonebot import get_plugin_config
|
16
|
+
from nonebot import get_plugin_config, get_driver
|
17
17
|
from nonebot.plugin import PluginMetadata
|
18
18
|
|
19
19
|
from .config import Config
|
20
|
-
from .converter import
|
20
|
+
from .converter import _converter, get_converter
|
21
21
|
|
22
|
-
__version__ = "0.0.
|
22
|
+
__version__ = "0.0.3"
|
23
23
|
|
24
24
|
__author__ = "Eilles"
|
25
25
|
|
@@ -27,11 +27,22 @@ __plugin_meta__ = PluginMetadata(
|
|
27
27
|
name="LaTeX图形渲染插件",
|
28
28
|
description="从互联网服务渲染LaTeX公式并发送",
|
29
29
|
usage="发送 latex 或 公式,后接内容或回复公式信息。",
|
30
|
-
type="
|
31
|
-
homepage="https://github.com/
|
30
|
+
type="library",
|
31
|
+
homepage="https://github.com/EillesWan/nonebot-plugin-latex",
|
32
|
+
config=Config,
|
33
|
+
supported_adapters={
|
34
|
+
"~onebot.v11",
|
35
|
+
},
|
32
36
|
extra={"License": "Mulan PSL v2", "Author": __author__},
|
33
37
|
)
|
34
38
|
|
39
|
+
__all__ = ["get_converter"]
|
40
|
+
|
41
|
+
|
42
|
+
@get_driver().on_startup
|
43
|
+
async def init():
|
44
|
+
await _converter.load_channel()
|
45
|
+
|
35
46
|
|
36
47
|
config = get_plugin_config(Config)
|
37
48
|
|
nonebot_plugin_latex/data.py
CHANGED
@@ -13,10 +13,23 @@ MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
13
13
|
See the Mulan PSL v2 for more details.
|
14
14
|
"""
|
15
15
|
|
16
|
-
from typing import Optional, Literal, Tuple
|
17
|
-
import httpx
|
18
|
-
import time
|
19
16
|
import re
|
17
|
+
import time
|
18
|
+
import asyncio
|
19
|
+
from typing import Literal, Optional, Tuple
|
20
|
+
|
21
|
+
import httpx
|
22
|
+
from nonebot import logger
|
23
|
+
|
24
|
+
|
25
|
+
# 正则匹配 LaTeX 公式内容
|
26
|
+
LATEX_PATTERN = re.compile(
|
27
|
+
r"\\begin\{equation\}(.*?)\\end\{equation\}|(?<!\$)(\$(.*?)\$|\$\$(.*?)\$\$|\\\[(.*?)\\\]|\\\[.*?\\\]|\\\((.*?)\\\))",
|
28
|
+
re.DOTALL,
|
29
|
+
)
|
30
|
+
|
31
|
+
|
32
|
+
MAX_TIME = 0xFFFFFF
|
20
33
|
|
21
34
|
|
22
35
|
class ConvertChannel:
|
@@ -33,8 +46,8 @@ class ConvertChannel:
|
|
33
46
|
return False, "请勿直接调用母类"
|
34
47
|
|
35
48
|
@staticmethod
|
36
|
-
def channel_test() -> int:
|
37
|
-
return
|
49
|
+
async def channel_test() -> int:
|
50
|
+
return MAX_TIME
|
38
51
|
|
39
52
|
|
40
53
|
class L2PChannel(ConvertChannel):
|
@@ -52,6 +65,7 @@ class L2PChannel(ConvertChannel):
|
|
52
65
|
|
53
66
|
async with httpx.AsyncClient(
|
54
67
|
timeout=timeout,
|
68
|
+
verify=False,
|
55
69
|
) as client:
|
56
70
|
while retry > 0:
|
57
71
|
try:
|
@@ -87,36 +101,38 @@ class L2PChannel(ConvertChannel):
|
|
87
101
|
return False, "未知错误"
|
88
102
|
|
89
103
|
@staticmethod
|
90
|
-
def channel_test() -> int:
|
91
|
-
with httpx.
|
104
|
+
async def channel_test() -> int:
|
105
|
+
async with httpx.AsyncClient(timeout=5, verify=False) as client:
|
92
106
|
try:
|
93
107
|
start_time = time.time_ns()
|
94
108
|
latex2png = (
|
95
|
-
client.get(
|
109
|
+
await client.get(
|
96
110
|
"http://www.latex2png.com{}"
|
97
|
-
+
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
111
|
+
+ (
|
112
|
+
await client.post(
|
113
|
+
"http://www.latex2png.com/api/convert",
|
114
|
+
json={
|
115
|
+
"auth": {"user": "guest", "password": "guest"},
|
116
|
+
"latex": "\\\\int_{a}^{b} x^2 \\\\, dx = \\\\frac{b^3}{3} - \\\\frac{a^3}{5}\n",
|
117
|
+
"resolution": 600,
|
118
|
+
"color": "000000",
|
119
|
+
},
|
120
|
+
)
|
105
121
|
).json()["url"]
|
106
122
|
),
|
107
123
|
time.time_ns() - start_time,
|
108
124
|
)
|
109
125
|
except:
|
110
|
-
return
|
126
|
+
return MAX_TIME
|
111
127
|
if latex2png[0].status_code == 200:
|
112
128
|
return latex2png[1]
|
113
129
|
else:
|
114
|
-
return
|
130
|
+
return MAX_TIME
|
115
131
|
|
116
132
|
|
117
133
|
class CDCChannel(ConvertChannel):
|
118
134
|
|
119
|
-
URL = "
|
135
|
+
URL = "https://latex.codecogs.com"
|
120
136
|
|
121
137
|
async def get_to_convert(
|
122
138
|
self,
|
@@ -128,6 +144,7 @@ class CDCChannel(ConvertChannel):
|
|
128
144
|
) -> Tuple[Literal[True], bytes] | Tuple[Literal[False], bytes | str]:
|
129
145
|
async with httpx.AsyncClient(
|
130
146
|
timeout=timeout,
|
147
|
+
verify=False,
|
131
148
|
) as client:
|
132
149
|
|
133
150
|
while retry > 0:
|
@@ -152,27 +169,27 @@ class CDCChannel(ConvertChannel):
|
|
152
169
|
return False, "未知错误"
|
153
170
|
|
154
171
|
@staticmethod
|
155
|
-
def channel_test() -> int:
|
156
|
-
with httpx.
|
172
|
+
async def channel_test() -> int:
|
173
|
+
async with httpx.AsyncClient(timeout=5, verify=False) as client:
|
157
174
|
try:
|
158
175
|
start_time = time.time_ns()
|
159
176
|
codecogs = (
|
160
|
-
client.get(
|
161
|
-
r"
|
177
|
+
await client.get(
|
178
|
+
r"https://latex.codecogs.com/png.image?\huge%20\dpi{600}\\int_{a}^{b}x^2\\,dx=\\frac{b^3}{3}-\\frac{a^3}{5}"
|
162
179
|
),
|
163
180
|
time.time_ns() - start_time,
|
164
181
|
)
|
165
182
|
except:
|
166
|
-
return
|
183
|
+
return MAX_TIME
|
167
184
|
if codecogs[0].status_code == 200:
|
168
185
|
return codecogs[1]
|
169
186
|
else:
|
170
|
-
return
|
187
|
+
return MAX_TIME
|
171
188
|
|
172
189
|
|
173
190
|
class JRTChannel(ConvertChannel):
|
174
191
|
|
175
|
-
URL = "
|
192
|
+
URL = "https://latex2image.joeraut.com"
|
176
193
|
|
177
194
|
async def get_to_convert(
|
178
195
|
self,
|
@@ -185,6 +202,7 @@ class JRTChannel(ConvertChannel):
|
|
185
202
|
|
186
203
|
async with httpx.AsyncClient(
|
187
204
|
timeout=timeout,
|
205
|
+
verify=False,
|
188
206
|
) as client:
|
189
207
|
while retry > 0:
|
190
208
|
try:
|
@@ -196,7 +214,6 @@ class JRTChannel(ConvertChannel):
|
|
196
214
|
"outputScale": "{}%".format(dpi / 3 * 5),
|
197
215
|
},
|
198
216
|
)
|
199
|
-
print(post_response)
|
200
217
|
if post_response.status_code == 200:
|
201
218
|
|
202
219
|
if not (json_response := post_response.json())["error"]:
|
@@ -218,29 +235,31 @@ class JRTChannel(ConvertChannel):
|
|
218
235
|
return False, "未知错误"
|
219
236
|
|
220
237
|
@staticmethod
|
221
|
-
def channel_test() -> int:
|
222
|
-
with httpx.
|
238
|
+
async def channel_test() -> int:
|
239
|
+
async with httpx.AsyncClient(timeout=5, verify=False) as client:
|
223
240
|
try:
|
224
241
|
start_time = time.time_ns()
|
225
242
|
joeraut = (
|
226
|
-
client.get(
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
243
|
+
await client.get(
|
244
|
+
(
|
245
|
+
await client.post(
|
246
|
+
"http://www.latex2png.com/api/convert",
|
247
|
+
json={
|
248
|
+
"latexInput": "\\\\int_{a}^{b} x^2 \\\\, dx = \\\\frac{b^3}{3} - \\\\frac{a^3}{5}",
|
249
|
+
"outputFormat": "PNG",
|
250
|
+
"outputScale": "1000%",
|
251
|
+
},
|
252
|
+
)
|
234
253
|
).json()["imageUrl"]
|
235
254
|
),
|
236
255
|
time.time_ns() - start_time,
|
237
256
|
)
|
238
257
|
except:
|
239
|
-
return
|
258
|
+
return MAX_TIME
|
240
259
|
if joeraut[0].status_code == 200:
|
241
260
|
return joeraut[1]
|
242
261
|
else:
|
243
|
-
return
|
262
|
+
return MAX_TIME
|
244
263
|
|
245
264
|
|
246
265
|
CHANNEL_LIST: list[type[ConvertChannel]] = [L2PChannel, CDCChannel, JRTChannel]
|
@@ -248,20 +267,17 @@ CHANNEL_LIST: list[type[ConvertChannel]] = [L2PChannel, CDCChannel, JRTChannel]
|
|
248
267
|
|
249
268
|
class ConvertLatex:
|
250
269
|
|
251
|
-
channel: ConvertChannel
|
252
|
-
|
253
|
-
def __init__(self, channel: Optional[ConvertChannel] = None) -> None:
|
254
|
-
"""
|
255
|
-
LaTeX在线渲染类
|
270
|
+
channel: Optional[ConvertChannel]
|
256
271
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
[WARNING] 请注意!选择通道时采取的是同步函数,因此可能造成阻塞。
|
261
|
-
"""
|
272
|
+
def __init__(self, channel: Optional[ConvertChannel] = None):
|
273
|
+
self.channel = channel
|
274
|
+
logger.info("LaTeX 转换服务将在 Bot 连接时异步加载")
|
262
275
|
|
276
|
+
async def load_channel(self, channel: ConvertChannel | None = None) -> None:
|
263
277
|
if channel is None:
|
264
|
-
|
278
|
+
logger.info("正在选择 LaTeX 转换服务频道,请稍等...")
|
279
|
+
self.channel = await self.auto_choose_channel()
|
280
|
+
logger.info(f"已选择 {self.channel.__class__.__name__} 服务频道")
|
265
281
|
else:
|
266
282
|
self.channel = channel
|
267
283
|
|
@@ -276,8 +292,7 @@ class ConvertLatex:
|
|
276
292
|
"""
|
277
293
|
LaTeX 在线渲染
|
278
294
|
|
279
|
-
|
280
|
-
====
|
295
|
+
参数:
|
281
296
|
|
282
297
|
latex: str
|
283
298
|
LaTeX 代码
|
@@ -289,26 +304,37 @@ class ConvertLatex:
|
|
289
304
|
超时时间
|
290
305
|
retry_: int
|
291
306
|
重试次数
|
292
|
-
|
293
|
-
====
|
307
|
+
返回:
|
294
308
|
bytes
|
295
309
|
图片
|
296
310
|
"""
|
297
|
-
|
311
|
+
if self.channel is None:
|
312
|
+
await self.load_channel()
|
313
|
+
|
314
|
+
return await self.channel.get_to_convert( # type: ignore
|
298
315
|
latex, dpi, foreground_colour, timeout_, retry_
|
299
316
|
)
|
300
317
|
|
301
318
|
@staticmethod
|
302
|
-
def auto_choose_channel() -> ConvertChannel:
|
319
|
+
async def auto_choose_channel() -> ConvertChannel:
|
320
|
+
"""
|
321
|
+
依据访问延迟,自动选择 LaTeX 转换服务频道
|
303
322
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
323
|
+
返回
|
324
|
+
====
|
325
|
+
ConvertChannel
|
326
|
+
LaTeX 转换服务实例
|
327
|
+
"""
|
308
328
|
|
329
|
+
async def channel_test_wrapper(
|
330
|
+
channel: type[ConvertChannel],
|
331
|
+
) -> Tuple[int, type[ConvertChannel]]:
|
332
|
+
score = await channel.channel_test()
|
333
|
+
return score, channel
|
309
334
|
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
335
|
+
return min(
|
336
|
+
await asyncio.gather(
|
337
|
+
*(channel_test_wrapper(channel) for channel in CHANNEL_LIST)
|
338
|
+
),
|
339
|
+
key=lambda x: x[0],
|
340
|
+
)[1]()
|
nonebot_plugin_latex/main.py
CHANGED
@@ -14,8 +14,8 @@ See the Mulan PSL v2 for more details.
|
|
14
14
|
"""
|
15
15
|
|
16
16
|
import nonebot
|
17
|
-
from nonebot.adapters.onebot.v11 import MessageEvent
|
18
17
|
|
18
|
+
from nonebot.adapters.onebot.v11 import MessageEvent
|
19
19
|
|
20
20
|
# from nonebot.matcher import Matcher
|
21
21
|
|
@@ -29,7 +29,7 @@ from nonebot_plugin_alconna import (
|
|
29
29
|
)
|
30
30
|
|
31
31
|
from .data import LATEX_PATTERN
|
32
|
-
from .converter import
|
32
|
+
from .converter import _converter
|
33
33
|
|
34
34
|
command_heads = (
|
35
35
|
"latex",
|
@@ -69,6 +69,7 @@ async def check_for_scan(
|
|
69
69
|
# print("判断:这不是指令")
|
70
70
|
return False
|
71
71
|
return False
|
72
|
+
return False
|
72
73
|
|
73
74
|
|
74
75
|
latexg = nonebot.on_message(
|
@@ -103,7 +104,7 @@ async def handle_pic(
|
|
103
104
|
|
104
105
|
for tex_macher in latexes:
|
105
106
|
tex = tex_macher.group().replace("$", "")
|
106
|
-
if (result := await
|
107
|
+
if (result := await _converter.generate_png(tex))[0]:
|
107
108
|
result_msg.append(
|
108
109
|
Alconna_Image(raw=result[1], mimetype="image/png", name="latex.png") # type: ignore
|
109
110
|
)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: nonebot-plugin-latex
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.3
|
4
4
|
Summary: 通过互联网公共服务渲染LaTeX公式
|
5
5
|
Author-email: Eilles <EillesWan@outlook.com>
|
6
6
|
License: 木兰宽松许可证, 第2版
|
@@ -136,14 +136,10 @@ Project-URL: Bug Tracker, https://github.com/EillesWan/nonebot-plugin-latex/issu
|
|
136
136
|
Requires-Python: <4.0,>=3.9
|
137
137
|
Description-Content-Type: text/markdown
|
138
138
|
License-File: LICENSE
|
139
|
-
Requires-Dist:
|
140
|
-
Requires-Dist:
|
141
|
-
Requires-Dist:
|
142
|
-
Requires-Dist:
|
143
|
-
Requires-Dist: Pygments>=2.10.0
|
144
|
-
Requires-Dist: python-markdown-math>=0.8
|
145
|
-
Requires-Dist: pymdown-extensions>=9.1
|
146
|
-
Requires-Dist: aiofiles>=0.8.0
|
139
|
+
Requires-Dist: nonebot2
|
140
|
+
Requires-Dist: httpx<0.28.0,>=0.27.0
|
141
|
+
Requires-Dist: nonebot-adapter-onebot
|
142
|
+
Requires-Dist: nonebot-plugin-alconna
|
147
143
|
|
148
144
|
# nonebot-plugin-latex
|
149
145
|
|
@@ -168,3 +164,8 @@ latex_enable_as_application = true
|
|
168
164
|
```
|
169
165
|
|
170
166
|
这样就可以使用 `latex` 命令进行渲染了,例如 `latex $E=mc^2$` 就会返回这个方程式的渲染图片。
|
167
|
+
|
168
|
+
## 提交
|
169
|
+
|
170
|
+
各位可以搭建自己的 LaTeX 在线渲染服务,或者直接本地渲染,如果提交到本仓库,在下不胜感激。\
|
171
|
+
对于本地渲染服务,请不要使用 nonebot-plugin-htmlrender
|
@@ -0,0 +1,10 @@
|
|
1
|
+
nonebot_plugin_latex/__init__.py,sha256=fIXI5w5E0KYMTqIVYWAMoogjzMHLE9V2wEFzNYUY40A,1476
|
2
|
+
nonebot_plugin_latex/config.py,sha256=5WOJF-vsuLqGTDGgklpMm-xLd_qdwVb0VCq-n8EwoJQ,198
|
3
|
+
nonebot_plugin_latex/converter.py,sha256=rTyeJoZOhXciLcfZ6FWOe0mHczmd2E_sTrqEbJRZ6d8,194
|
4
|
+
nonebot_plugin_latex/data.py,sha256=ygcJClUUM4lNR9PotPC1E0rtrRq1swanPWIBnwAvcWM,11443
|
5
|
+
nonebot_plugin_latex/main.py,sha256=mMbng0Eo1J3gnztTKbJe_PH63qMgJFZ6Ooj4EY8QKx4,3538
|
6
|
+
nonebot_plugin_latex-0.0.3.dist-info/LICENSE,sha256=ISc-fUbtRp39lxd4MpdVr2Saz7XF2yik0RTSRNuhlaM,9375
|
7
|
+
nonebot_plugin_latex-0.0.3.dist-info/METADATA,sha256=OWrGenR2OJ2mSyU-C2-kNAWt4XCaU_YZGEdBfE1QX2k,11737
|
8
|
+
nonebot_plugin_latex-0.0.3.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
9
|
+
nonebot_plugin_latex-0.0.3.dist-info/top_level.txt,sha256=AEtxXrscUdkhTvgg--hAE9WRsW0QVttzK2H-fI9xbGs,21
|
10
|
+
nonebot_plugin_latex-0.0.3.dist-info/RECORD,,
|
@@ -1,10 +0,0 @@
|
|
1
|
-
nonebot_plugin_latex/__init__.py,sha256=sX4NN2jsnbkd4RqLiT0INoRAi0vGgxuSmh40U-6iCZ8,1267
|
2
|
-
nonebot_plugin_latex/config.py,sha256=5WOJF-vsuLqGTDGgklpMm-xLd_qdwVb0VCq-n8EwoJQ,198
|
3
|
-
nonebot_plugin_latex/converter.py,sha256=08RmFCBrUda8PlnqazX71aHjntBzlSOJc_ZeUHfMQ_M,89
|
4
|
-
nonebot_plugin_latex/data.py,sha256=S_KjGkOBGdJMUeKYRfhJ8yllJgbhg1XjPXyypJQ3CFA,10427
|
5
|
-
nonebot_plugin_latex/main.py,sha256=uBN1bQm-i55RimDfwpOPMOx3bkCuPe-vli2NTzuoXRg,3518
|
6
|
-
nonebot_plugin_latex-0.0.2.2.dist-info/LICENSE,sha256=ISc-fUbtRp39lxd4MpdVr2Saz7XF2yik0RTSRNuhlaM,9375
|
7
|
-
nonebot_plugin_latex-0.0.2.2.dist-info/METADATA,sha256=SGjgpPfQFtnqKJ0RU-trNf_18pBT4-kZr0Xygqm10P4,11656
|
8
|
-
nonebot_plugin_latex-0.0.2.2.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
9
|
-
nonebot_plugin_latex-0.0.2.2.dist-info/top_level.txt,sha256=AEtxXrscUdkhTvgg--hAE9WRsW0QVttzK2H-fI9xbGs,21
|
10
|
-
nonebot_plugin_latex-0.0.2.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
{nonebot_plugin_latex-0.0.2.2.dist-info → nonebot_plugin_latex-0.0.3.dist-info}/top_level.txt
RENAMED
File without changes
|