nonebot-plugin-pErithacus 1.4.2__tar.gz → 1.4.4__tar.gz

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 (23) hide show
  1. {nonebot_plugin_perithacus-1.4.2 → nonebot_plugin_perithacus-1.4.4}/PKG-INFO +3 -3
  2. {nonebot_plugin_perithacus-1.4.2 → nonebot_plugin_perithacus-1.4.4}/pyproject.toml +5 -5
  3. {nonebot_plugin_perithacus-1.4.2 → nonebot_plugin_perithacus-1.4.4}/src/nonebot_plugin_perithacus/__init__.py +1 -1
  4. {nonebot_plugin_perithacus-1.4.2 → nonebot_plugin_perithacus-1.4.4}/src/nonebot_plugin_perithacus/cmd_add.py +5 -5
  5. {nonebot_plugin_perithacus-1.4.2 → nonebot_plugin_perithacus-1.4.4}/src/nonebot_plugin_perithacus/cmd_check.py +1 -1
  6. {nonebot_plugin_perithacus-1.4.2 → nonebot_plugin_perithacus-1.4.4}/src/nonebot_plugin_perithacus/cmd_delete.py +1 -1
  7. {nonebot_plugin_perithacus-1.4.2 → nonebot_plugin_perithacus-1.4.4}/src/nonebot_plugin_perithacus/cmd_detail.py +1 -1
  8. {nonebot_plugin_perithacus-1.4.2 → nonebot_plugin_perithacus-1.4.4}/src/nonebot_plugin_perithacus/cmd_edit.py +1 -1
  9. {nonebot_plugin_perithacus-1.4.2 → nonebot_plugin_perithacus-1.4.4}/src/nonebot_plugin_perithacus/cmd_list.py +1 -1
  10. {nonebot_plugin_perithacus-1.4.2 → nonebot_plugin_perithacus-1.4.4}/src/nonebot_plugin_perithacus/cmd_search.py +15 -21
  11. {nonebot_plugin_perithacus-1.4.2 → nonebot_plugin_perithacus-1.4.4}/src/nonebot_plugin_perithacus/database.py +7 -2
  12. {nonebot_plugin_perithacus-1.4.2 → nonebot_plugin_perithacus-1.4.4}/src/nonebot_plugin_perithacus/lib.py +13 -12
  13. {nonebot_plugin_perithacus-1.4.2 → nonebot_plugin_perithacus-1.4.4}/src/nonebot_plugin_perithacus/trigger.py +1 -1
  14. {nonebot_plugin_perithacus-1.4.2 → nonebot_plugin_perithacus-1.4.4}/README.md +0 -0
  15. {nonebot_plugin_perithacus-1.4.2 → nonebot_plugin_perithacus-1.4.4}/src/nonebot_plugin_perithacus/apscheduler.py +0 -0
  16. {nonebot_plugin_perithacus-1.4.2 → nonebot_plugin_perithacus-1.4.4}/src/nonebot_plugin_perithacus/command.py +0 -0
  17. {nonebot_plugin_perithacus-1.4.2 → nonebot_plugin_perithacus-1.4.4}/src/nonebot_plugin_perithacus/handle_args.py +0 -0
  18. {nonebot_plugin_perithacus-1.4.2 → nonebot_plugin_perithacus-1.4.4}/src/nonebot_plugin_perithacus/migrations/4184a6a150e9_.py +0 -0
  19. {nonebot_plugin_perithacus-1.4.2 → nonebot_plugin_perithacus-1.4.4}/src/nonebot_plugin_perithacus/migrations/59488f45afb7_.py +0 -0
  20. {nonebot_plugin_perithacus-1.4.2 → nonebot_plugin_perithacus-1.4.4}/src/nonebot_plugin_perithacus/migrations/bd67704f77ad_.py +0 -0
  21. {nonebot_plugin_perithacus-1.4.2 → nonebot_plugin_perithacus-1.4.4}/src/nonebot_plugin_perithacus/migrations/c2cbe27dd921_.py +0 -0
  22. {nonebot_plugin_perithacus-1.4.2 → nonebot_plugin_perithacus-1.4.4}/src/nonebot_plugin_perithacus/migrations/cc658325e0ad_.py +0 -0
  23. {nonebot_plugin_perithacus-1.4.2 → nonebot_plugin_perithacus-1.4.4}/src/nonebot_plugin_perithacus/upgrade_database.py +0 -0
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nonebot-plugin-pErithacus
3
- Version: 1.4.2
3
+ Version: 1.4.4
4
4
  Summary: pErithacus 是一个基于 NoneBot2 框架的聊天插件,可以根据用户设定的关键词自动回复相关内容。该插件提供了完整的词条管理功能,让用户能够轻松创建、编辑和管理自定义回复内容
