@gakr-gakr/qqbot 0.1.0

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 (149) hide show
  1. package/api.ts +56 -0
  2. package/autobot.plugin.json +167 -0
  3. package/channel-plugin-api.ts +1 -0
  4. package/index.ts +33 -0
  5. package/package.json +64 -0
  6. package/runtime-api.ts +9 -0
  7. package/secret-contract-api.ts +5 -0
  8. package/setup-entry.ts +13 -0
  9. package/setup-plugin-api.ts +3 -0
  10. package/skills/qqbot-channel/SKILL.md +262 -0
  11. package/skills/qqbot-channel/references/api_references.md +521 -0
  12. package/skills/qqbot-media/SKILL.md +37 -0
  13. package/skills/qqbot-remind/SKILL.md +153 -0
  14. package/src/bridge/approval/capability.ts +225 -0
  15. package/src/bridge/approval/handler-runtime.ts +204 -0
  16. package/src/bridge/bootstrap.ts +135 -0
  17. package/src/bridge/channel-entry.ts +18 -0
  18. package/src/bridge/commands/framework-context-adapter.ts +60 -0
  19. package/src/bridge/commands/framework-registration.ts +66 -0
  20. package/src/bridge/commands/from-parser.ts +60 -0
  21. package/src/bridge/commands/result-dispatcher.ts +76 -0
  22. package/src/bridge/config-shared.ts +132 -0
  23. package/src/bridge/config.ts +176 -0
  24. package/src/bridge/gateway.ts +178 -0
  25. package/src/bridge/logger.ts +31 -0
  26. package/src/bridge/narrowing.ts +31 -0
  27. package/src/bridge/plugin-version.ts +102 -0
  28. package/src/bridge/runtime.ts +25 -0
  29. package/src/bridge/sdk-adapter.ts +164 -0
  30. package/src/bridge/setup/finalize.ts +144 -0
  31. package/src/bridge/setup/surface.ts +34 -0
  32. package/src/bridge/tools/channel.ts +58 -0
  33. package/src/bridge/tools/index.ts +15 -0
  34. package/src/bridge/tools/remind.ts +91 -0
  35. package/src/channel.setup.ts +33 -0
  36. package/src/channel.ts +399 -0
  37. package/src/config-schema.ts +84 -0
  38. package/src/engine/access/index.ts +2 -0
  39. package/src/engine/access/resolve-policy.ts +30 -0
  40. package/src/engine/access/sender-match.ts +55 -0
  41. package/src/engine/access/types.ts +2 -0
  42. package/src/engine/adapter/audio.port.ts +27 -0
  43. package/src/engine/adapter/commands.port.ts +22 -0
  44. package/src/engine/adapter/history.port.ts +52 -0
  45. package/src/engine/adapter/index.ts +76 -0
  46. package/src/engine/adapter/mention-gate.port.ts +50 -0
  47. package/src/engine/adapter/types.ts +38 -0
  48. package/src/engine/api/api-client.ts +212 -0
  49. package/src/engine/api/media-chunked.ts +644 -0
  50. package/src/engine/api/media.ts +218 -0
  51. package/src/engine/api/messages.ts +293 -0
  52. package/src/engine/api/retry.ts +217 -0
  53. package/src/engine/api/routes.ts +95 -0
  54. package/src/engine/api/token.ts +277 -0
  55. package/src/engine/approval/index.ts +224 -0
  56. package/src/engine/commands/builtin/log-helpers.ts +341 -0
  57. package/src/engine/commands/builtin/register-all.ts +17 -0
  58. package/src/engine/commands/builtin/register-approve.ts +201 -0
  59. package/src/engine/commands/builtin/register-basic.ts +95 -0
  60. package/src/engine/commands/builtin/register-clear-storage.ts +187 -0
  61. package/src/engine/commands/builtin/register-logs.ts +20 -0
  62. package/src/engine/commands/builtin/register-streaming.ts +138 -0
  63. package/src/engine/commands/builtin/state.ts +31 -0
  64. package/src/engine/commands/slash-command-auth.ts +88 -0
  65. package/src/engine/commands/slash-command-handler.ts +168 -0
  66. package/src/engine/commands/slash-command-test-support.ts +39 -0
  67. package/src/engine/commands/slash-commands-impl.ts +61 -0
  68. package/src/engine/commands/slash-commands.ts +202 -0
  69. package/src/engine/config/credential-backup.ts +108 -0
  70. package/src/engine/config/credentials.ts +76 -0
  71. package/src/engine/config/group.ts +227 -0
  72. package/src/engine/config/resolve.ts +283 -0
  73. package/src/engine/config/setup-logic.ts +84 -0
  74. package/src/engine/gateway/active-cfg.ts +52 -0
  75. package/src/engine/gateway/codec.ts +47 -0
  76. package/src/engine/gateway/constants.ts +117 -0
  77. package/src/engine/gateway/event-dispatcher.ts +177 -0
  78. package/src/engine/gateway/gateway-connection.ts +356 -0
  79. package/src/engine/gateway/gateway.ts +267 -0
  80. package/src/engine/gateway/inbound-attachments.ts +360 -0
  81. package/src/engine/gateway/inbound-context.ts +82 -0
  82. package/src/engine/gateway/inbound-pipeline.ts +171 -0
  83. package/src/engine/gateway/interaction-handler.ts +345 -0
  84. package/src/engine/gateway/message-queue.ts +404 -0
  85. package/src/engine/gateway/outbound-dispatch.ts +590 -0
  86. package/src/engine/gateway/reconnect.ts +199 -0
  87. package/src/engine/gateway/stages/access-stage.ts +99 -0
  88. package/src/engine/gateway/stages/assembly-stage.ts +156 -0
  89. package/src/engine/gateway/stages/content-stage.ts +77 -0
  90. package/src/engine/gateway/stages/envelope-stage.ts +144 -0
  91. package/src/engine/gateway/stages/group-gate-stage.ts +223 -0
  92. package/src/engine/gateway/stages/index.ts +18 -0
  93. package/src/engine/gateway/stages/quote-stage.ts +113 -0
  94. package/src/engine/gateway/stages/refidx-stage.ts +62 -0
  95. package/src/engine/gateway/stages/stub-contexts.ts +77 -0
  96. package/src/engine/gateway/types.ts +230 -0
  97. package/src/engine/gateway/typing-keepalive.ts +102 -0
  98. package/src/engine/gateway/ws-client.ts +16 -0
  99. package/src/engine/group/activation.ts +88 -0
  100. package/src/engine/group/history.ts +321 -0
  101. package/src/engine/group/mention.ts +114 -0
  102. package/src/engine/group/message-gating.ts +108 -0
  103. package/src/engine/messaging/decode-media-path.ts +82 -0
  104. package/src/engine/messaging/media-source.ts +210 -0
  105. package/src/engine/messaging/media-type-detect.ts +27 -0
  106. package/src/engine/messaging/outbound-audio-port.ts +38 -0
  107. package/src/engine/messaging/outbound-deliver.ts +810 -0
  108. package/src/engine/messaging/outbound-media-send.ts +658 -0
  109. package/src/engine/messaging/outbound-reply.ts +27 -0
  110. package/src/engine/messaging/outbound-result-helpers.ts +54 -0
  111. package/src/engine/messaging/outbound-types.ts +47 -0
  112. package/src/engine/messaging/outbound.ts +485 -0
  113. package/src/engine/messaging/reply-dispatcher.ts +597 -0
  114. package/src/engine/messaging/reply-limiter.ts +164 -0
  115. package/src/engine/messaging/sender.ts +741 -0
  116. package/src/engine/messaging/streaming-c2c.ts +1192 -0
  117. package/src/engine/messaging/streaming-media-send.ts +544 -0
  118. package/src/engine/messaging/target-parser.ts +104 -0
  119. package/src/engine/ref/format-message-ref.ts +142 -0
  120. package/src/engine/ref/format-ref-entry.ts +27 -0
  121. package/src/engine/ref/store.ts +211 -0
  122. package/src/engine/ref/types.ts +27 -0
  123. package/src/engine/session/known-users.ts +138 -0
  124. package/src/engine/session/session-store.ts +207 -0
  125. package/src/engine/tools/channel-api.ts +244 -0
  126. package/src/engine/tools/remind-logic.ts +377 -0
  127. package/src/engine/types.ts +313 -0
  128. package/src/engine/utils/attachment-tags.ts +174 -0
  129. package/src/engine/utils/audio.ts +525 -0
  130. package/src/engine/utils/data-paths.ts +38 -0
  131. package/src/engine/utils/diagnostics.ts +93 -0
  132. package/src/engine/utils/file-utils.ts +215 -0
  133. package/src/engine/utils/format.ts +70 -0
  134. package/src/engine/utils/image-size.ts +249 -0
  135. package/src/engine/utils/log.ts +77 -0
  136. package/src/engine/utils/media-tags.ts +177 -0
  137. package/src/engine/utils/payload.ts +157 -0
  138. package/src/engine/utils/platform.ts +265 -0
  139. package/src/engine/utils/request-context.ts +60 -0
  140. package/src/engine/utils/string-normalize.ts +91 -0
  141. package/src/engine/utils/stt.ts +103 -0
  142. package/src/engine/utils/text-parsing.ts +155 -0
  143. package/src/engine/utils/upload-cache.ts +96 -0
  144. package/src/engine/utils/voice-text.ts +15 -0
  145. package/src/exec-approvals.ts +237 -0
  146. package/src/qqbot-test-support.ts +29 -0
  147. package/src/secret-contract.ts +82 -0
  148. package/src/types.ts +210 -0
  149. package/tsconfig.json +16 -0
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Passive reply limiter — enforce per-message reply count and TTL limits.
3
+ *
4
+ * QQ Bot restricts how many passive replies can be sent in response to a
5
+ * single inbound message (4 per hour by default). This module tracks reply
6
+ * counts and determines whether the next reply should be passive or
7
+ * fall back to proactive mode.
8
+ *
9
+ * The module is a **class** with zero I/O dependencies, fully supporting
10
+ * multi-account concurrent operation via separate instances.
11
+ */
12
+
13
+ /** Configuration for the reply limiter. */
14
+ interface ReplyLimiterConfig {
15
+ /** Maximum passive replies per message. Defaults to 4. */
16
+ limit?: number;
17
+ /** TTL in milliseconds for the passive reply window. Defaults to 1 hour. */
18
+ ttlMs?: number;
19
+ /** Maximum number of tracked messages before eviction. Defaults to 10000. */
20
+ maxTrackedMessages?: number;
21
+ }
22
+
23
+ /** Result of a passive-reply limit check. */
24
+ export interface ReplyLimitResult {
25
+ /** Whether a passive reply is still allowed. */
26
+ allowed: boolean;
27
+ /** Number of remaining passive replies. */
28
+ remaining: number;
29
+ /** Whether the caller should fall back to proactive mode. */
30
+ shouldFallbackToProactive: boolean;
31
+ /** Reason for the fallback. */
32
+ fallbackReason?: "expired" | "limit_exceeded";
33
+ /** Human-readable diagnostic message. */
34
+ message?: string;
35
+ }
36
+
37
+ interface ReplyRecord {
38
+ count: number;
39
+ firstReplyAt: number;
40
+ }
41
+
42
+ const DEFAULT_LIMIT = 4;
43
+ const DEFAULT_TTL_MS = 60 * 60 * 1000;
44
+ const DEFAULT_MAX_TRACKED = 10_000;
45
+
46
+ /**
47
+ * Per-account reply limiter with automatic eviction.
48
+ *
49
+ * Usage:
50
+ * ```ts
51
+ * const limiter = new ReplyLimiter({ limit: 4, ttlMs: 3600000 });
52
+ * const check = limiter.checkLimit(messageId);
53
+ * if (check.allowed) {
54
+ * await sendPassiveReply(...);
55
+ * limiter.record(messageId);
56
+ * } else if (check.shouldFallbackToProactive) {
57
+ * await sendProactiveMessage(...);
58
+ * }
59
+ * ```
60
+ */
61
+ export class ReplyLimiter {
62
+ private readonly limit: number;
63
+ private readonly ttlMs: number;
64
+ private readonly maxTracked: number;
65
+ private readonly tracker = new Map<string, ReplyRecord>();
66
+
67
+ constructor(config?: ReplyLimiterConfig) {
68
+ this.limit = config?.limit ?? DEFAULT_LIMIT;
69
+ this.ttlMs = config?.ttlMs ?? DEFAULT_TTL_MS;
70
+ this.maxTracked = config?.maxTrackedMessages ?? DEFAULT_MAX_TRACKED;
71
+ }
72
+
73
+ /** Check whether a passive reply is allowed for the given message. */
74
+ checkLimit(messageId: string): ReplyLimitResult {
75
+ const now = Date.now();
76
+ this.evictIfNeeded(now);
77
+
78
+ const record = this.tracker.get(messageId);
79
+
80
+ if (!record) {
81
+ return {
82
+ allowed: true,
83
+ remaining: this.limit,
84
+ shouldFallbackToProactive: false,
85
+ };
86
+ }
87
+
88
+ if (now - record.firstReplyAt > this.ttlMs) {
89
+ return {
90
+ allowed: false,
91
+ remaining: 0,
92
+ shouldFallbackToProactive: true,
93
+ fallbackReason: "expired",
94
+ message: `Message is older than ${this.ttlMs / (60 * 60 * 1000)}h; sending as a proactive message instead`,
95
+ };
96
+ }
97
+
98
+ const remaining = this.limit - record.count;
99
+ if (remaining <= 0) {
100
+ return {
101
+ allowed: false,
102
+ remaining: 0,
103
+ shouldFallbackToProactive: true,
104
+ fallbackReason: "limit_exceeded",
105
+ message: `Passive reply limit reached (${this.limit} per hour); sending proactively instead`,
106
+ };
107
+ }
108
+
109
+ return {
110
+ allowed: true,
111
+ remaining,
112
+ shouldFallbackToProactive: false,
113
+ };
114
+ }
115
+
116
+ /** Record one passive reply against a message. */
117
+ record(messageId: string): void {
118
+ const now = Date.now();
119
+ const existing = this.tracker.get(messageId);
120
+
121
+ if (!existing) {
122
+ this.tracker.set(messageId, { count: 1, firstReplyAt: now });
123
+ } else if (now - existing.firstReplyAt > this.ttlMs) {
124
+ this.tracker.set(messageId, { count: 1, firstReplyAt: now });
125
+ } else {
126
+ existing.count++;
127
+ }
128
+ }
129
+
130
+ /** Return diagnostic stats. */
131
+ getStats(): { trackedMessages: number; totalReplies: number } {
132
+ let totalReplies = 0;
133
+ for (const record of this.tracker.values()) {
134
+ totalReplies += record.count;
135
+ }
136
+ return { trackedMessages: this.tracker.size, totalReplies };
137
+ }
138
+
139
+ /** Return limiter configuration. */
140
+ getConfig(): { limit: number; ttlMs: number; ttlHours: number } {
141
+ return {
142
+ limit: this.limit,
143
+ ttlMs: this.ttlMs,
144
+ ttlHours: this.ttlMs / (60 * 60 * 1000),
145
+ };
146
+ }
147
+
148
+ /** Clear all tracked records. */
149
+ clear(): void {
150
+ this.tracker.clear();
151
+ }
152
+
153
+ /** Opportunistically evict expired records to keep the tracker bounded. */
154
+ private evictIfNeeded(now: number): void {
155
+ if (this.tracker.size <= this.maxTracked) {
156
+ return;
157
+ }
158
+ for (const [id, rec] of this.tracker) {
159
+ if (now - rec.firstReplyAt > this.ttlMs) {
160
+ this.tracker.delete(id);
161
+ }
162
+ }
163
+ }
164
+ }