ErisPulse-MatrixAdapter 4.3.0__tar.gz → 4.4.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.
Files changed (17) hide show
  1. {erispulse_matrixadapter-4.3.0 → erispulse_matrixadapter-4.4.0}/ErisPulse_MatrixAdapter.egg-info/PKG-INFO +1 -1
  2. {erispulse_matrixadapter-4.3.0 → erispulse_matrixadapter-4.4.0}/MatrixAdapter/Core.py +100 -1
  3. {erispulse_matrixadapter-4.3.0 → erispulse_matrixadapter-4.4.0}/PKG-INFO +1 -1
  4. {erispulse_matrixadapter-4.3.0 → erispulse_matrixadapter-4.4.0}/pyproject.toml +1 -1
  5. {erispulse_matrixadapter-4.3.0 → erispulse_matrixadapter-4.4.0}/ErisPulse_MatrixAdapter.egg-info/SOURCES.txt +0 -0
  6. {erispulse_matrixadapter-4.3.0 → erispulse_matrixadapter-4.4.0}/ErisPulse_MatrixAdapter.egg-info/dependency_links.txt +0 -0
  7. {erispulse_matrixadapter-4.3.0 → erispulse_matrixadapter-4.4.0}/ErisPulse_MatrixAdapter.egg-info/entry_points.txt +0 -0
  8. {erispulse_matrixadapter-4.3.0 → erispulse_matrixadapter-4.4.0}/ErisPulse_MatrixAdapter.egg-info/requires.txt +0 -0
  9. {erispulse_matrixadapter-4.3.0 → erispulse_matrixadapter-4.4.0}/ErisPulse_MatrixAdapter.egg-info/top_level.txt +0 -0
  10. {erispulse_matrixadapter-4.3.0 → erispulse_matrixadapter-4.4.0}/LICENSE +0 -0
  11. {erispulse_matrixadapter-4.3.0 → erispulse_matrixadapter-4.4.0}/MatrixAdapter/Converter.py +0 -0
  12. {erispulse_matrixadapter-4.3.0 → erispulse_matrixadapter-4.4.0}/MatrixAdapter/NioSession.py +0 -0
  13. {erispulse_matrixadapter-4.3.0 → erispulse_matrixadapter-4.4.0}/MatrixAdapter/__init__.py +0 -0
  14. {erispulse_matrixadapter-4.3.0 → erispulse_matrixadapter-4.4.0}/README.md +0 -0
  15. {erispulse_matrixadapter-4.3.0 → erispulse_matrixadapter-4.4.0}/setup.cfg +0 -0
  16. {erispulse_matrixadapter-4.3.0 → erispulse_matrixadapter-4.4.0}/test/test.py +0 -0
  17. {erispulse_matrixadapter-4.3.0 → erispulse_matrixadapter-4.4.0}/test/test_e2ee.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ErisPulse-MatrixAdapter
3
- Version: 4.3.0
3
+ Version: 4.4.0
4
4
  Summary: ErisPulse Matrix protocol adapter with native end-to-end encryption support
5
5
  Author-email: wsu2059 <wsu2059@qq.com>
6
6
  License: MIT License
@@ -1,4 +1,5 @@
1
1
  import asyncio
2
+ import html as _html
2
3
  import mimetypes
3
4
  import uuid
4
5
  from dataclasses import dataclass, field
@@ -14,12 +15,63 @@ from ErisPulse.Core.i18n import i18n
14
15
  from .Converter import MatrixConverter
15
16
  from .NioSession import NIO_AVAILABLE, NioSession, NioSessionError
16
17
 