5
5
  Keywords: nonebot2,chatbot,qqbot,nonebot-plugin
6
6
  Author: SnowMoonSS
7
7
  License-Expression: MIT
8
8
  Classifier: Programming Language :: Python :: 3
9
- Classifier: Programming Language :: Python :: 3.14
9
+ Classifier: Programming Language :: Python :: 3.12
10
10
  Classifier: Operating System :: OS Independent
11
11
  Classifier: Framework :: Robot Framework
12
12
  Requires-Dist: httpx>=0.24.0
@@ -18,7 +18,7 @@ Requires-Dist: nonebot-plugin-orm[default]>=0.8.2
18
18
  Requires-Dist: nonebot-plugin-apscheduler>=0.5.0
19
19
  Requires-Dist: pyright[nodejs] ; extra == 'dev'
20
20
  Requires-Dist: ruff ; extra == 'dev'
21
- Requires-Python: >=3.12, <4.0
21
+ Requires-Python: >=3.11, <4.0
22
22
  Project-URL: Homepage, https://github.com/SnowMoonSS/nonebot-plugin-pErithacus
23
23
  Project-URL: Repository, https://github.com/SnowMoonSS/nonebot-plugin-pErithacus.git
24
24
  Project-URL: Documentation, https://github.com/SnowMoonSS/nonebot-plugin-pErithacus/blob/master/README.md
@@ -1,9 +1,9 @@
1
1
  [project]
2
2
  name = "nonebot-plugin-pErithacus"
3
- version = "1.4.2"
3
+ version = "1.4.4"
4
4
  description = "pErithacus 是一个基于 NoneBot2 框架的聊天插件,可以根据用户设定的关键词自动回复相关内容。该插件提供了完整的词条管理功能,让用户能够轻松创建、编辑和管理自定义回复内容"
5
5
  readme = "README.md"
