nonebot-plugin-quotation 0.1.0__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.
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
from nonebot import require, get_plugin_config
|
|
2
|
+
from nonebot.plugin import PluginMetadata, inherit_supported_adapters
|
|
3
|
+
from nonebot.typing import T_State
|
|
4
|
+
from nonebot.adapters import Event, MessageTemplate, Message, Bot
|
|
5
|
+
from nonebot.params import Arg, Received, Depends
|
|
6
|
+
from nonebot.matcher import Matcher
|
|
7
|
+
from nonebot.message import handle_event
|
|
8
|
+
from pydantic import BaseModel
|
|
9
|
+
from .quotation import (
|
|
10
|
+
init_quotation,
|
|
11
|
+
send_quo,
|
|
12
|
+
return_index,
|
|
13
|
+
save_pic,
|
|
14
|
+
return_symlink,
|
|
15
|
+
del_symlink,
|
|
16
|
+
create_symlink,
|
|
17
|
+
audit,
|
|
18
|
+
save_pic_audit,
|
|
19
|
+
rename_pic,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
from nepattern import AnyString
|
|
23
|
+
from arclet.alconna import Alconna, CommandMeta, Args
|
|
24
|
+
require("nonebot_plugin_alconna")
|
|
25
|
+
from nonebot_plugin_alconna import on_alconna, Match, get_message_id, AlconnaMatch # noqa: E402
|
|
26
|
+
from nonebot_plugin_alconna.uniseg import Image, UniMessage, Reply # noqa: E402
|
|
27
|
+
from nonebot_plugin_alconna.extension import Extension # noqa: E402
|
|
28
|
+
|
|
29
|
+
class Config(BaseModel):
|
|
30
|
+
trusted_user: list = []
|
|
31
|
+
|
|
32
|
+
__version__ = "0.1.0"
|
|
33
|
+
__plugin_meta__ = PluginMetadata(
|
|
34
|
+
name="Quotation",
|
|
35
|
+
description="简单的语录插件",
|
|
36
|
+
usage="",
|
|
37
|
+
type="application",
|
|
38
|
+
homepage="https://github.com/MerCuJerry/nonebot-plugin-batitle",
|
|
39
|
+
config=Config,
|
|
40
|
+
supported_adapters=inherit_supported_adapters("nonebot_plugin_alconna"),
|
|
41
|
+
extra={
|
|
42
|
+
"version": __version__,
|
|
43
|
+
"author": "MerCuJerry <mercujerry@gmail.com>",
|
|
44
|
+
},
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
async def checker(person: Match[str]) -> bool:
|
|
48
|
+
try:
|
|
49
|
+
return_index().index(person.result)
|
|
50
|
+
except ValueError:
|
|
51
|
+
return False
|
|
52
|
+
else:
|
|
53
|
+
return True
|
|
54
|
+
|
|
55
|
+
quote_matcher = on_alconna(
|
|
56
|
+
Alconna("来点", Args["person", AnyString], meta=CommandMeta(description="来一张群友的怪话", compact=True)),
|
|
57
|
+
after_rule=checker,
|
|
58
|
+
priority=2,
|
|
59
|
+
block=True)
|
|
60
|
+
|
|
61
|
+
class TrustedUserPermissionExtension(Extension):
|
|
62
|
+
@property
|
|
63
|
+
def priority(self) -> int:
|
|
64
|
+
return 10
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def id(self) -> str:
|
|
68
|
+
return "TrustedUserPermissionExtension"
|
|
69
|
+
|
|
70
|
+
async def permission_check(self, bot, event, medium) -> bool:
|
|
71
|
+
return event.get_user_id() in bot.config.superusers or event.get_user_id() in get_plugin_config(Config).trusted_user
|
|
72
|
+
|
|
73
|
+
quote_update = on_alconna(
|
|
74
|
+
Alconna("更新语录", meta=CommandMeta(description="刷新怪话缓存", hide=True, hide_shortcut=True)),
|
|
75
|
+
extensions=[TrustedUserPermissionExtension()],
|
|
76
|
+
use_cmd_start=True,
|
|
77
|
+
priority=5,
|
|
78
|
+
block=True)
|
|
79
|
+
|
|
80
|
+
quote_query = on_alconna(
|
|
81
|
+
Alconna("查询语录", meta=CommandMeta(description="查询怪话", hide=True, hide_shortcut=True)),
|
|
82
|
+
extensions=[TrustedUserPermissionExtension()],
|
|
83
|
+
use_cmd_start=True,
|
|
84
|
+
priority=5,
|
|
85
|
+
block=True)
|
|
86
|
+
|
|
87
|
+
quote_add = on_alconna(
|
|
88
|
+
Alconna("添加", Args["person", str], meta=CommandMeta(description="添加一张群友的怪话", compact=True)),
|
|
89
|
+
use_cmd_start=True,
|
|
90
|
+
priority=5,
|
|
91
|
+
block=True)
|
|
92
|
+
|
|
93
|
+
quote_audit = on_alconna(
|
|
94
|
+
Alconna("审查语录", meta=CommandMeta(description="审查怪话", hide=True, hide_shortcut=True)),
|
|
95
|
+
extensions=[TrustedUserPermissionExtension()],
|
|
96
|
+
use_cmd_start=True,
|
|
97
|
+
priority=5,
|
|
98
|
+
block=True)
|
|
99
|
+
|
|
100
|
+
symlink = on_alconna(
|
|
101
|
+
Alconna("查询语录别名", meta=CommandMeta(description="查询语录别名", hide=True, hide_shortcut=True)),
|
|
102
|
+
extensions=[TrustedUserPermissionExtension()],
|
|
103
|
+
use_cmd_start=True,
|
|
104
|
+
priority=5,
|
|
105
|
+
block=True)
|
|
106
|
+
|
|
107
|
+
symlink_create = on_alconna(
|
|
108
|
+
Alconna(
|
|
109
|
+
"添加语录别名",
|
|
110
|
+
Args["name", AnyString]["person", return_index()].separate('>'),
|
|
111
|
+
meta=CommandMeta(description="添加语录别名", hide=True, hide_shortcut=True, compact=True)
|
|
112
|
+
),
|
|
113
|
+
extensions=[TrustedUserPermissionExtension()],
|
|
114
|
+
use_cmd_start=True,
|
|
115
|
+
priority=2,
|
|
116
|
+
block=True)
|
|
117
|
+
|
|
118
|
+
async def checker_symlink(person: Match[str]) -> bool:
|
|
119
|
+
try:
|
|
120
|
+
[k.name for k in return_symlink().keys()].index(person.result)
|
|
121
|
+
except ValueError:
|
|
122
|
+
return False
|
|
123
|
+
else:
|
|
124
|
+
return True
|
|
125
|
+
|
|
126
|
+
symlink_del = on_alconna(
|
|
127
|
+
Alconna(
|
|
128
|
+
"删除语录别名",
|
|
129
|
+
Args["person", AnyString],
|
|
130
|
+
meta=CommandMeta(description="删除语录别名", hide=True, hide_shortcut=True, compact=True)
|
|
131
|
+
),
|
|
132
|
+
after_rule=checker_symlink,
|
|
133
|
+
extensions=[TrustedUserPermissionExtension()],
|
|
134
|
+
use_cmd_start=True,
|
|
135
|
+
priority=2,
|
|
136
|
+
block=True)
|
|
137
|
+
|
|
138
|
+
@quote_matcher.handle()
|
|
139
|
+
async def qmhandler(person : Match[str], state: T_State):
|
|
140
|
+
path = send_quo(person.result)
|
|
141
|
+
state["quotation_last_path"] = path
|
|
142
|
+
await UniMessage([Reply(get_message_id()), Image(raw=path.read_bytes())]).send()
|
|
143
|
+
|
|
144
|
+
async def quote_checker(bot: Bot, event: Event = Received("delete_quote")) -> Event | None:
|
|
145
|
+
if str(event.get_message()) == "删除语录" and (event.get_user_id() in bot.config.superusers or event.get_user_id() in get_plugin_config(Config).trusted_user):
|
|
146
|
+
return event
|
|
147
|
+
else:
|
|
148
|
+
await handle_event(bot, event)
|
|
149
|
+
|
|
150
|
+
@quote_matcher.receive("delete_quote")
|
|
151
|
+
async def quotereceive(matcher: Matcher, state: T_State, event: Event = Depends(quote_checker)):
|
|
152
|
+
try:
|
|
153
|
+
state["quotation_last_path"].unlink()
|
|
154
|
+
await matcher.finish("删除成功")
|
|
155
|
+
except FileNotFoundError:
|
|
156
|
+
await matcher.finish("文件未找到")
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@quote_update.handle()
|
|
160
|
+
async def quoteupdatehandler(matcher: Matcher):
|
|
161
|
+
if init_quotation():
|
|
162
|
+
await matcher.finish("更新完毕")
|
|
163
|
+
else:
|
|
164
|
+
await matcher.finish("更新错误")
|
|
165
|
+
|
|
166
|
+
@quote_query.handle()
|
|
167
|
+
async def queryhandler(matcher: Matcher):
|
|
168
|
+
query_sep = '/'
|
|
169
|
+
query_head = '当前语录列表: \n'
|
|
170
|
+
await matcher.finish(query_head+query_sep.join(return_index()))
|
|
171
|
+
|
|
172
|
+
# quote add
|
|
173
|
+
@quote_add.handle()
|
|
174
|
+
async def addhandler(person : Match[str], state: T_State):
|
|
175
|
+
state["add_path"] = person.result
|
|
176
|
+
|
|
177
|
+
@quote_add.got("arg", prompt=MessageTemplate("请发送要添加至{add_path}的图片"))
|
|
178
|
+
async def addgot(matcher: Matcher, bot: Bot, event: Event, state: T_State, arg: Message = Arg()):
|
|
179
|
+
if "image" not in arg:
|
|
180
|
+
await matcher.finish("添加出错请重新添加 (回复中需要包含至少一张图片)")
|
|
181
|
+
if(event.get_user_id() in bot.config.superusers or event.get_user_id() in get_plugin_config(Config).trusted_user):
|
|
182
|
+
for pic in arg.get("image"):
|
|
183
|
+
image_url = pic.data["url"]
|
|
184
|
+
ok = await save_pic(state["add_path"], image_url) # type: ignore
|
|
185
|
+
if ok:
|
|
186
|
+
await matcher.finish("添加成功")
|
|
187
|
+
else:
|
|
188
|
+
await matcher.finish("添加失败了哦,请重新添加")
|
|
189
|
+
else:
|
|
190
|
+
for pic in arg.get("image"):
|
|
191
|
+
image_url = pic.data["url"]
|
|
192
|
+
ok = await save_pic_audit(state["add_path"], image_url, event.get_user_id()) # type: ignore
|
|
193
|
+
if ok:
|
|
194
|
+
await matcher.finish("添加成功,等待审查")
|
|
195
|
+
else:
|
|
196
|
+
await matcher.finish("你已经有待审查的语录了哦,请等待审查结果")
|
|
197
|
+
|
|
198
|
+
# audit
|
|
199
|
+
@quote_audit.handle()
|
|
200
|
+
async def audithandler(matcher: Matcher, state: T_State):
|
|
201
|
+
audit_path = await audit()
|
|
202
|
+
if audit_path is not None:
|
|
203
|
+
state["audit_path"] = audit_path
|
|
204
|
+
state["audit_path_name"] = audit_path.name.split("@", -1)[0]
|
|
205
|
+
await UniMessage(Image(raw=audit_path.read_bytes())).send()
|
|
206
|
+
else:
|
|
207
|
+
await matcher.finish("没有待审查的语录了哦")
|
|
208
|
+
|
|
209
|
+
@quote_audit.got("arg_audit", prompt=MessageTemplate("该图片添加到{audit_path_name},请输入审查结果,发送“通过”以通过审查,发送“拒绝”以拒绝审查"))
|
|
210
|
+
async def auditgot(matcher: Matcher, state: T_State, arg_audit: Message = Arg()):
|
|
211
|
+
if not state["audit_path"].is_file():
|
|
212
|
+
await matcher.finish("需要审查的语录已经被审查")
|
|
213
|
+
if str(arg_audit) == "通过":
|
|
214
|
+
ok = await rename_pic(state["audit_path"])
|
|
215
|
+
if ok:
|
|
216
|
+
await matcher.finish("审查通过,已添加至语录")
|
|
217
|
+
else:
|
|
218
|
+
await matcher.finish("审查通过,但添加至语录失败了")
|
|
219
|
+
elif str(arg_audit) == "拒绝":
|
|
220
|
+
state["audit_path"].unlink()
|
|
221
|
+
await matcher.finish("审查拒绝,已删除待审查语录")
|
|
222
|
+
else:
|
|
223
|
+
await matcher.finish("输入有误,请重新输入")
|
|
224
|
+
|
|
225
|
+
# symlink
|
|
226
|
+
@symlink.handle()
|
|
227
|
+
async def symlinkhandler(matcher: Matcher):
|
|
228
|
+
formatted_str = "\n".join(f"{key.name} --> {value.name}" for key, value in return_symlink().items())
|
|
229
|
+
await matcher.finish(formatted_str)
|
|
230
|
+
|
|
231
|
+
@symlink_create.handle()
|
|
232
|
+
async def symlinkChandler(matcher: Matcher, name: Match[str] = AlconnaMatch("name"), person: Match[str] = AlconnaMatch("person")):
|
|
233
|
+
try:
|
|
234
|
+
create_symlink(name.result, person.result)
|
|
235
|
+
await matcher.send("添加成功")
|
|
236
|
+
except AssertionError:
|
|
237
|
+
await matcher.send("存在这样的语录别名或语录")
|
|
238
|
+
except TypeError:
|
|
239
|
+
await matcher.send("请检查命令格式!")
|
|
240
|
+
except Exception:
|
|
241
|
+
await matcher.send("出错了...怎么回事呢?")
|
|
242
|
+
finally:
|
|
243
|
+
await matcher.finish()
|
|
244
|
+
|
|
245
|
+
@symlink_del.handle()
|
|
246
|
+
async def symlinkDhandler(matcher: Matcher, person: Match[str]):
|
|
247
|
+
try:
|
|
248
|
+
del_symlink(person.result)
|
|
249
|
+
await matcher.send("删除成功")
|
|
250
|
+
except AssertionError:
|
|
251
|
+
await matcher.send("没有那样的别名哦")
|
|
252
|
+
except Exception:
|
|
253
|
+
await matcher.send("出错了...怎么回事呢?")
|
|
254
|
+
finally:
|
|
255
|
+
await matcher.finish()
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
from nonebot import logger, require
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from json import JSONEncoder, JSONDecoder
|
|
4
|
+
import random
|
|
5
|
+
from typing import Optional, List, Dict
|
|
6
|
+
from httpx import AsyncClient
|
|
7
|
+
import ssl
|
|
8
|
+
import hashlib
|
|
9
|
+
require("nonebot_plugin_localstore")
|
|
10
|
+
import nonebot_plugin_localstore as store # noqa: E402
|
|
11
|
+
|
|
12
|
+
QUO_PATH = store.get_plugin_data_dir()
|
|
13
|
+
|
|
14
|
+
def init_quotation() -> bool:
|
|
15
|
+
try:
|
|
16
|
+
for QUO_DIR in QUO_PATH.iterdir():
|
|
17
|
+
if not QUO_DIR.is_symlink():
|
|
18
|
+
store.get_plugin_cache_file(str(QUO_DIR.name + ".json")).write_text(
|
|
19
|
+
JSONEncoder().encode( [key.name for key in QUO_DIR.iterdir()] ),
|
|
20
|
+
encoding="u8",
|
|
21
|
+
)
|
|
22
|
+
return True
|
|
23
|
+
except IOError:
|
|
24
|
+
return False
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def send_quo(args: str) -> Path:
|
|
28
|
+
PIC_DIR = QUO_PATH / args
|
|
29
|
+
ENSURE_THIS_PATH = store.get_plugin_cache_file(str(args + ".json")) if not PIC_DIR.is_symlink() else store.get_plugin_cache_file(str(PIC_DIR.readlink().name + ".json"))
|
|
30
|
+
ensure_pic_use: List[str] = JSONDecoder().decode(ENSURE_THIS_PATH.read_text(encoding="u8"))
|
|
31
|
+
try:
|
|
32
|
+
randompic = random.choice(ensure_pic_use)
|
|
33
|
+
except IndexError:
|
|
34
|
+
ensure_pic_use = [key.name for key in PIC_DIR.iterdir()]
|
|
35
|
+
randompic = random.choice(ensure_pic_use)
|
|
36
|
+
PIC_PATH : Path = PIC_DIR / randompic
|
|
37
|
+
randompictmp : str = randompic.replace(' ','').replace('-','').replace('_','')
|
|
38
|
+
if not randompictmp.__eq__(randompic):
|
|
39
|
+
PIC_PATH_NEW = PIC_DIR / randompictmp
|
|
40
|
+
PIC_PATH = PIC_PATH.rename(PIC_PATH_NEW)
|
|
41
|
+
ensure_pic_use.remove(randompic)
|
|
42
|
+
ENSURE_THIS_PATH.write_text(JSONEncoder().encode(ensure_pic_use), encoding="u8")
|
|
43
|
+
return PIC_PATH
|
|
44
|
+
|
|
45
|
+
def return_index() -> List[str]:
|
|
46
|
+
try:
|
|
47
|
+
list_return = [key.name for key in QUO_PATH.iterdir()]
|
|
48
|
+
return list_return
|
|
49
|
+
except ValueError as e:
|
|
50
|
+
raise e from e
|
|
51
|
+
|
|
52
|
+
async def save_pic(arg: str, url: str) -> bool:
|
|
53
|
+
try:
|
|
54
|
+
assert arg != "" and arg is not None
|
|
55
|
+
PIC_DIR = QUO_PATH / arg
|
|
56
|
+
if not PIC_DIR.exists():
|
|
57
|
+
PIC_DIR.mkdir(parents=True)
|
|
58
|
+
ctx = ssl.create_default_context()
|
|
59
|
+
ctx.set_ciphers('@SECLEVEL=2:ECDH+AESGCM:ECDH+CHACHA20:ECDH+AES:DHE+AES:AESGCM:!aNULL:!eNULL:!aDSS:!SHA1:!AESCCM:!PSK')
|
|
60
|
+
async with AsyncClient(verify=ctx) as client:
|
|
61
|
+
image_bytes = await client.get(url)
|
|
62
|
+
md5 = hashlib.md5()
|
|
63
|
+
md5.update(image_bytes.content)
|
|
64
|
+
PIC_PATH = PIC_DIR / str(md5.hexdigest() + ".jpg")
|
|
65
|
+
PIC_PATH.write_bytes(image_bytes.content)
|
|
66
|
+
return True
|
|
67
|
+
except Exception as e:
|
|
68
|
+
logger.warning(e)
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
async def save_pic_audit(arg: str, url: str, sender: str) -> bool:
|
|
72
|
+
try:
|
|
73
|
+
assert arg != "" and arg is not None
|
|
74
|
+
list_return = [key.name for key in (store.get_plugin_cache_dir()).glob("*@" + sender + "@*")]
|
|
75
|
+
if len(list_return) != 0:
|
|
76
|
+
return False
|
|
77
|
+
ctx = ssl.create_default_context()
|
|
78
|
+
ctx.set_ciphers('@SECLEVEL=2:ECDH+AESGCM:ECDH+CHACHA20:ECDH+AES:DHE+AES:AESGCM:!aNULL:!eNULL:!aDSS:!SHA1:!AESCCM:!PSK')
|
|
79
|
+
async with AsyncClient(verify=ctx) as client:
|
|
80
|
+
image_bytes = await client.get(url)
|
|
81
|
+
md5 = hashlib.md5()
|
|
82
|
+
md5.update(image_bytes.content)
|
|
83
|
+
store.get_plugin_cache_file(
|
|
84
|
+
str(arg + "@" + sender + "@" + md5.hexdigest() + ".jpg")
|
|
85
|
+
).write_bytes(image_bytes.content)
|
|
86
|
+
return True
|
|
87
|
+
except Exception as e:
|
|
88
|
+
logger.warning(e)
|
|
89
|
+
return False
|
|
90
|
+
|
|
91
|
+
async def rename_pic(arg: Path) -> bool:
|
|
92
|
+
try:
|
|
93
|
+
assert arg is not None
|
|
94
|
+
PIC_DIR = QUO_PATH / arg.name.split("@", -1)[0]
|
|
95
|
+
if not PIC_DIR.exists():
|
|
96
|
+
PIC_DIR.mkdir(parents=True)
|
|
97
|
+
NEW_PATH = PIC_DIR / arg.name.split("@", -1)[2]
|
|
98
|
+
arg.rename(NEW_PATH)
|
|
99
|
+
return True
|
|
100
|
+
except Exception as e:
|
|
101
|
+
logger.warning(e)
|
|
102
|
+
return False
|
|
103
|
+
|
|
104
|
+
async def return_audit() -> List[Path]:
|
|
105
|
+
try:
|
|
106
|
+
list_return = [key for key in (store.get_plugin_cache_dir()).iterdir()]
|
|
107
|
+
return list_return
|
|
108
|
+
except Exception as e:
|
|
109
|
+
logger.warning(e)
|
|
110
|
+
raise e from BaseException("Quotation Plugin: return_audit error")
|
|
111
|
+
|
|
112
|
+
async def audit() -> Optional[Path]:
|
|
113
|
+
try:
|
|
114
|
+
list_return = await return_audit()
|
|
115
|
+
if len(list_return) == 0:
|
|
116
|
+
return None
|
|
117
|
+
randompic = random.choice(list_return)
|
|
118
|
+
return randompic
|
|
119
|
+
except Exception as e:
|
|
120
|
+
logger.warning(e)
|
|
121
|
+
raise e from BaseException("Quotation Plugin: audit error")
|
|
122
|
+
|
|
123
|
+
def return_symlink() -> Dict[Path, Path]:
|
|
124
|
+
return {dir : dir.readlink() for dir in QUO_PATH.iterdir() if dir.is_symlink()}
|
|
125
|
+
|
|
126
|
+
def create_symlink(dir: str, target: str):
|
|
127
|
+
SYMLINK_PATH = QUO_PATH / dir
|
|
128
|
+
assert not SYMLINK_PATH.exists()
|
|
129
|
+
TGT_PATH = QUO_PATH / target
|
|
130
|
+
SYMLINK_PATH.symlink_to(TGT_PATH.name, True)
|
|
131
|
+
|
|
132
|
+
def del_symlink(arg: str):
|
|
133
|
+
SYMLINK_PATH = QUO_PATH / arg
|
|
134
|
+
assert SYMLINK_PATH.exists() and SYMLINK_PATH.is_symlink()
|
|
135
|
+
SYMLINK_PATH.unlink()
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: nonebot-plugin-quotation
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Simple quotation plugin
|
|
5
|
+
Keywords: nonebot,nonebot2,qqbot,bot
|
|
6
|
+
Author: MerCuJerry
|
|
7
|
+
Author-email: MerCuJerry <mercujerry@gmail.com>
|
|
8
|
+
License: MIT License
|
|
9
|
+
|
|
10
|
+
Copyright (c) 2026 MerCuJerry
|
|
11
|
+
|
|
12
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
17
|
+
Requires-Dist: nonebot2[fastapi,httpx]>=2.5.0
|
|
18
|
+
Requires-Dist: nonebot-adapter-onebot>=2.4.6
|
|
19
|
+
Requires-Dist: pydantic>=2.0,!=2.5.0,!=2.5.1,<3.0.0
|
|
20
|
+
Requires-Dist: nonebot-plugin-localstore>=0.7.4
|
|
21
|
+
Requires-Dist: nonebot-plugin-alconna>=0.62.0
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Project-URL: homepage, https://github.com/MerCuJerry/nonebot-plugin-quotation
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
nonebot_plugin_quotation/__init__.py,sha256=g6lABwHUwlGE8LyGiCrxJwEqJlar77qhbm4NvJNBGHk,9521
|
|
2
|
+
nonebot_plugin_quotation/quotation.py,sha256=7YnjKAd4-d6NQT60FvaPN4BACcWVI-3w_GFunaKuUVM,5166
|
|
3
|
+
nonebot_plugin_quotation-0.1.0.dist-info/WHEEL,sha256=s_zqWxHFEH8b58BCtf46hFCqPaISurdB9R1XJ8za6XI,80
|
|
4
|
+
nonebot_plugin_quotation-0.1.0.dist-info/METADATA,sha256=siN86qizLJ7f8o6k_w5E0qEWZbYzNiYl77-WexOYXZY,1733
|
|
5
|
+
nonebot_plugin_quotation-0.1.0.dist-info/RECORD,,
|