18
+
19
+ def _forward_node_text(node: Dict) -> str:
20
+ """提取合并转发节点 content 中全部 text 段文本(段间直接连接)"""
21
+ data = node.get("data") or {}
22
+ return "".join(
23
+ str(seg.get("data", {}).get("text") or "")
24
+ for seg in (data.get("content") or [])
25
+ if isinstance(seg, dict) and seg.get("type") == "text"
26
+ )
27
+
28
+
29
+ def _forward_nodes_plain(nodes, separator: str = "\n") -> str:
30
+ """合并转发节点降级纯文本:每条「昵称: 正文」"""
31
+ lines = []
32
+ for node in nodes if isinstance(nodes, list) else [nodes]:
33
+ if not isinstance(node, dict):
34
+ continue
35
+ data = node.get("data") or {}
36
+ nickname = str(data.get("nickname") or "").strip()
37
+ text = _forward_node_text(node).strip()
38
+ if nickname and text:
39
+ lines.append(f"{nickname}: {text}")
40
+ elif text:
41
+ lines.append(text)
42
+ return separator.join(lines)
43
+
44
+
45
+ def _build_forward_matrix_html(nodes, title: str = "合并聊天记录") -> str:
46
+ """渲染合并转发为 Matrix formatted_body(白名单标签:p/strong/hr/blockquote/br)
47
+
48
+ Matrix HTML 白名单会剥离 details/summary 与内联样式,故用 blockquote 引用块形态。
49
+ """
50
+ blocks = []
51
+ for node in nodes if isinstance(nodes, list) else [nodes]:
52
+ if not isinstance(node, dict):
53
+ continue
54
+ data = node.get("data") or {}
55
+ nickname = str(data.get("nickname") or "").strip()
56
+ text = _forward_node_text(node).strip()
57
+ if not text:
58
+ continue
59
+ header = f"<strong>{_html.escape(nickname)}</strong><br/>" if nickname else ""
60
+ body = _html.escape(text).replace("\n", "<br/>")
61
+ blocks.append(f"<blockquote>{header}{body}</blockquote>")
62
+ if not blocks:
63
+ return ""
64
+ return (
65
+ f"<p><strong>{_html.escape(title)}</strong></p>"
66
+ + "<hr/>".join(blocks)
67
+ )
68
+
17
69
  try:
18
70
  from ErisPulse.runtime.tasks import spawn_background
19
71
  except ImportError: # pragma: no cover
20
72
  spawn_background = None
21
73
 
22
- __version__ = "4.3.0"
74
+ __version__ = "4.4.0"
23
75
 
24
76
  # 软依赖的框架最低版本(运行时检测,仅提示不强制)
25
77
  MIN_FRAMEWORK_VERSION = (2, 7, 1)
@@ -183,7 +235,54 @@ class MatrixAdapter(BaseAdapter):
183
235
  [{"type": "html", "data": {"html": html, "fallback": fallback}}]
184
236
  )
185
237
 
238
+ def MergeForward(self, nodes, title: str = "合并聊天记录"):
239
+ """发送合并转发消息(Matrix formatted_body:标题 + 每条 blockquote 引用块)
240
+
241
+ :param nodes: go-cqhttp 扩展的 OneBot v11 合并转发节点数组(或单节点 dict)
242
+ :param title: 标题
243
+ :return: asyncio.Task
244
+ """
245
+ if isinstance(nodes, dict):
246
+ nodes = [nodes]
247
+ return asyncio.create_task(
248
+ self._do_send_merge_forward(
249
+ [n for n in nodes if isinstance(n, dict)], title
250
+ )
251
+ )
252
+
253
+ async def _do_send_merge_forward(self, nodes, title: str = "合并聊天记录"):
254
+ html = _build_forward_matrix_html(nodes, title)
255
+ if not html:
256
+ return self._adapter.make_error(
257
+ retcode=10006, message="MergeForward: 节点无有效文本内容"
258
+ )
259
+ return await self._do_send_raw_ob12(
260
+ [
261
+ {
262
+ "type": "html",
263
+ "data": {
264
+ "html": html,
265
+ "fallback": _forward_nodes_plain(nodes),
266
+ },
267
+ }
268
+ ]
269
+ )
270
+
186
271
  def Raw_ob12(self, message: List[Dict], **kwargs):
272
+ # node 段解析:纯 node 列表自动路由合并转发;混合列表降级为纯文本
273
+ if message and all(
274
+ isinstance(seg, dict) and seg.get("type") == "node" for seg in message
275
+ ):
276
+ return asyncio.create_task(
277
+ self._do_send_merge_forward([seg for seg in message])
278
+ )
279
+ message = [
280
+ {"type": "text", "data": {"text": _forward_nodes_plain(seg)}}
281
+ if isinstance(seg, dict) and seg.get("type") == "node"
282
+ else seg
283
+ for seg in message
284
+ ]
285
+
187
286
  async def _send():
188
287
  return await self._do_send_raw_ob12(message, **kwargs)
189
288
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ErisPulse-MatrixAdapter
3
- Version: 4.3.0
3
+ Version: 4.4.0
4
4
  Summary: ErisPulse Matrix protocol adapter with native end-to-end encryption support
5
5
  Author-email: wsu2059 <wsu2059@qq.com>
6
6
  License: MIT License
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ErisPulse-MatrixAdapter"
3
- version = "4.3.0"
3
+ version = "4.4.0"
4
4
  description = "ErisPulse Matrix protocol adapter with native end-to-end encryption support"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"