ErisPulse-QvQMemes 0.1.0__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.
@@ -0,0 +1,105 @@
1
+ Metadata-Version: 2.4
2
+ Name: ErisPulse-QvQMemes
3
+ Version: 0.1.0
4
+ Summary: ErisPulse 表情包系统插件:自动收录打标签、语义选图发送 —— 全智能零概率(QvQChat 伴侣模块)
5
+ License: MIT
6
+ Project-URL: homepage, https://github.com/wsu2059q/ErisPulse-QvQMemes
7
+ Project-URL: Issues, https://github.com/wsu2059q/ErisPulse-QvQMemes/issues
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: ErisPulse>=2.8.0
11
+ Provides-Extra: test
12
+ Requires-Dist: pytest>=8.0; extra == "test"
13
+ Requires-Dist: pytest-asyncio>=0.23; extra == "test"
14
+
15
+ <div align="center">
16
+
17
+ 简体中文 | [繁體中文](README.zh-TW.md) | [English](README.en.md) | [日本語](README.ja.md) | [Русский](README.ru.md)
18
+
19
+ <img src="https://raw.githubusercontent.com/ErisPulse/ErisPulse/main/.github/assets/ErisPulseLogo.png" width="180" alt="QvQMemes" />
20
+
21
+ <img src="https://raw.githubusercontent.com/ErisPulse/.github/main/assets/badges/module.svg" alt="ErisPulse Module" />
22
+
23
+ </div>
24
+
25
+ # QvQMemes
26
+
27
+ **给 [QvQChat](https://github.com/wsu2059q/ErisPulse-QvQChat) 智能体用的表情包系统:旁听群聊自动收录、视觉模型自动打标签,何时发、发哪张由 AI 自己决定。**
28
+
29
+ 市面上的"斗图插件"多半是台老虎机:消息一来掷个骰子,从文件夹里随机抽出一张,抽到什么和正在聊什么毫无关系。这个模块反其道而行。表情包在这里被当作智能体表达的一部分,离开库的唯一途径是 `send_sticker` 工具——它经 QvQChat 生态桥注册成一个普通的 AI 工具。此刻该不该发表情包——接个梗、给个情绪、回敬一句——完全是模型的判断,唯一的引导是工具描述里那句"适合轻松/调侃/情绪表达场景,正式任务慎用"。整条链路没有定时器、没有骰子,这正是它与"随机斗图"类插件的本质区别,而且是刻意为之。
30
+
31
+ "发哪张"的解决方式相同。模型给出语义查询("得意的猫""委婉地感谢"),模块对库内每张图的描述和标签做确定性的词面匹配打分——标签全等记 4 分、标签子串 2 分、描述命中 3 分,逐词累加——然后把分数最高的一张发出去。收录同样不劳人手:群聊里流过的图片经 QvQChat 的 `qvq.observe` 事件被顺路收下,视觉模型逐张看过,写一句内容与情绪的描述,再给三到六个适用场景标签,全程没人动手。模块对排序做过的唯一调整是新鲜度降权:`recency_hours` 窗口内发过的图检索分数减半,免得智能体一星期都在刷同一张图。这是确定性调度,不是概率——近期用过的降权、库满了按最久未用淘汰,除此之外没有任何东西会打乱顺序。
32
+
33
+ <p>
34
+
35
+ <a href="https://www.python.org/"><img src="https://img.shields.io/badge/Python-3.10+-FFD43B?style=for-the-badge&logo=python&logoColor=blue" alt="Python"></a>
36
+
37
+ <a href="https://github.com/ErisPulse/ErisPulse"><img src="https://img.shields.io/badge/ErisPulse-2.8.0+-00A3FF?style=for-the-badge" alt="ErisPulse"></a>
38
+
39
+ <a href="./LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue?style=for-the-badge" alt="License"></a>
40
+
41
+ </p>
42
+
43
+ ## 安装
44
+
45
+ 需要 [QvQChat](https://github.com/wsu2059q/ErisPulse-QvQChat) 4.0.4 及以上(本模块对接的 `qvq.observe` 事件与插件设置页均自该版本引入),基于 ErisPulse 2.8.0+。
46
+
47
+ ```bash
48
+ epsdk install ErisPulse-QvQMemes
49
+ # 或
50
+ pip install ErisPulse-QvQMemes
51
+ ```
52
+
53
+ ## 启用
54
+
55
+ 不需要启用。模块装上后,生态桥自动把它的服务转成 AI 工具:智能体随即获得 `send_sticker`,连同 `sticker_search`、`sticker_stats`、`sticker_add`;卸载时这些工具同样悄无声息地消失。配置项则出现在 QvQChat Dashboard 设置页的插件分区。
56
+
57
+ 表情包库默认在 `data/QvQMemes/stickers`。从聊天里收录的图片会下载到这里,以内容哈希的前 16 位十六进制命名;每次启动时模块还会扫描该目录,把你手动放进去的图片一并入库——直接丢一个文件夹的表情包进来铺底,完全是正当用法。
58
+
59
+ ## 工作方式
60
+
61
+ 收录走的是旁路,绝不阻塞聊天主链路。QvQChat 广播 `qvq.observe`(它看到的每条群消息,每条最多带 3 张图)时,模块快速校验载荷后就把活儿交给后台任务。后台任务先按 URL 和内容哈希双指纹去重,再过单会话每日上限,然后串行下载落盘——下载失败也会保留 URL 引用,图照样能发——最后排队等视觉打标签。打标签经 QvQChat 的 `describe_image` 完成;视觉模型缺失或调用失败时,条目暂记「未分类」,仍可凭描述参与检索。这条链路上没有哪一环是致命的。所有条目以 `{id, path, url, desc, tags, source, uses, last_used, added}` 的形式存在框架的键值存储里。
62
+
63
+ 发送走平台适配器的 SendDSL,框架支持的平台上表现一致:
64
+
65
+ ```python
66
+ # 模型调用 send_sticker 工具时,生态桥实际执行的就是:
67
+ await sdk.module.call("QvQMemes", "send_sticker",
68
+ query="得意的猫 嘚瑟",
69
+ platform="qqbot",
70
+ target_id="123456",
71
+ target_type="group")
72
+ # -> "已发送:一只翻着白眼的得意白猫|得意 调侃 反击"
73
+ ```
74
+
75
+ 没有匹配时,返回值会说明情况并列出库里的高频标签,模型可以换词重试,而不是无声地失败。
76
+
77
+ ## 配置
78
+
79
+ 配置项在本模块的 ConfigClass 中声明,QvQChat 设置页自动渲染成表单;运行值在加载时经 `QvQChat.plugin_config` 拉回,失败则静默沿用默认。
80
+
81
+ | 键 | 默认值 | 说明 |
82
+ |------|---------|-------------|
83
+ | `library_dir` | `data/QvQMemes/stickers` | 库目录:下载的图片落在这里,启动时扫描收录已有图片 |
84
+ | `max_library` | `500` | 库容量上限;超出按最久未用淘汰(从未用过的按入库时间计) |
85
+ | `recency_hours` | `72` | 新鲜度降权窗口(小时):窗口内发过的图检索分数减半 |
86
+ | `send_method` | `Image` | 发送所用的 SendDSL 方法(图片 URL 或本地路径) |
87
+ | `auto_collect` | `True` | 旁听 `qvq.observe` 自动收录图片(每条消息最多 3 张) |
88
+ | `collect_per_chat_daily` | `20` | 单会话每日自动收录上限;≤0 表示不限 |
89
+ | `tag_via_vision` | `True` | 收录后用 QvQChat 视觉模型打标签;不可用时暂记「未分类」 |
90
+
91
+ ## 服务
92
+
93
+ 五个服务全部在 `get_meta().services` 白名单中。它们只返回 str 或 dict,绝不抛异常——内部错误一律转成可读文本或 `error` 字段,模型拿到的永远是一个能接着用的答复。
94
+
95
+ | 服务 | 签名 | 说明 |
96
+ |---------|-----------|-------------|
97
+ | `send_sticker` | `(query: str, platform: str, target_id: str, target_type: str = "group") -> str` | 按语义查询选最优一张并发送;成功返回「已发送:\<desc\>」,无匹配返回可用标签提示,失败返回错误文本 |
98
+ | `sticker_search` | `(query: str, top_n: int = 5) -> dict` | 只检索不发图:`{query, count, results: [{id, desc, tags, score, url, path, uses}], tag_hints}` |
99
+ | `sticker_stats` | `() -> dict` | 库统计:`{total, max_library, untagged, added_today, tag_distribution, top_used}` |
100
+ | `sticker_add` | `(url: str = '', path: str = '', desc: str = '', tags: str = '') -> str` | 手动收录(URL 或本地路径);desc/tags 省略时走视觉打标签 |
101
+ | `qvq_settings` | `() -> dict` | 归属标记 `{"title": ...}`;声明该服务的模块才会出现在 QvQChat 设置页插件分区 |
102
+
103
+ ## 许可证
104
+
105
+ MIT
@@ -0,0 +1,19 @@
1
+ README.md
2
+ pyproject.toml
3
+ ErisPulse_QvQMemes.egg-info/PKG-INFO
4
+ ErisPulse_QvQMemes.egg-info/SOURCES.txt
5
+ ErisPulse_QvQMemes.egg-info/dependency_links.txt
6
+ ErisPulse_QvQMemes.egg-info/entry_points.txt
7
+ ErisPulse_QvQMemes.egg-info/requires.txt
8
+ ErisPulse_QvQMemes.egg-info/top_level.txt
9
+ QvQMemes/Config.py
10
+ QvQMemes/Main.py
11
+ QvQMemes/__init__.py
12
+ QvQMemes/collect.py
13
+ QvQMemes/library.py
14
+ QvQMemes/service.py
15
+ QvQMemes/texts.py
16
+ tests/test_collect.py
17
+ tests/test_library.py
18
+ tests/test_plugin_cfg.py
19
+ tests/test_service.py
@@ -0,0 +1,2 @@
1
+ [erispulse.module]
2
+ QvQMemes = QvQMemes:Main
@@ -0,0 +1,5 @@
1
+ ErisPulse>=2.8.0
2
+
3
+ [test]
4
+ pytest>=8.0
5
+ pytest-asyncio>=0.23
@@ -0,0 +1,105 @@
1
+ Metadata-Version: 2.4
2
+ Name: ErisPulse-QvQMemes
3
+ Version: 0.1.0
4
+ Summary: ErisPulse 表情包系统插件:自动收录打标签、语义选图发送 —— 全智能零概率(QvQChat 伴侣模块)
5
+ License: MIT
6
+ Project-URL: homepage, https://github.com/wsu2059q/ErisPulse-QvQMemes
7
+ Project-URL: Issues, https://github.com/wsu2059q/ErisPulse-QvQMemes/issues
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: ErisPulse>=2.8.0
11
+ Provides-Extra: test
12
+ Requires-Dist: pytest>=8.0; extra == "test"
13
+ Requires-Dist: pytest-asyncio>=0.23; extra == "test"
14
+
15
+ <div align="center">
16
+
17
+ 简体中文 | [繁體中文](README.zh-TW.md) | [English](README.en.md) | [日本語](README.ja.md) | [Русский](README.ru.md)
18
+
19
+ <img src="https://raw.githubusercontent.com/ErisPulse/ErisPulse/main/.github/assets/ErisPulseLogo.png" width="180" alt="QvQMemes" />
20
+
21
+ <img src="https://raw.githubusercontent.com/ErisPulse/.github/main/assets/badges/module.svg" alt="ErisPulse Module" />
22
+
23
+ </div>
24
+
25
+ # QvQMemes
26
+
27
+ **给 [QvQChat](https://github.com/wsu2059q/ErisPulse-QvQChat) 智能体用的表情包系统:旁听群聊自动收录、视觉模型自动打标签,何时发、发哪张由 AI 自己决定。**
28
+
29
+ 市面上的"斗图插件"多半是台老虎机:消息一来掷个骰子,从文件夹里随机抽出一张,抽到什么和正在聊什么毫无关系。这个模块反其道而行。表情包在这里被当作智能体表达的一部分,离开库的唯一途径是 `send_sticker` 工具——它经 QvQChat 生态桥注册成一个普通的 AI 工具。此刻该不该发表情包——接个梗、给个情绪、回敬一句——完全是模型的判断,唯一的引导是工具描述里那句"适合轻松/调侃/情绪表达场景,正式任务慎用"。整条链路没有定时器、没有骰子,这正是它与"随机斗图"类插件的本质区别,而且是刻意为之。
30
+
31
+ "发哪张"的解决方式相同。模型给出语义查询("得意的猫""委婉地感谢"),模块对库内每张图的描述和标签做确定性的词面匹配打分——标签全等记 4 分、标签子串 2 分、描述命中 3 分,逐词累加——然后把分数最高的一张发出去。收录同样不劳人手:群聊里流过的图片经 QvQChat 的 `qvq.observe` 事件被顺路收下,视觉模型逐张看过,写一句内容与情绪的描述,再给三到六个适用场景标签,全程没人动手。模块对排序做过的唯一调整是新鲜度降权:`recency_hours` 窗口内发过的图检索分数减半,免得智能体一星期都在刷同一张图。这是确定性调度,不是概率——近期用过的降权、库满了按最久未用淘汰,除此之外没有任何东西会打乱顺序。
32
+
33
+ <p>
34
+
35
+ <a href="https://www.python.org/"><img src="https://img.shields.io/badge/Python-3.10+-FFD43B?style=for-the-badge&logo=python&logoColor=blue" alt="Python"></a>
36
+
37
+ <a href="https://github.com/ErisPulse/ErisPulse"><img src="https://img.shields.io/badge/ErisPulse-2.8.0+-00A3FF?style=for-the-badge" alt="ErisPulse"></a>
38
+
39
+ <a href="./LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue?style=for-the-badge" alt="License"></a>
40
+
41
+ </p>
42
+
43
+ ## 安装
44
+
45
+ 需要 [QvQChat](https://github.com/wsu2059q/ErisPulse-QvQChat) 4.0.4 及以上(本模块对接的 `qvq.observe` 事件与插件设置页均自该版本引入),基于 ErisPulse 2.8.0+。
46
+
47
+ ```bash
48
+ epsdk install ErisPulse-QvQMemes
49
+ # 或
50
+ pip install ErisPulse-QvQMemes
51
+ ```
52
+
53
+ ## 启用
54
+
55
+ 不需要启用。模块装上后,生态桥自动把它的服务转成 AI 工具:智能体随即获得 `send_sticker`,连同 `sticker_search`、`sticker_stats`、`sticker_add`;卸载时这些工具同样悄无声息地消失。配置项则出现在 QvQChat Dashboard 设置页的插件分区。
56
+
57
+ 表情包库默认在 `data/QvQMemes/stickers`。从聊天里收录的图片会下载到这里,以内容哈希的前 16 位十六进制命名;每次启动时模块还会扫描该目录,把你手动放进去的图片一并入库——直接丢一个文件夹的表情包进来铺底,完全是正当用法。
58
+
59
+ ## 工作方式
60
+
61
+ 收录走的是旁路,绝不阻塞聊天主链路。QvQChat 广播 `qvq.observe`(它看到的每条群消息,每条最多带 3 张图)时,模块快速校验载荷后就把活儿交给后台任务。后台任务先按 URL 和内容哈希双指纹去重,再过单会话每日上限,然后串行下载落盘——下载失败也会保留 URL 引用,图照样能发——最后排队等视觉打标签。打标签经 QvQChat 的 `describe_image` 完成;视觉模型缺失或调用失败时,条目暂记「未分类」,仍可凭描述参与检索。这条链路上没有哪一环是致命的。所有条目以 `{id, path, url, desc, tags, source, uses, last_used, added}` 的形式存在框架的键值存储里。
62
+
63
+ 发送走平台适配器的 SendDSL,框架支持的平台上表现一致:
64
+
65
+ ```python
66
+ # 模型调用 send_sticker 工具时,生态桥实际执行的就是:
67
+ await sdk.module.call("QvQMemes", "send_sticker",
68
+ query="得意的猫 嘚瑟",
69
+ platform="qqbot",
70
+ target_id="123456",
71
+ target_type="group")
72
+ # -> "已发送:一只翻着白眼的得意白猫|得意 调侃 反击"
73
+ ```
74
+
75
+ 没有匹配时,返回值会说明情况并列出库里的高频标签,模型可以换词重试,而不是无声地失败。
76
+
77
+ ## 配置
78
+
79
+ 配置项在本模块的 ConfigClass 中声明,QvQChat 设置页自动渲染成表单;运行值在加载时经 `QvQChat.plugin_config` 拉回,失败则静默沿用默认。
80
+
81
+ | 键 | 默认值 | 说明 |
82
+ |------|---------|-------------|
83
+ | `library_dir` | `data/QvQMemes/stickers` | 库目录:下载的图片落在这里,启动时扫描收录已有图片 |
84
+ | `max_library` | `500` | 库容量上限;超出按最久未用淘汰(从未用过的按入库时间计) |
85
+ | `recency_hours` | `72` | 新鲜度降权窗口(小时):窗口内发过的图检索分数减半 |
86
+ | `send_method` | `Image` | 发送所用的 SendDSL 方法(图片 URL 或本地路径) |
87
+ | `auto_collect` | `True` | 旁听 `qvq.observe` 自动收录图片(每条消息最多 3 张) |
88
+ | `collect_per_chat_daily` | `20` | 单会话每日自动收录上限;≤0 表示不限 |
89
+ | `tag_via_vision` | `True` | 收录后用 QvQChat 视觉模型打标签;不可用时暂记「未分类」 |
90
+
91
+ ## 服务
92
+
93
+ 五个服务全部在 `get_meta().services` 白名单中。它们只返回 str 或 dict,绝不抛异常——内部错误一律转成可读文本或 `error` 字段,模型拿到的永远是一个能接着用的答复。
94
+
95
+ | 服务 | 签名 | 说明 |
96
+ |---------|-----------|-------------|
97
+ | `send_sticker` | `(query: str, platform: str, target_id: str, target_type: str = "group") -> str` | 按语义查询选最优一张并发送;成功返回「已发送:\<desc\>」,无匹配返回可用标签提示,失败返回错误文本 |
98
+ | `sticker_search` | `(query: str, top_n: int = 5) -> dict` | 只检索不发图:`{query, count, results: [{id, desc, tags, score, url, path, uses}], tag_hints}` |
99
+ | `sticker_stats` | `() -> dict` | 库统计:`{total, max_library, untagged, added_today, tag_distribution, top_used}` |
100
+ | `sticker_add` | `(url: str = '', path: str = '', desc: str = '', tags: str = '') -> str` | 手动收录(URL 或本地路径);desc/tags 省略时走视觉打标签 |
101
+ | `qvq_settings` | `() -> dict` | 归属标记 `{"title": ...}`;声明该服务的模块才会出现在 QvQChat 设置页插件分区 |
102
+
103
+ ## 许可证
104
+
105
+ MIT
@@ -0,0 +1,61 @@
1
+ """QvQMemes 配置(dataclass + metadata,组织方式对照 QvQStalker/Config.py 独立实现)。
2
+
3
+ metadata 的 description.i18n 键形如 ``QvQMemes.<key>``,ui 提供
4
+ Dashboard 设置表单所需的 widget/group/order 信息。
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from dataclasses import dataclass, field
10
+
11
+ try: # 独立运行/测试环境没有 ErisPulse 时仍可实例化配置
12
+ from ErisPulse.Core.Bases import BaseConfig
13
+ except Exception: # pragma: no cover - 环境兜底
14
+ BaseConfig = object # type: ignore[assignment, misc]
15
+
16
+
17
+ def _desc(key: str, text: str, group: str, order: int, widget: str = "text",
18
+ options: list | None = None) -> dict:
19
+ ui: dict = {"widget": widget, "group": group, "order": order}
20
+ if options is not None:
21
+ ui["options"] = options
22
+ return {"description": {"i18n": f"QvQMemes.{key}", "default": text}, "ui": ui}
23
+
24
+
25
+ @dataclass
26
+ class QvQMemesConfig(BaseConfig):
27
+ # ---- 库 ----
28
+ library_dir: str = field(
29
+ default="data/QvQMemes/stickers",
30
+ metadata=_desc("library_dir", "表情包库存目录(下载/扫描的图片落盘位置)", "library", 1),
31
+ )
32
+ max_library: int = field(
33
+ default=500,
34
+ metadata=_desc("max_library", "库容量上限(超出按最久未用淘汰)", "library", 2, "number"),
35
+ )
36
+ recency_hours: float = field(
37
+ default=72.0,
38
+ metadata=_desc("recency_hours", "新鲜度调度窗口(小时):该窗口内用过的表情包在选图时确定性降权(非概率),避免短期内重复刷同一张",
39
+ "library", 3, "number"),
40
+ )
41
+ send_method: str = field(
42
+ default="Image",
43
+ metadata=_desc("send_method", "发送方式(SendDSL 方法名),默认 Image(图片 URL 或本地路径)",
44
+ "library", 4),
45
+ )
46
+
47
+ # ---- 自动收录 ----
48
+ auto_collect: bool = field(
49
+ default=True,
50
+ metadata=_desc("auto_collect", "自动收录:旁听 qvq.observe 载荷中的 images(每条消息最多 3 张)",
51
+ "collect", 1, "switch"),
52
+ )
53
+ collect_per_chat_daily: int = field(
54
+ default=20,
55
+ metadata=_desc("collect_per_chat_daily", "单会话每日自动收录上限(防刷屏式入库)", "collect", 2, "number"),
56
+ )
57
+ tag_via_vision: bool = field(
58
+ default=True,
59
+ metadata=_desc("tag_via_vision", "收录后用 QvQChat 视觉模型自动打标签(describe_image 服务;不可用时记为「未分类」,不影响收录)",
60
+ "collect", 3, "switch"),
61
+ )