ErisPulse-GovReport 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,160 @@
1
+ Metadata-Version: 2.4
2
+ Name: ErisPulse-GovReport
3
+ Version: 0.1.0
4
+ Summary: 治理观测模块:采集命令执行与事件拦截记录,输出统计报表与配额消耗
5
+ Author: wsu2059q
6
+ License: MIT
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Provides-Extra: test
11
+ Requires-Dist: pytest>=7.0; extra == "test"
12
+ Requires-Dist: pytest-asyncio>=0.21; extra == "test"
13
+ Requires-Dist: ErisPulse-Testing; extra == "test"
14
+ Dynamic: license-file
15
+
16
+ <div align="center">
17
+
18
+ <img src=".github/assets/ErisPulseLogo.png" width="180" alt="ErisPulse GovReport" />
19
+
20
+ # ErisPulse GovReport
21
+
22
+ **治理观测模块 —— 命令执行、事件拦截与配额消耗的统计报表。**
23
+
24
+ 基于 ErisPulse 2.9 命令治理与生命周期事件的观测模块,使用内置 ORM 持久化记录,报表经 Takumi 渲染为卡片。
25
+
26
+ <p>
27
+ <a href="https://pypi.org/project/ErisPulse-GovReport/"><img src="https://img.shields.io/pypi/v/ErisPulse-GovReport?style=for-the-badge&logo=pypi&logoColor=white" alt="PyPI"></a>
28
+ <a href="https://pypi.org/project/ErisPulse-GovReport/"><img src="https://img.shields.io/badge/Python-3.10+-FFD43B?style=for-the-badge&logo=python&logoColor=blue" alt="Python"></a>
29
+ <a href="./LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue?style=for-the-badge" alt="License"></a>
30
+ <a href="https://github.com/ErisPulse/ErisPulse-GovReport"><img src="https://img.shields.io/github/stars/ErisPulse/ErisPulse-GovReport?style=for-the-badge&logo=github&color=brightgreen" alt="Stars"></a>
31
+ <a href="https://pepy.tech/project/ErisPulse-GovReport"><img src="https://img.shields.io/pepy/dt/ErisPulse-GovReport?style=for-the-badge&color=blue" alt="Downloads"></a>
32
+ <a href="https://github.com/ErisPulse/ErisPulse"><img src="https://img.shields.io/badge/Powered_by-ErisPulse-FF6B9D?style=for-the-badge&logo=bookstack&logoColor=white" alt="ErisPulse"></a>
33
+ </p>
34
+
35
+ <p>
36
+ <a href="./README.md">简体中文</a> | <a href="./README.en.md">English</a>
37
+ </p>
38
+
39
+ </div>
40
+
41
+ ---
42
+
43
+ ## 简介
44
+
45
+ GovReport 是基于 [ErisPulse](https://github.com/ErisPulse/ErisPulse/) 架构开发的治理观测模块:
46
+
47
+ - 采集 `command.executed` 命令执行记录(成功/失败/废弃)
48
+ - 采集 `adapter.event.blocked`(中间件否决)与 `scope.blocked`(作用域拦截)记录
49
+ - 读取 `usage_limit` 配额计数(`erispulse.usage` 存储键),对照声明上限展示消耗
50
+ - 枚举全部命令的治理声明(cooldown / rate_limit / usage_limit / deprecated)
51
+ - 记录用内置 ORM(`Model` / `Field`)持久化,按保留天数自动清理
52
+ - 报表经 Takumi 渲染为图片卡片,不可用时降级文本
53
+
54
+ 要求 ErisPulse >= 2.9.0。
55
+
56
+ ---
57
+
58
+ ## 效果展示
59
+
60
+ | `/gov stats` | `/gov blocked` |
61
+ |:---:|:---:|
62
+ | ![stats](.github/assets/demo/stats-light.png) | ![blocked](.github/assets/demo/blocked-light.png) |
63
+
64
+ | `/gov usage` | `/gov list` |
65
+ |:---:|:---:|
66
+ | ![usage](.github/assets/demo/usage-light.png) | ![list](.github/assets/demo/list-light.png) |
67
+
68
+ ---
69
+
70
+
71
+ ---
72
+
73
+ ## 安装
74
+
75
+ ```bash
76
+ epsdk install ErisPulse-GovReport
77
+ ```
78
+
79
+ ---
80
+
81
+ ## 命令
82
+
83
+ | 命令 | 说明 |
84
+ |------|------|
85
+ | `/gov` | 显示用法 |
86
+ | `/gov stats [窗口]` | 命令执行统计(调用 Top / 成功率 / 废弃使用),窗口如 `24h`、`7d`,默认 `24h` |
87
+ | `/gov user <用户ID> [窗口]` | 单用户调用统计 |
88
+ | `/gov blocked [窗口]` | 拦截统计(中间件否决 / 作用域拦截) |
89
+ | `/gov usage` | 配额消耗报表(used/limit + 周期 + 作用域) |
90
+ | `/gov list [命令]` | 治理声明清单(可按命令名过滤) |
91
+ | `/gov purge <天数>` | 清理指定天数之前的记录 |
92
+
93
+ 全部命令需要框架主人权限。
94
+
95
+ ---
96
+
97
+ ## 使用示例
98
+
99
+ ```toml
100
+ # 给命令声明治理规则(本模块负责观测这些规则的运行效果)
101
+ [ErisPulse.event.command]
102
+ prefix = "/"
103
+
104
+ # Python 侧
105
+ # @command("translate", usage_limit="100/day", usage_limit_key="user")
106
+ ```
107
+
108
+ ```python
109
+ from ErisPulse.Core.Event import command
110
+
111
+ @command("translate", cooldown="30s", rate_limit="5/minute",
112
+ usage_limit="100/day", usage_limit_key="user", help="翻译")
113
+ async def translate_handler(event):
114
+ await event.reply("结果")
115
+
116
+ @command("oldcmd", deprecated="请用 /translate", deprecated_reject=True)
117
+ async def old_handler(event):
118
+ ...
119
+ ```
120
+
121
+ ---
122
+
123
+ ## 配置说明
124
+
125
+ ```toml
126
+ [GovReport]
127
+ enabled = true # 是否启用模块
128
+ collect_command_calls = true # 采集命令执行记录
129
+ collect_blocked = true # 采集拦截记录
130
+ retention_days = 30 # 记录保留天数(0 永久保留)
131
+ top_n = 10 # 报表单次展示条目数
132
+ render_card = true # 报表用 Takumi 渲染为卡片
133
+ theme = "auto" # 卡片配色 (auto/light/dark)
134
+ ```
135
+
136
+ | 配置项 | 类型 | 默认值 | 说明 |
137
+ |--------|------|--------|------|
138
+ | `enabled` | bool | `true` | 是否启用模块 |
139
+ | `collect_command_calls` | bool | `true` | 采集 `command.executed` |
140
+ | `collect_blocked` | bool | `true` | 采集中间件否决与作用域拦截 |
141
+ | `retention_days` | int | `30` | 保留天数(0 永久,启动与每日清理) |
142
+ | `top_n` | int | `10` | 报表单次展示条目数 |
143
+ | `render_card` | bool | `true` | 报表渲染为图片卡片 |
144
+ | `theme` | str | `auto` | 卡片配色 |
145
+
146
+ ---
147
+
148
+ ## 注意事项
149
+
150
+ 1. 配额消耗读取自治理系统的存储键(`erispulse.usage`),重启不丢
151
+ 2. 冷却/限流命中默认静默,不在采集范围内(可用 ErisPulse-Testing 的 DispatchTrace 排查单条命令)
152
+ 3. 记录写入框架存储后端(SQLite / MySQL / PostgreSQL 由存储配置决定)
153
+
154
+ ---
155
+
156
+ ## 参考链接
157
+
158
+ - [ErisPulse 主库](https://github.com/ErisPulse/ErisPulse/)
159
+ - [命令治理文档](https://github.com/ErisPulse/ErisPulse/tree/main/docs/zh-CN/getting-started/event-handling.md)
160
+ - [ORM 文档](https://github.com/ErisPulse/ErisPulse/tree/main/docs/zh-CN/developer-guide/orm.md)
@@ -0,0 +1,19 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ ErisPulse_GovReport.egg-info/PKG-INFO
5
+ ErisPulse_GovReport.egg-info/SOURCES.txt
6
+ ErisPulse_GovReport.egg-info/dependency_links.txt
7
+ ErisPulse_GovReport.egg-info/entry_points.txt
8
+ ErisPulse_GovReport.egg-info/requires.txt
9
+ ErisPulse_GovReport.egg-info/top_level.txt
10
+ GovReport/Config.py
11
+ GovReport/Core.py
12
+ GovReport/I18n.py
13
+ GovReport/Models.py
14
+ GovReport/Stats.py
15
+ GovReport/Visualizer.py
16
+ GovReport/__init__.py
17
+ tests/test_collector.py
18
+ tests/test_commands.py
19
+ tests/test_visual.py
@@ -0,0 +1,2 @@
1
+ [erispulse.module]
2
+ GovReport = GovReport:Main
@@ -0,0 +1,5 @@
1
+
2
+ [test]
3
+ pytest>=7.0
4
+ pytest-asyncio>=0.21
5
+ ErisPulse-Testing
@@ -0,0 +1,62 @@
1
+ from dataclasses import dataclass, field
2
+
3
+ from ErisPulse.Core.Bases import BaseConfig
4
+
5
+
6
+ @dataclass
7
+ class GovReportConfig(BaseConfig):
8
+
9
+ enabled: bool = field(
10
+ default=True,
11
+ metadata={
12
+ "description": {"i18n": "GovReport.enabled", "default": "是否启用模块"},
13
+ "ui": {"widget": "switch", "group": "basic", "order": 1},
14
+ },
15
+ )
16
+ collect_command_calls: bool = field(
17
+ default=True,
18
+ metadata={
19
+ "description": {"i18n": "GovReport.collect_command_calls", "default": "采集命令执行记录(command.executed)"},
20
+ "ui": {"widget": "switch", "group": "collect", "order": 2},
21
+ },
22
+ )
23
+ collect_blocked: bool = field(
24
+ default=True,
25
+ metadata={
26
+ "description": {"i18n": "GovReport.collect_blocked", "default": "采集事件拦截记录(中间件否决 / 作用域拦截)"},
27
+ "ui": {"widget": "switch", "group": "collect", "order": 3},
28
+ },
29
+ )
30
+ retention_days: int = field(
31
+ default=30,
32
+ metadata={
33
+ "description": {"i18n": "GovReport.retention_days", "default": "记录保留天数(0 永久保留,启动与每日清理)"},
34
+ "ui": {"widget": "number", "group": "collect", "order": 4},
35
+ },
36
+ )
37
+ top_n: int = field(
38
+ default=10,
39
+ metadata={
40
+ "description": {"i18n": "GovReport.top_n", "default": "统计报表单次展示的最大条目数"},
41
+ "ui": {"widget": "number", "group": "report", "order": 5},
42
+ },
43
+ )
44
+ render_card: bool = field(
45
+ default=True,
46
+ metadata={
47
+ "description": {"i18n": "GovReport.render_card", "default": "报表用 Takumi 渲染为卡片(不可用时降级文本)"},
48
+ "ui": {"widget": "switch", "group": "render", "order": 6},
49
+ },
50
+ )
51
+ theme: str = field(
52
+ default="auto",
53
+ metadata={
54
+ "description": {"i18n": "GovReport.theme", "default": "卡片配色 (auto=按时间自动切深浅色)"},
55
+ "ui": {
56
+ "widget": "select",
57
+ "options": ["auto", "light", "dark"],
58
+ "group": "render",
59
+ "order": 7,
60
+ },
61
+ },
62
+ )
@@ -0,0 +1,209 @@
1
+ import asyncio
2
+ import time
3
+
4
+ from ErisPulse import i18n, sdk
5
+ from ErisPulse.Core import Depends
6
+ from ErisPulse.Core.Bases import BaseModule, ModuleMeta
7
+ from ErisPulse.Core.Event import command
8
+ from ErisPulse.loaders import ModuleLoadStrategy
9
+
10
+ from .Config import GovReportConfig
11
+ from .I18n import GovReportI18n
12
+ from .Models import BlockEvent, CommandCall
13
+ from .Stats import GovStatsService, parse_window
14
+ from .Visualizer import CardVisualizer
15
+
16
+
17
+ def get_stats_service(event):
18
+ from ErisPulse import module as module_mgr
19
+
20
+ inst = module_mgr.get("GovReport")
21
+ if inst is None:
22
+ raise RuntimeError("GovReport not loaded")
23
+ return inst._stats
24
+
25
+
26
+ class Main(BaseModule):
27
+
28
+ ConfigClass = GovReportConfig
29
+ I18nClass = GovReportI18n
30
+
31
+ def __init__(self):
32
+ self.sdk = sdk
33
+ self.logger = sdk.logger.get_child("GovReport")
34
+ self._stats = GovStatsService(self.logger)
35
+ self._viz = CardVisualizer(sdk, self.logger)
36
+ self._daily_task = None
37
+
38
+ @staticmethod
39
+ def get_load_strategy() -> ModuleLoadStrategy:
40
+ return ModuleLoadStrategy(lazy_load=False, priority=20)
41
+
42
+ @staticmethod
43
+ def get_meta() -> ModuleMeta:
44
+ return ModuleMeta(
45
+ name="GovReport",
46
+ description="治理观测模块:采集命令执行与事件拦截记录,输出统计报表与配额消耗",
47
+ version="0.1.0",
48
+ author="ErisPulse-devs",
49
+ group="运维",
50
+ tags=["治理", "统计", "ORM", "观测"],
51
+ min_sdk_version="2.9.0",
52
+ )
53
+
54
+ async def on_load(self, event: dict) -> bool:
55
+ if not self.cfg.enabled:
56
+ self.logger.info("治理观测未启用(enabled=false)")
57
+ return True
58
+
59
+ await CommandCall.create_table()
60
+ await BlockEvent.create_table()
61
+ await self._stats.purge_expired(self.cfg.retention_days)
62
+
63
+ self._register_collectors()
64
+ self._register_commands()
65
+ self._daily_task = self.spawn(self._daily_purge_loop())
66
+ self.logger.info(i18n.t("GovReport.module_loaded", event=event))
67
+ return True
68
+
69
+ async def on_unload(self, event: dict) -> bool:
70
+ if self._daily_task is not None:
71
+ self._daily_task.cancel()
72
+ await asyncio.gather(self._daily_task, return_exceptions=True)
73
+ self._daily_task = None
74
+ self.logger.info(i18n.t("GovReport.module_unloaded", event=event))
75
+ return True
76
+
77
+ async def _daily_purge_loop(self):
78
+ while True:
79
+ await asyncio.sleep(86400)
80
+ try:
81
+ await self._stats.purge_expired(self.cfg.retention_days)
82
+ except Exception as e:
83
+ self.logger.warning(f"每日清理失败: {e}")
84
+
85
+ def _register_collectors(self):
86
+ @sdk.lifecycle.on("command.executed")
87
+ async def _on_command_executed(data):
88
+ if not self.cfg.collect_command_calls:
89
+ return
90
+ try:
91
+ await CommandCall.create(
92
+ ts=time.time(),
93
+ command=str(data.get("command", ""))[:128],
94
+ platform=str(data.get("platform", ""))[:64],
95
+ user_id=str(data.get("user_id", ""))[:128],
96
+ success=bool(data.get("success")),
97
+ error=str(data.get("error", "") or "")[:255],
98
+ )
99
+ except Exception as e:
100
+ self.logger.warning(f"记录命令执行失败: {e}")
101
+
102
+ @sdk.lifecycle.on("adapter.event.blocked")
103
+ async def _on_event_blocked(data):
104
+ if not self.cfg.collect_blocked:
105
+ return
106
+ raw = data.get("event") or {}
107
+ try:
108
+ await BlockEvent.create(
109
+ ts=time.time(),
110
+ source="middleware",
111
+ middleware=str(data.get("middleware", ""))[:128],
112
+ platform=str(data.get("platform", ""))[:64],
113
+ detail_type=str(data.get("detail_type", ""))[:64],
114
+ user_id=str(raw.get("user_id", "") or "")[:128],
115
+ )
116
+ except Exception as e:
117
+ self.logger.warning(f"记录拦截事件失败: {e}")
118
+
119
+ @sdk.lifecycle.on("scope.blocked")
120
+ async def _on_scope_blocked(data):
121
+ if not self.cfg.collect_blocked:
122
+ return
123
+ try:
124
+ await BlockEvent.create(
125
+ ts=time.time(),
126
+ source="scope",
127
+ dimension=str(data.get("dimension", ""))[:16],
128
+ module=str(data.get("module", "") or "")[:128],
129
+ platform=str(data.get("platform", ""))[:64],
130
+ user_id=str(data.get("user_id", "") or "")[:128],
131
+ session_id=str(data.get("session_id", "") or "")[:128],
132
+ )
133
+ except Exception as e:
134
+ self.logger.warning(f"记录作用域拦截失败: {e}")
135
+
136
+ async def _reply_card(self, event, html, fallback_text: str):
137
+ if self.cfg.render_card:
138
+ png = await self._viz.render(html, theme=self.cfg.theme)
139
+ if png is not None:
140
+ try:
141
+ await event.reply(png, method="Image")
142
+ return
143
+ except Exception as e:
144
+ self.logger.warning(f"图片回复失败,降级文本: {e}")
145
+ await event.reply(fallback_text)
146
+
147
+ def _register_commands(self):
148
+ @command("gov", master=True, help="治理观测")
149
+ async def gov_handler(event):
150
+ await event.reply(
151
+ "用法: /gov stats [窗口] | /gov user <用户ID> [窗口] | /gov blocked [窗口] | "
152
+ "/gov usage | /gov list [命令] | /gov purge <天数>"
153
+ )
154
+
155
+ @command("gov stats", master=True, help="命令执行统计", args="[window:str=24h]")
156
+ async def gov_stats_handler(event, window: str = "24h", stats=Depends(get_stats_service)):
157
+ secs = parse_window(window)
158
+ if secs is None:
159
+ await event.reply(i18n.t("GovReport.invalid_window", window=window))
160
+ return
161
+ await self._reply_card(
162
+ event,
163
+ await stats.command_stats_card(window, self.cfg.top_n, self._viz),
164
+ await stats.command_stats(secs, window, self.cfg.top_n),
165
+ )
166
+
167
+ @command("gov user", master=True, help="单用户统计", args="<user_id:str> [window:str=24h]")
168
+ async def gov_user_handler(event, user_id: str, window: str = "24h"):
169
+ secs = parse_window(window)
170
+ if secs is None:
171
+ await event.reply(i18n.t("GovReport.invalid_window", window=window))
172
+ return
173
+ await event.reply(await self._stats.user_stats(user_id, secs, window, self.cfg.top_n))
174
+
175
+ @command("gov blocked", master=True, help="拦截统计", args="[window:str=24h]")
176
+ async def gov_blocked_handler(event, window: str = "24h"):
177
+ secs = parse_window(window)
178
+ if secs is None:
179
+ await event.reply(i18n.t("GovReport.invalid_window", window=window))
180
+ return
181
+ await self._reply_card(
182
+ event,
183
+ await self._stats.blocked_stats_card(window, self.cfg.top_n, self._viz),
184
+ await self._stats.blocked_stats(secs, window, self.cfg.top_n),
185
+ )
186
+
187
+ @command("gov usage", master=True, help="配额消耗报表")
188
+ async def gov_usage_handler(event):
189
+ await self._reply_card(
190
+ event,
191
+ await self._stats.usage_report_card(self._viz),
192
+ await self._stats.usage_report(),
193
+ )
194
+
195
+ @command("gov list", master=True, help="治理声明清单", args="[cmd:str]")
196
+ async def gov_list_handler(event, cmd: str = ""):
197
+ await self._reply_card(
198
+ event,
199
+ self._stats.governance_list_card(cmd or None, self._viz),
200
+ self._stats.governance_list(cmd or None),
201
+ )
202
+
203
+ @command("gov purge", master=True, help="清理旧记录", args="<days:int>")
204
+ async def gov_purge_handler(event, days: int):
205
+ if days <= 0:
206
+ await event.reply(i18n.t("GovReport.invalid_window", window=str(days)))
207
+ return
208
+ calls, blocks = await self._stats.purge(days)
209
+ await event.reply(i18n.t("GovReport.purge_done", calls=calls, blocks=blocks, days=days))
@@ -0,0 +1,173 @@
1
+ from ErisPulse.Core.Bases import BaseI18n, I18nKey
2
+
3
+
4
+ class GovReportI18n(BaseI18n):
5
+
6
+ enabled: I18nKey = I18nKey(
7
+ default="Enable module",
8
+ zh_CN="是否启用模块",
9
+ zh_TW="是否啟用模組",
10
+ en="Enable module",
11
+ ja="モジュールを有効化",
12
+ ru="Включить модуль",
13
+ )
14
+ collect_command_calls: I18nKey = I18nKey(
15
+ default="Collect command execution records",
16
+ zh_CN="采集命令执行记录(command.executed)",
17
+ zh_TW="採集命令執行記錄(command.executed)",
18
+ en="Collect command execution records",
19
+ ja="コマンド実行記録を収集",
20
+ ru="Собирать записи выполнения команд",
21
+ )
22
+ collect_blocked: I18nKey = I18nKey(
23
+ default="Collect blocked event records (middleware veto / scope)",
24
+ zh_CN="采集事件拦截记录(中间件否决 / 作用域拦截)",
25
+ zh_TW="採集事件攔截記錄(中介軟體否決 / 作用域攔截)",
26
+ en="Collect blocked event records (middleware veto / scope)",
27
+ ja="ブロック記録を収集(ミドルウェア拒否 / スコープ)",
28
+ ru="Собирать записи блокировки событий",
29
+ )
30
+ retention_days: I18nKey = I18nKey(
31
+ default="Retention days (0 = keep forever)",
32
+ zh_CN="记录保留天数(0 永久保留,启动与每日清理)",
33
+ zh_TW="記錄保留天數(0 永久保留,啟動與每日清理)",
34
+ en="Retention days (0 = keep forever)",
35
+ ja="保持日数(0 で無期限保持)",
36
+ ru="Срок хранения в днях (0 = бессрочно)",
37
+ )
38
+ top_n: I18nKey = I18nKey(
39
+ default="Max entries shown in one report",
40
+ zh_CN="统计报表单次展示的最大条目数",
41
+ zh_TW="統計報表單次展示的最大條目數",
42
+ en="Max entries shown in one report",
43
+ ja="レポート1回の最大表示件数",
44
+ ru="Максимум записей в одном отчёте",
45
+ )
46
+ render_card: I18nKey = I18nKey(
47
+ default="Render reports as image cards via Takumi (fallback to text)",
48
+ zh_CN="报表用 Takumi 渲染为卡片(不可用时降级文本)",
49
+ zh_TW="報表用 Takumi 渲染為卡片(不可用時降級文本)",
50
+ en="Render reports as image cards via Takumi (fallback to text)",
51
+ ja="レポートを Takumi でカード画像化(不可時はテキスト)",
52
+ ru="Рендер отчётов в карточки через Takumi (иначе текст)",
53
+ )
54
+ theme: I18nKey = I18nKey(
55
+ default="Card color theme (auto = light/dark by time)",
56
+ zh_CN="卡片配色 (auto=按时间自动切深浅色)",
57
+ zh_TW="卡片配色 (auto=按時間自動切深淺色)",
58
+ en="Card color theme (auto = light/dark by time)",
59
+ ja="カード配色(auto=時間で自動切替)",
60
+ ru="Тема карточек (auto = светлая/тёмная по времени)",
61
+ )
62
+ module_loaded: I18nKey = I18nKey(
63
+ default="GovReport loaded: {event}",
64
+ zh_CN="治理观测已加载: {event}",
65
+ zh_TW="治理觀測已載入: {event}",
66
+ en="GovReport loaded: {event}",
67
+ ja="GovReport を読み込みました: {event}",
68
+ ru="GovReport загружен: {event}",
69
+ )
70
+ module_unloaded: I18nKey = I18nKey(
71
+ default="GovReport unloaded: {event}",
72
+ zh_CN="治理观测已卸载: {event}",
73
+ zh_TW="治理觀測已卸載: {event}",
74
+ en="GovReport unloaded: {event}",
75
+ ja="GovReport を解除しました: {event}",
76
+ ru="GovReport выгружен: {event}",
77
+ )
78
+ stats_header: I18nKey = I18nKey(
79
+ default="Command stats in last {window}: total {total}, success {ok}, failed {failed}, deprecated uses {dep}",
80
+ zh_CN="近 {window} 命令统计:共 {total} 次,成功 {ok},失败 {failed},废弃命令使用 {dep} 次",
81
+ zh_TW="近 {window} 命令統計:共 {total} 次,成功 {ok},失敗 {failed},廢棄命令使用 {dep} 次",
82
+ en="Command stats in last {window}: total {total}, success {ok}, failed {failed}, deprecated uses {dep}",
83
+ ja="直近 {window} のコマンド統計:合計 {total}、成功 {ok}、失敗 {failed}、非推奨使用 {dep}",
84
+ ru="Статистика команд за {window}: всего {total}, успешно {ok}, ошибок {failed}, устаревших {dep}",
85
+ )
86
+ stats_empty: I18nKey = I18nKey(
87
+ default="No command records in window {window}",
88
+ zh_CN="窗口 {window} 内没有命令记录",
89
+ zh_TW="視窗 {window} 內沒有命令記錄",
90
+ en="No command records in window {window}",
91
+ ja="期間 {window} にコマンド記録がありません",
92
+ ru="За период {window} записей команд нет",
93
+ )
94
+ user_header: I18nKey = I18nKey(
95
+ default="User {user} in last {window}: total {total}, success {ok}, failed {failed}",
96
+ zh_CN="用户 {user} 近 {window}:共 {total} 次,成功 {ok},失败 {failed}",
97
+ zh_TW="用戶 {user} 近 {window}:共 {total} 次,成功 {ok},失敗 {failed}",
98
+ en="User {user} in last {window}: total {total}, success {ok}, failed {failed}",
99
+ ja="ユーザー {user} 直近 {window}:合計 {total}、成功 {ok}、失敗 {failed}",
100
+ ru="Пользователь {user} за {window}: всего {total}, успешно {ok}, ошибок {failed}",
101
+ )
102
+ user_empty: I18nKey = I18nKey(
103
+ default="User {user} has no records in window {window}",
104
+ zh_CN="用户 {user} 在窗口 {window} 内没有记录",
105
+ zh_TW="用戶 {user} 在視窗 {window} 內沒有記錄",
106
+ en="User {user} has no records in window {window}",
107
+ ja="ユーザー {user} は期間 {window} に記録がありません",
108
+ ru="У пользователя {user} нет записей за {window}",
109
+ )
110
+ blocked_header: I18nKey = I18nKey(
111
+ default="Blocked events in last {window}: total {total}",
112
+ zh_CN="近 {window} 拦截统计:共 {total} 次",
113
+ zh_TW="近 {window} 攔截統計:共 {total} 次",
114
+ en="Blocked events in last {window}: total {total}",
115
+ ja="直近 {window} のブロック統計:合計 {total}",
116
+ ru="Блокировки за {window}: всего {total}",
117
+ )
118
+ blocked_empty: I18nKey = I18nKey(
119
+ default="No blocked events in window {window}",
120
+ zh_CN="窗口 {window} 内没有拦截记录",
121
+ zh_TW="視窗 {window} 內沒有攔截記錄",
122
+ en="No blocked events in window {window}",
123
+ ja="期間 {window} にブロック記録がありません",
124
+ ru="За период {window} блокировок нет",
125
+ )
126
+ usage_header: I18nKey = I18nKey(
127
+ default="Usage quota consumption ({count} active key(s)):",
128
+ zh_CN="配额消耗(活跃键 {count} 个):",
129
+ zh_TW="配額消耗(活躍鍵 {count} 個):",
130
+ en="Usage quota consumption ({count} active key(s)):",
131
+ ja="クォータ消費(アクティブキー {count} 件):",
132
+ ru="Потребление квот (активных ключей: {count}):",
133
+ )
134
+ usage_empty: I18nKey = I18nKey(
135
+ default="No usage quota records",
136
+ zh_CN="没有配额消耗记录",
137
+ zh_TW="沒有配額消耗記錄",
138
+ en="No usage quota records",
139
+ ja="クォータ消費記録がありません",
140
+ ru="Нет записей потребления квот",
141
+ )
142
+ gov_list_empty: I18nKey = I18nKey(
143
+ default="No command has governance declarations",
144
+ zh_CN="没有任何命令声明治理规则",
145
+ zh_TW="沒有任何命令聲明治理規則",
146
+ en="No command has governance declarations",
147
+ ja="治理宣言を持つコマンドはありません",
148
+ ru="Нет команд с правилами управления",
149
+ )
150
+ gov_list_header: I18nKey = I18nKey(
151
+ default="Governance declarations ({count} command(s)):",
152
+ zh_CN="治理声明清单({count} 个命令):",
153
+ zh_TW="治理聲明清單({count} 個命令):",
154
+ en="Governance declarations ({count} command(s)):",
155
+ ja="治理宣言一覧({count} 件):",
156
+ ru="Объявления управления ({count} команд):",
157
+ )
158
+ purge_done: I18nKey = I18nKey(
159
+ default="Purged {calls} call record(s) and {blocks} block record(s) older than {days} day(s)",
160
+ zh_CN="已清理 {days} 天前的记录:命令 {calls} 条,拦截 {blocks} 条",
161
+ zh_TW="已清理 {days} 天前的記錄:命令 {calls} 條,攔截 {blocks} 條",
162
+ en="Purged {calls} call record(s) and {blocks} block record(s) older than {days} day(s)",
163
+ ja="{days} 日より前の記録を削除:コマンド {calls} 件、ブロック {blocks} 件",
164
+ ru="Удалено записей старше {days} дн.: команд {calls}, блокировок {blocks}",
165
+ )
166
+ invalid_window: I18nKey = I18nKey(
167
+ default="Invalid window: {window} (examples: 30m / 24h / 7d)",
168
+ zh_CN="无效的时间窗口: {window}(示例: 30m / 24h / 7d)",
169
+ zh_TW="無效的時間視窗: {window}(示例: 30m / 24h / 7d)",
170
+ en="Invalid window: {window} (examples: 30m / 24h / 7d)",
171
+ ja="無効な期間指定: {window}(例: 30m / 24h / 7d)",
172
+ ru="Неверный период: {window} (примеры: 30m / 24h / 7d)",
173
+ )
@@ -0,0 +1,30 @@
1
+ from ErisPulse.Core.Bases import Field, Model
2
+
3
+
4
+ class CommandCall(Model):
5
+
6
+ __tablename__ = "gov_command_calls"
7
+
8
+ id: int = Field(primary_key=True, autoincrement=True)
9
+ ts: float = Field(index=True)
10
+ command: str = Field(max_length=128, index=True)
11
+ platform: str = Field(max_length=64, default="")
12
+ user_id: str = Field(max_length=128, default="", index=True)
13
+ success: bool = Field(default=True)
14
+ error: str = Field(max_length=255, default="")
15
+
16
+
17
+ class BlockEvent(Model):
18
+
19
+ __tablename__ = "gov_block_events"
20
+
21
+ id: int = Field(primary_key=True, autoincrement=True)
22
+ ts: float = Field(index=True)
23
+ source: str = Field(max_length=16, choices=["middleware", "scope"], index=True)
24
+ middleware: str = Field(max_length=128, default="")
25
+ dimension: str = Field(max_length=16, default="")
26
+ module: str = Field(max_length=128, default="")
27
+ platform: str = Field(max_length=64, default="")
28
+ detail_type: str = Field(max_length=64, default="")
29
+ user_id: str = Field(max_length=128, default="")
30
+ session_id: str = Field(max_length=128, default="")