6
- requires-python = ">=3.12, <4.0"
6
+ requires-python = ">=3.11, <4.0"
7
7
  dependencies = [
8
8
  "httpx>=0.24.0",
9
9
  "filetype>=1.2.0",
@@ -17,7 +17,7 @@ authors = [{ name = "SnowMoonSS" }]
17
17
  license = "MIT"
18
18
  classifiers = [
19
19
  "Programming Language :: Python :: 3",
20
- "Programming Language :: Python :: 3.14",
20
+ "Programming Language :: Python :: 3.12",
21
21
  "Operating System :: OS Independent",
22
22
  "Framework :: Robot Framework"
23
23
  ]
@@ -58,7 +58,7 @@ nonebot-plugin-apscheduler = ["nonebot_plugin_apscheduler"]
58
58
 
59
59
  [tool.ruff]
60
60
  line-length = 128
61
- target-version = "py314"
61
+ target-version = "py312"
62
62
 
63
63
  [tool.ruff.format]
64
64
  line-ending = "lf"
@@ -126,6 +126,6 @@ keep-runtime-typing = true
126
126
 
127
127
  [tool.pyright]
128
128
  # For Pylance/Pyright configurations, see https://microsoft.github.io/pyright/#/configuration.
129
- pythonVersion = "3.14"
129
+ pythonVersion = "3.12"
130
130
  pythonPlatform = "All"
131
131
  typeCheckingMode = "standard"
@@ -26,7 +26,7 @@ from .upgrade_database import upgrade_content_db
26
26
 
27
27
  driver = get_driver()
28
28
 
29
- __version__ = "1.4.2"
29
+ __version__ = "1.4.4"
30
30
  __plugin_meta__ = PluginMetadata(
31
31
  name="pErithacus",
32
32
  description=("pErithacus 是一个基于 NoneBot2 框架的聊天插件,"
@@ -8,7 +8,7 @@ from nonebot_plugin_alconna import (
8
8
  UniMessage,
9
9
  UniMsg,
10
10
  )
11
- from nonebot_plugin_orm import async_scoped_session # noqa: TC002
11
+ from nonebot_plugin_orm import async_scoped_session
12
12
 
13
13
  from .apscheduler import add_cron_job
14
14
  from .command import pe
@@ -27,7 +27,7 @@ from .handle_args import (
27
27
  handle_reg,
28
28
  handle_scope,
29
29
  )
30
- from .lib import dump_msg, get_cron, get_scope, get_source, load_media
30
+ from .lib import dump_msg, get_cron, get_scope, get_source
31
31
 
32
32
 
33
33
  @pe.assign("add")
@@ -91,13 +91,13 @@ async def _( # noqa: PLR0913
91
91
  entry_id = existing_entry.id
92
92
  await session.commit()
93
93
 
94
- uni_keyword = load_media(keyword_text)
94
+ uni_keyword = UniMessage.load(keyword_text)
95
95
  await pe.finish(
96
96
  f"词条 {entry_id} : " + uni_keyword + " 加入了新的内容:"
97
97
  f"{add_content_result.content_id}"
98
98
  )
99
99
  else:
100
- uni_keyword = load_media(keyword_text)
100
+ uni_keyword = UniMessage.load(keyword_text)
101
101
  await pe.finish(
102
102
  f"词条 {existing_entry.id} : " + uni_keyword + " 已存在该内容:"
103
103
  f"{add_content_result.content_id}",
@@ -128,7 +128,7 @@ async def _( # noqa: PLR0913
128
128
  entry_id = new_entry.id
129
129
  await session.commit()
130
130
 
131
- uni_keyword = load_media(keyword_text)
131
+ uni_keyword = UniMessage.load(keyword_text)
132
132
  await pe.finish(
133
133
  f"词条 {entry_id} : " + uni_keyword + " 已创建并加入了新的内容:"
134
134
  f"{add_content_result.content_id}"
@@ -2,7 +2,7 @@ import json
2
2
  from datetime import UTC, timedelta, timezone
3
3
 
4
4
  from nonebot_plugin_alconna import AlconnaMatch, AlconnaQuery, Match, Query, UniMessage
5
- from nonebot_plugin_orm import async_scoped_session # noqa: TC002
5
+ from nonebot_plugin_orm import async_scoped_session
6
6
 
7
7
  from .command import pe
8
8
  from .database import get_entry_by_id
@@ -2,7 +2,7 @@ import json
2
2
  from datetime import UTC, datetime
3
3
 
4
4
  from nonebot_plugin_alconna import AlconnaMatch, Match, MsgTarget, UniMsg
5
- from nonebot_plugin_orm import async_scoped_session # noqa: TC002
5
+ from nonebot_plugin_orm import async_scoped_session
6
6
 
7
7
  from .apscheduler import remove_cron_job
8
8
  from .command import pe
@@ -1,7 +1,7 @@
1
1
  from datetime import UTC, timedelta, timezone
2
2
 
3
3
  from nonebot_plugin_alconna import AlconnaMatch, AlconnaQuery, Match, Query, UniMessage
4
- from nonebot_plugin_orm import async_scoped_session # noqa: TC002
4
+ from nonebot_plugin_orm import async_scoped_session
5
5
 
6
6
  from .command import pe
7
7
  from .database import get_contents, get_entry_by_id
@@ -1,6 +1,6 @@
1
1
  from nonebot import logger
2
2
  from nonebot_plugin_alconna import AlconnaMatch, Match, MsgTarget, UniMessage, UniMsg
3
- from nonebot_plugin_orm import async_scoped_session # noqa: TC002
3
+ from nonebot_plugin_orm import async_scoped_session
4
4
 
5
5
  from .command import pe
6
6
  from .database import (
@@ -7,7 +7,7 @@ from nonebot_plugin_alconna import (
7
7
  Query,
8
8
  UniMessage,
9
9
  )
10
- from nonebot_plugin_orm import async_scoped_session # noqa: TC002
10
+ from nonebot_plugin_orm import async_scoped_session
11
11
 
12
12
  from .command import pe
13
13
  from .database import get_entries
@@ -12,11 +12,12 @@ from nonebot_plugin_alconna import (
12
12
  Query,
13
13
  UniMessage,
14
14
  )
15
+ from nonebot_plugin_alconna.uniseg.segment import At, Media
15
16
  from nonebot_plugin_orm import async_scoped_session # noqa: TC002
16
17
 
17
18
  from .command import pe
18
19
  from .database import Index, get_all_contents, get_entries, get_entry_by_id
19
- from .lib import dump_msg, get_scope, get_source, load_msg
20
+ from .lib import get_scope, get_source, load_msg, pe_download
20
21
 
21
22
  if TYPE_CHECKING:
22
23
  from collections.abc import Sequence
@@ -82,19 +83,17 @@ async def _( # noqa: PLR0913
82
83
  await pe.finish(UniMessage("搜索结果:\n无"))
83
84
 
84
85
  async def get_key(keyword: Match) -> str:
85
- keyword_text = await dump_msg(keyword.result, media_save_dir=False)
86
- pe_message_list = json.loads(keyword_text)
86
+ msg = UniMessage(keyword.result) if not isinstance(keyword.result, UniMessage) else keyword.result
87
+ msg = await pe_download(msg)
87
88
 
88
89
  # 检查pe_message_list中的每个元素
89
90
  key = None
90
- for item in pe_message_list:
91
- if not isinstance(item, dict):
92
- continue
93
- if item.get("media"):
94
- key = item["id"]
91
+ for seg in msg:
92
+ if isinstance(seg, Media):
93
+ key = seg.id
95
94
  break
96
- if item.get("type") == "at":
97
- key = item["target"]
95
+ if isinstance(seg, At):
96
+ key = seg.target
98
97
  break
99
98
  if not key:
100
99
  key = UniMessage(keyword.result).extract_plain_text()
@@ -110,9 +109,7 @@ async def search_in_entries(
110
109
  # 检查keyword列和alias列包含key的行
111
110
  if key in entry.keyword or (entry.alias and key in entry.alias):
112
111
  result_list.append(entry.id)
113
- logger.info(
114
- f"在 Index 中找到匹配的词条 {entry.id},关键词 {entry.keyword}"
115
- )
112
+ logger.info(f"在 Index 中找到匹配的词条 {entry.id},关键词 {entry.keyword}")
116
113
  return result_list
117
114
 
118
115
  async def search_in_contents(
@@ -124,14 +121,11 @@ async def search_in_contents(
124
121
  ) -> list[int]:
125
122
  contents = await get_all_contents(session)
126
123
  for content in contents:
127
- if not content.deleted and key in content.content:
128
- if content.entry_id not in result_list:
129
- if not await check_is_deleted(session, content.entry_id, scope_list):
130
- result_list.append(content.entry_id)
131
- else:
132
- logger.debug(f"跳过词条 {content.entry_id},该词条已存在搜索结果中")
133
- else:
134
- logger.debug(f"未在 {content.entry_id} 中找到包含 {key} 的内容")
124
+ if ((not content.deleted and key in content.content)
125
+ and (content.entry_id not in result_list)
126
+ and (not await check_is_deleted(session, content.entry_id, scope_list))):
127
+ result_list.append(content.entry_id)
128
+ logger.debug(f"在 {content.entry_id} 中找到包含 {key} 的内容")
135
129
 
136
130
  return result_list
137
131
 
@@ -151,6 +151,8 @@ def _is_fuzzy_match(entry: Index, keyword_msg: UniMessage) -> bool:
151
151
  return False
152
152
 
153
153
  key = keyword_msg.extract_plain_text()
154
+ if not key: # 空字符串不应匹配任何词条
155
+ return False
154
156
  entry_key = load_media(entry.keyword).extract_plain_text()
155
157
  if key in entry_key:
156
158
  return True
@@ -174,6 +176,8 @@ def _is_regex_match(entry: Index, keyword_msg: UniMessage) -> bool:
174
176
  return False
175
177
 
176
178
  key = keyword_msg.extract_plain_text()
179
+ if not key: # 空字符串不应匹配任何词条
180
+ return False
177
181
  return bool(re.match(entry.reg, key))
178
182
 
179
183
  async def matching(
@@ -320,7 +324,8 @@ async def add_entry(
320
324
  **kwargs
321
325
  )
322
326
  session.add(new_entry)
323
- logger.debug(f"添加新词条 {new_entry.id} : {new_entry.keyword} 到 Index")
327
+ await session.flush()
328
+ logger.info(f"添加新词条 {new_entry.id}: {new_entry.keyword} 到 Index")
324
329
  return new_entry
325
330
 
326
331
  async def get_entry_by_id(
@@ -535,7 +540,6 @@ async def add_content(
535
540
  await restore_deleted_content(session, row.id)
536
541
  return AddContentResult(success=True, content_id=row.id)
537
542
 
538
- logger.debug(f"添加内容 {content} 到 Content")
539
543
  new_content = Content(
540
544
  entry_id=entry_id,
541
545
  content=content,
@@ -544,6 +548,7 @@ async def add_content(
544
548
  )
545
549
  session.add(new_content)
546
550
  await session.flush()
551
+ logger.info(f"添加内容 {new_content.id}: {content} 到 Content")
547
552
  return AddContentResult(success=True, content_id=new_content.id)
548
553
 
549
554
  async def delete_content(session: async_scoped_session, content_id: int) -> bool:
@@ -10,6 +10,7 @@ from json import dumps
10
10
  from pathlib import Path
11
11
  from typing import Any, Literal, overload
12
12
 
13
+ import anyio
13
14
  import filetype
14
15
  import httpx
15
16
  from apscheduler.triggers.cron import CronTrigger
@@ -37,11 +38,11 @@ async def download_media(
37
38
  :return: 最终文件路径 或 None(失败时)
38
39
  """
39
40
  save_dir = Path(save_dir)
40
- save_dir.mkdir(parents=True, exist_ok=True)
41
+ await anyio.Path(save_dir).mkdir(parents=True, exist_ok=True)
41
42
 
42
43
  # 创建临时文件(在 save_dir 中)
43
44
  with tempfile.NamedTemporaryFile(delete=False, dir=save_dir) as tmp_file:
44
- tmp_path = Path(tmp_file.name)
45
+ tmp_path = anyio.Path(tmp_file.name)
45
46
  md5_hash = hashlib.md5()
46
47
 
47
48
  try:
@@ -59,13 +60,13 @@ async def download_media(
59
60
  os.fsync(tmp_file.fileno())
60
61
  except httpx.HTTPError as e:
61
62
  # 包括连接错误、超时、4xx/5xx 等
62
- tmp_path.unlink(missing_ok=True)
63
+ await tmp_path.unlink(missing_ok=True)
63
64
  logger.error(f"HTTP 请求失败: {e}")
64
65
  return None
65
66
 
66
67
  except OSError as e:
67
68
  # 文件写入、fsync、磁盘空间、权限等问题
68
- tmp_path.unlink(missing_ok=True)
69
+ await tmp_path.unlink(missing_ok=True)
69
70
  logger.error(f"文件 I/O 错误: {e}")
70
71
  return None
71
72
 
@@ -82,20 +83,20 @@ async def download_media(
82
83
  final_path = save_dir / (md5_hex + extension)
83
84
 
84
85
  if json:
85
- tmp_path.unlink(missing_ok=True)
86
+ await tmp_path.unlink(missing_ok=True)
86
87
  return final_path
87
- if final_path.exists():
88
+ if await anyio.Path(final_path).exists():
88
89
  logger.info(f"文件已存在,跳过: {final_path}")
89
- tmp_path.unlink(missing_ok=True)
90
+ await tmp_path.unlink(missing_ok=True)
90
91
  return final_path
91
92
 
92
93
  # 重命名
93
94
  try:
94
- tmp_path.rename(final_path)
95
+ await tmp_path.rename(anyio.Path(final_path))
95
96
  logger.info(f"保存成功: {final_path}")
96
97
  except OSError as e:
97
98
  logger.info(f"重命名失败: {e}")
98
- tmp_path.unlink(missing_ok=True)
99
+ await tmp_path.unlink(missing_ok=True)
99
100
  return None
100
101
  else:
101
102
  return final_path
@@ -247,10 +248,10 @@ async def pe_download(
247
248
  stream (bool, optional): 是否以流式下载. Defaults to False.
248
249
  **kwargs: 传递给下载器的参数
249
250
  """
250
- logger.debug("开始下载媒体数据")
251
251
  for media in self.select(Media):
252
252
  if not media.url:
253
253
  continue
254
+ logger.debug("开始下载媒体数据")
254
255
  raw: bytes = b""
255
256
  async with httpx.AsyncClient(**kwargs) as client:
256
257
  if stream:
@@ -363,7 +364,7 @@ def pe_save(
363
364
  return path.resolve()
364
365
 
365
366
  async def dump_msg(
366
- msg: UniMessage,
367
+ msg: UniMessage | str | tuple,
367
368
  *,
368
369
  media_save_dir: str | Path | bool | None = MEDIA_SAVE_DIR
369
370
  ) -> str:
@@ -382,7 +383,7 @@ async def dump_msg(
382
383
  :rtype: str
383
384
  """
384
385
 
385
- if isinstance(msg, tuple):
386
+ if not isinstance(msg, UniMessage):
386
387
  msg = UniMessage(msg)
387
388
 
388
389
  msg = await pe_download(msg)
@@ -2,7 +2,7 @@ import random
2
2
 
3
3
  from nonebot.log import logger
4
4
  from nonebot_plugin_alconna import MsgTarget, UniMessage, UniMsg
5
- from nonebot_plugin_orm import async_scoped_session # noqa: TC002
5
+ from nonebot_plugin_orm import async_scoped_session
6
6
 
7
7
  from .command import on_every_message
8
8
  from .database import get_contents, get_entry