@openclaw/buzz 2026.9.6 → 2026.9.7
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.
- package/assets/icon.png +0 -0
- package/dist/.setup/{adapter.runtime-FIC9NMFI.mjs → adapter.runtime-Bpx9a0g6.mjs} +2 -2
- package/dist/.setup/{channel-qV8kQiPi.mjs → channel-B7r1381j.mjs} +86 -243
- package/dist/.setup/directory-config-M9KSexdh.mjs +24 -0
- package/dist/.setup/directory-query-D1BNu8nO.mjs +22 -0
- package/dist/.setup/room-membership-7of91SyC.mjs +537 -0
- package/dist/.setup/{secret-contract-DjibkTDL.mjs → secret-contract-B2-Mrm4v.mjs} +1 -1
- package/dist/.setup/{secret-contract-api-DcFDI-eR.mjs → secret-contract-api-CfJYRXZf.mjs} +1 -1
- package/dist/.setup/setup-plugin-api-CtM1M1Oa.mjs +2 -0
- package/dist/.setup/{setup-plugin-api-DI8WfvKQ.mjs → setup-plugin-api-m1pk2Cow.mjs} +2 -2
- package/dist/.setup/{setup-surface-DOHxdPor.mjs → setup-surface-CVXY6_50.mjs} +10 -24
- package/dist/.setup/{types-CeupBG0P.mjs → types-DRUZMyTx.mjs} +10 -2
- package/dist/api.js +2 -2
- package/dist/channel-plugin-api.js +1 -1
- package/dist/directory-contract-api.js +1 -1
- package/dist/qa-runner-api.js +1 -1
- package/dist/secret-contract-api.js +1 -1
- package/dist/setup-entry.js +2 -2
- package/dist/setup-plugin-api.js +1 -1
- package/openclaw.plugin.json +19 -3
- package/package.json +4 -4
- package/dist/.setup/directory-config-CzHz6fzo.mjs +0 -43
- package/dist/.setup/room-membership-tqLQznFE.mjs +0 -276
- package/dist/.setup/setup-plugin-api-H-XWBzaF.mjs +0 -2
- package/dist/runtime-api.js +0 -1
package/assets/icon.png
CHANGED
|
Binary file
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { m as parseBuzzTarget, n as decodeBuzzPrivateKey, o as resolveBuzzPublicKey, u as buildBuzzTarget } from "./types-
|
|
2
|
-
import { c as openBuzzRelaySubscription, i as parseBuzzRoomMembershipEvent, o as connectAuthenticatedBuzzRelaySession, s as parseBuzzAuthTag, t as BUZZ_ROOM_MEMBERSHIP_KIND
|
|
1
|
+
import { m as parseBuzzTarget, n as decodeBuzzPrivateKey, o as resolveBuzzPublicKey, u as buildBuzzTarget } from "./types-DRUZMyTx.mjs";
|
|
2
|
+
import { c as openBuzzRelaySubscription, i as parseBuzzRoomMembershipEvent, m as isNewerBuzzRevision, o as connectAuthenticatedBuzzRelaySession, s as parseBuzzAuthTag, t as BUZZ_ROOM_MEMBERSHIP_KIND } from "./room-membership-7of91SyC.mjs";
|
|
3
3
|
import { i as buildBuzzMessageTags, s as parseBuzzMessageEvent } from "./message-event-X_aBiM1X.mjs";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { finalizeEvent } from "nostr-tools";
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { a as resolveBuzzAccountConfig, c as BuzzConfigSchema, d as isConfiguredBuzzChannel, f as looksLikeBuzzTarget, i as resolveBuzzAccount, m as parseBuzzTarget, n as decodeBuzzPrivateKey, o as resolveBuzzPublicKey, p as normalizeBuzzTarget, r as listBuzzAccountIds, s as resolveDefaultBuzzAccountId, t as assertBuzzAccountAvailable, u as buildBuzzTarget } from "./types-
|
|
2
|
-
import { n as listBuzzDirectoryPeersFromConfig,
|
|
3
|
-
import { a as connectAuthenticatedBuzzRelay, c as openBuzzRelaySubscription, i as parseBuzzRoomMembershipEvent, l as queryBuzzRelaySnapshot, n as BUZZ_ROOM_SYSTEM_KIND, o as connectAuthenticatedBuzzRelaySession, r as parseBuzzRoomMembershipChangeEvent, s as parseBuzzAuthTag, t as BUZZ_ROOM_MEMBERSHIP_KIND, u as
|
|
1
|
+
import { a as resolveBuzzAccountConfig, c as BuzzConfigSchema, d as isConfiguredBuzzChannel, f as looksLikeBuzzTarget, i as resolveBuzzAccount, m as parseBuzzTarget, n as decodeBuzzPrivateKey, o as resolveBuzzPublicKey, p as normalizeBuzzTarget, r as listBuzzAccountIds, s as resolveDefaultBuzzAccountId, t as assertBuzzAccountAvailable, u as buildBuzzTarget } from "./types-DRUZMyTx.mjs";
|
|
2
|
+
import { n as listBuzzDirectoryPeersFromConfig, t as listBuzzDirectoryGroupsFromConfig } from "./directory-config-M9KSexdh.mjs";
|
|
3
|
+
import { a as connectAuthenticatedBuzzRelay, c as openBuzzRelaySubscription, d as resolveBuzzSubscriptionBudget, f as BUZZ_ROOM_METADATA_KIND, i as parseBuzzRoomMembershipEvent, l as queryBuzzRelaySnapshot, m as isNewerBuzzRevision, n as BUZZ_ROOM_SYSTEM_KIND, o as connectAuthenticatedBuzzRelaySession, p as BuzzDirectoryState, r as parseBuzzRoomMembershipChangeEvent, s as parseBuzzAuthTag, t as BUZZ_ROOM_MEMBERSHIP_KIND, u as BUZZ_MAX_CONFIGURED_ROOMS } from "./room-membership-7of91SyC.mjs";
|
|
4
4
|
import { a as formatBuzzMessageForAgent, c as inspectBuzzMentionSyntax, i as buildBuzzMessageTags, l as resolveBuzzMessageMentions, n as BUZZ_INBOUND_MESSAGE_KINDS, o as isBuzzInboundMessageKind, r as BUZZ_TYPING_INDICATOR_KIND, s as parseBuzzMessageEvent, t as BUZZ_DIFF_MESSAGE_KIND } from "./message-event-X_aBiM1X.mjs";
|
|
5
|
-
import { i as startBuzzRoomMembershipNotifications, n as buzzSetupContract, r as discoverBuzzRooms, t as buzzSetupWizard } from "./setup-surface-
|
|
6
|
-
import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-
|
|
5
|
+
import { i as startBuzzRoomMembershipNotifications, n as buzzSetupContract, r as discoverBuzzRooms, t as buzzSetupWizard } from "./setup-surface-CVXY6_50.mjs";
|
|
6
|
+
import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-B2-Mrm4v.mjs";
|
|
7
7
|
import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
|
|
8
8
|
import { buildChannelOutboundSessionRoute, buildThreadAwareOutboundSessionRoute, createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
|
|
9
9
|
import { createChannelMessageAdapterFromOutbound, waitUntilAbort } from "openclaw/plugin-sdk/channel-outbound";
|
|
10
10
|
import { createChannelDirectoryAdapter } from "openclaw/plugin-sdk/directory-runtime";
|
|
11
11
|
import { parseThreadSessionSuffix } from "openclaw/plugin-sdk/routing";
|
|
12
12
|
import { createComputedAccountStatusAdapter, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
|
|
13
|
-
import { compareEvents, finalizeEvent } from "nostr-tools";
|
|
13
|
+
import { compareEvents, finalizeEvent, verifyEvent } from "nostr-tools";
|
|
14
14
|
import { chunkItems } from "openclaw/plugin-sdk/text-chunking";
|
|
15
|
-
import {
|
|
15
|
+
import { truncateUtf8Prefix } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
16
16
|
import { attachChannelToResult } from "openclaw/plugin-sdk/channel-send-result";
|
|
17
17
|
import { channelReadyPatch } from "openclaw/plugin-sdk/gateway-runtime";
|
|
18
18
|
import { computeBackoff, sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
|
|
@@ -23,218 +23,6 @@ import { createSubsystemLogger } from "openclaw/plugin-sdk/logging-core";
|
|
|
23
23
|
import { buildHistoryContext, createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
|
|
24
24
|
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
|
25
25
|
import { createHash } from "node:crypto";
|
|
26
|
-
const BUZZ_ROOM_METADATA_KIND = 39e3;
|
|
27
|
-
const DEFAULT_BUZZ_DIRECTORY_PROFILE_LIMIT = 2e3;
|
|
28
|
-
const HEX_PUBLIC_KEY_PATTERN = /^[0-9a-f]{64}$/u;
|
|
29
|
-
const MAX_DIRECTORY_NAME_CHARS = 512;
|
|
30
|
-
const MAX_DIRECTORY_HANDLE_CHARS = 320;
|
|
31
|
-
const MAX_DIRECTORY_URL_CHARS = 4096;
|
|
32
|
-
function normalizeBoundedString(value, maxChars) {
|
|
33
|
-
if (typeof value !== "string") return;
|
|
34
|
-
const trimmed = value.trim();
|
|
35
|
-
if (!trimmed) return;
|
|
36
|
-
return truncateUtf16Safe(trimmed, maxChars);
|
|
37
|
-
}
|
|
38
|
-
function readPreferredString(params) {
|
|
39
|
-
if (Object.hasOwn(params.content, params.primary)) return normalizeBoundedString(params.content[params.primary], params.maxChars);
|
|
40
|
-
return normalizeBoundedString(params.content[params.fallback], params.maxChars);
|
|
41
|
-
}
|
|
42
|
-
function fallbackPublicKeyLabel(publicKey) {
|
|
43
|
-
return `${publicKey.slice(0, 8)}...${publicKey.slice(-6)}`;
|
|
44
|
-
}
|
|
45
|
-
function parseBuzzDirectoryProfileEvent(event) {
|
|
46
|
-
const publicKey = event.pubkey.trim().toLowerCase();
|
|
47
|
-
if (event.kind !== 0 || !HEX_PUBLIC_KEY_PATTERN.test(publicKey)) return;
|
|
48
|
-
let content;
|
|
49
|
-
try {
|
|
50
|
-
const parsed = JSON.parse(event.content);
|
|
51
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return;
|
|
52
|
-
content = parsed;
|
|
53
|
-
} catch {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
return {
|
|
57
|
-
publicKey,
|
|
58
|
-
displayName: readPreferredString({
|
|
59
|
-
content,
|
|
60
|
-
primary: "display_name",
|
|
61
|
-
fallback: "name",
|
|
62
|
-
maxChars: MAX_DIRECTORY_NAME_CHARS
|
|
63
|
-
}),
|
|
64
|
-
handle: normalizeBoundedString(content.nip05, MAX_DIRECTORY_HANDLE_CHARS),
|
|
65
|
-
avatarUrl: readPreferredString({
|
|
66
|
-
content,
|
|
67
|
-
primary: "picture",
|
|
68
|
-
fallback: "image",
|
|
69
|
-
maxChars: MAX_DIRECTORY_URL_CHARS
|
|
70
|
-
}),
|
|
71
|
-
createdAt: event.created_at,
|
|
72
|
-
eventId: event.id
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
function parseBuzzDirectoryRoomEvent(event) {
|
|
76
|
-
if (event.kind !== 39e3) return;
|
|
77
|
-
const roomId = event.tags.find((tag) => tag[0] === "d")?.[1]?.trim().toLowerCase();
|
|
78
|
-
if (!roomId) return;
|
|
79
|
-
try {
|
|
80
|
-
parseBuzzTarget(roomId);
|
|
81
|
-
} catch {
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
return {
|
|
85
|
-
roomId,
|
|
86
|
-
name: normalizeBoundedString(event.tags.find((tag) => tag[0] === "name")?.[1], MAX_DIRECTORY_NAME_CHARS),
|
|
87
|
-
archived: event.tags.some((tag) => tag[0] === "archived" && tag[1] === "true"),
|
|
88
|
-
createdAt: event.created_at,
|
|
89
|
-
eventId: event.id
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
var BuzzDirectoryState = class {
|
|
93
|
-
#publicKey;
|
|
94
|
-
#fallbackProfileName;
|
|
95
|
-
#configuredRoomIds;
|
|
96
|
-
#profileLimit;
|
|
97
|
-
#memberships = /* @__PURE__ */ new Map();
|
|
98
|
-
#profilePublicKeys = /* @__PURE__ */ new Set();
|
|
99
|
-
#profiles = /* @__PURE__ */ new Map();
|
|
100
|
-
#rooms = /* @__PURE__ */ new Map();
|
|
101
|
-
constructor(params) {
|
|
102
|
-
this.#publicKey = params.publicKey.trim().toLowerCase();
|
|
103
|
-
this.#fallbackProfileName = params.fallbackProfileName.trim() || "OpenClaw";
|
|
104
|
-
this.#configuredRoomIds = new Set(params.channelIds.map(parseBuzzTarget));
|
|
105
|
-
const requestedProfileLimit = params.profileLimit ?? DEFAULT_BUZZ_DIRECTORY_PROFILE_LIMIT;
|
|
106
|
-
this.#profileLimit = Number.isFinite(requestedProfileLimit) && requestedProfileLimit >= 0 ? Math.floor(requestedProfileLimit) : DEFAULT_BUZZ_DIRECTORY_PROFILE_LIMIT;
|
|
107
|
-
if (this.#profileLimit > 0) this.#profilePublicKeys.add(this.#publicKey);
|
|
108
|
-
}
|
|
109
|
-
replaceMemberships(memberships) {
|
|
110
|
-
const nextMemberships = /* @__PURE__ */ new Map();
|
|
111
|
-
const memberPublicKeys = /* @__PURE__ */ new Set();
|
|
112
|
-
for (const roomId of this.#configuredRoomIds) {
|
|
113
|
-
const membership = memberships.get(roomId);
|
|
114
|
-
if (!membership) continue;
|
|
115
|
-
nextMemberships.set(roomId, membership);
|
|
116
|
-
for (const publicKey of membership.members) memberPublicKeys.add(publicKey);
|
|
117
|
-
}
|
|
118
|
-
memberPublicKeys.delete(this.#publicKey);
|
|
119
|
-
const nextProfilePublicKeys = this.#profileLimit === 0 ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([this.#publicKey, ...[...memberPublicKeys].toSorted().slice(0, this.#profileLimit - 1)]);
|
|
120
|
-
const profileSelectionChanged = nextProfilePublicKeys.size !== this.#profilePublicKeys.size || [...nextProfilePublicKeys].some((publicKey) => !this.#profilePublicKeys.has(publicKey));
|
|
121
|
-
this.#memberships = nextMemberships;
|
|
122
|
-
this.#profilePublicKeys = nextProfilePublicKeys;
|
|
123
|
-
for (const publicKey of this.#profiles.keys()) if (!nextProfilePublicKeys.has(publicKey)) this.#profiles.delete(publicKey);
|
|
124
|
-
return profileSelectionChanged;
|
|
125
|
-
}
|
|
126
|
-
profilePublicKeys() {
|
|
127
|
-
return [...this.#profilePublicKeys];
|
|
128
|
-
}
|
|
129
|
-
activeRoomIds() {
|
|
130
|
-
return [...this.#configuredRoomIds].filter((roomId) => !this.#rooms.get(roomId)?.archived);
|
|
131
|
-
}
|
|
132
|
-
isRoomArchived(roomId) {
|
|
133
|
-
return this.#rooms.get(parseBuzzTarget(roomId))?.archived === true;
|
|
134
|
-
}
|
|
135
|
-
isBotMember(roomId, publicKey) {
|
|
136
|
-
return this.isMember(roomId, publicKey) && this.#memberships.get(parseBuzzTarget(roomId))?.roles.get(publicKey) === "bot";
|
|
137
|
-
}
|
|
138
|
-
isMember(roomId, publicKey) {
|
|
139
|
-
const normalizedRoomId = parseBuzzTarget(roomId);
|
|
140
|
-
return !this.#rooms.get(normalizedRoomId)?.archived && this.#memberships.get(normalizedRoomId)?.members.has(publicKey) === true;
|
|
141
|
-
}
|
|
142
|
-
applyProfileEvent(event) {
|
|
143
|
-
const profile = parseBuzzDirectoryProfileEvent(event);
|
|
144
|
-
if (!profile || !this.#profilePublicKeys.has(profile.publicKey) || !isNewerBuzzRevision(profile, this.#profiles.get(profile.publicKey))) return false;
|
|
145
|
-
this.#profiles.set(profile.publicKey, profile);
|
|
146
|
-
return true;
|
|
147
|
-
}
|
|
148
|
-
applyRoomEvent(event) {
|
|
149
|
-
const room = parseBuzzDirectoryRoomEvent(event);
|
|
150
|
-
if (!room || !this.#configuredRoomIds.has(room.roomId) || !isNewerBuzzRevision(room, this.#rooms.get(room.roomId))) return false;
|
|
151
|
-
this.#rooms.set(room.roomId, room);
|
|
152
|
-
return true;
|
|
153
|
-
}
|
|
154
|
-
resolveSenderName(publicKey) {
|
|
155
|
-
const normalized = publicKey.trim().toLowerCase();
|
|
156
|
-
return this.#profiles.get(normalized)?.displayName ?? fallbackPublicKeyLabel(normalized);
|
|
157
|
-
}
|
|
158
|
-
resolveRoomName(roomId) {
|
|
159
|
-
const normalized = parseBuzzTarget(roomId);
|
|
160
|
-
return this.#rooms.get(normalized)?.name ?? normalized;
|
|
161
|
-
}
|
|
162
|
-
self() {
|
|
163
|
-
return this.#buildUserEntry(this.#publicKey);
|
|
164
|
-
}
|
|
165
|
-
listPeers(params) {
|
|
166
|
-
const peers = /* @__PURE__ */ new Set();
|
|
167
|
-
for (const roomId of this.activeRoomIds()) {
|
|
168
|
-
const membership = this.#memberships.get(roomId);
|
|
169
|
-
if (!membership) continue;
|
|
170
|
-
for (const publicKey of membership.members) if (publicKey !== this.#publicKey) peers.add(publicKey);
|
|
171
|
-
}
|
|
172
|
-
const entries = [...peers].map((publicKey) => this.#buildUserEntry(publicKey)).toSorted(compareDirectoryEntries);
|
|
173
|
-
return applyBuzzDirectoryQueryAndLimit(entries, params);
|
|
174
|
-
}
|
|
175
|
-
listGroups(params) {
|
|
176
|
-
const entries = this.activeRoomIds().map((roomId) => this.#buildRoomEntry(roomId)).toSorted(compareDirectoryEntries);
|
|
177
|
-
return applyBuzzDirectoryQueryAndLimit(entries, params);
|
|
178
|
-
}
|
|
179
|
-
listGroupMembers(params) {
|
|
180
|
-
let roomId;
|
|
181
|
-
try {
|
|
182
|
-
roomId = parseBuzzTarget(params.groupId);
|
|
183
|
-
} catch {
|
|
184
|
-
return [];
|
|
185
|
-
}
|
|
186
|
-
if (this.#rooms.get(roomId)?.archived) return [];
|
|
187
|
-
const membership = this.#memberships.get(roomId);
|
|
188
|
-
if (!membership) return [];
|
|
189
|
-
const entries = [...membership.members].map((publicKey) => {
|
|
190
|
-
const entry = this.#buildUserEntry(publicKey);
|
|
191
|
-
entry.raw = {
|
|
192
|
-
publicKey,
|
|
193
|
-
role: membership.roles.get(publicKey),
|
|
194
|
-
roomId
|
|
195
|
-
};
|
|
196
|
-
return entry;
|
|
197
|
-
}).toSorted(compareDirectoryEntries);
|
|
198
|
-
return applyBuzzDirectoryQueryAndLimit(entries, { limit: params.limit });
|
|
199
|
-
}
|
|
200
|
-
mentionMembers(roomId) {
|
|
201
|
-
const normalized = parseBuzzTarget(roomId);
|
|
202
|
-
if (this.#rooms.get(normalized)?.archived) return;
|
|
203
|
-
const membership = this.#memberships.get(normalized);
|
|
204
|
-
if (!membership) return;
|
|
205
|
-
return [...membership.members].map((publicKey) => ({
|
|
206
|
-
publicKey,
|
|
207
|
-
displayName: this.#profiles.get(publicKey)?.displayName
|
|
208
|
-
})).toSorted((left, right) => left.publicKey.localeCompare(right.publicKey));
|
|
209
|
-
}
|
|
210
|
-
#buildUserEntry(publicKey) {
|
|
211
|
-
const profile = this.#profiles.get(publicKey);
|
|
212
|
-
return {
|
|
213
|
-
kind: "user",
|
|
214
|
-
id: publicKey,
|
|
215
|
-
name: profile?.displayName ?? (publicKey === this.#publicKey ? this.#fallbackProfileName : fallbackPublicKeyLabel(publicKey)),
|
|
216
|
-
handle: profile?.handle,
|
|
217
|
-
avatarUrl: profile?.avatarUrl,
|
|
218
|
-
raw: { publicKey }
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
#buildRoomEntry(roomId) {
|
|
222
|
-
const room = this.#rooms.get(roomId);
|
|
223
|
-
return {
|
|
224
|
-
kind: "group",
|
|
225
|
-
id: buildBuzzTarget(roomId),
|
|
226
|
-
name: room?.name ?? roomId,
|
|
227
|
-
handle: room?.name ? `#${room.name}` : void 0,
|
|
228
|
-
raw: { roomId }
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
};
|
|
232
|
-
function compareDirectoryEntries(a, b) {
|
|
233
|
-
const aLabel = a.name ?? a.handle ?? a.id;
|
|
234
|
-
const bLabel = b.name ?? b.handle ?? b.id;
|
|
235
|
-
return aLabel.localeCompare(bLabel) || a.id.localeCompare(b.id);
|
|
236
|
-
}
|
|
237
|
-
//#endregion
|
|
238
26
|
//#region extensions/buzz/src/directory-relay.ts
|
|
239
27
|
const BUZZ_ROOM_QUERY_CHUNK_SIZE = 1e3;
|
|
240
28
|
const PROFILE_SUBSCRIPTION_REPLACED_REASON = "directory profile subscription replaced";
|
|
@@ -752,7 +540,7 @@ async function drainBuzzRoomHistoryRange(params) {
|
|
|
752
540
|
skipEventIds: params.skipEventIds,
|
|
753
541
|
signal: params.signal
|
|
754
542
|
});
|
|
755
|
-
if (!page.overLimit) {
|
|
543
|
+
if (!page.overLimit || params.since === params.until) {
|
|
756
544
|
if (page.events.length === 0) return "complete";
|
|
757
545
|
const reservation = await params.reserveCapacity(page.events.length);
|
|
758
546
|
if (!reservation) return "aborted";
|
|
@@ -761,17 +549,7 @@ async function drainBuzzRoomHistoryRange(params) {
|
|
|
761
549
|
} finally {
|
|
762
550
|
reservation.release();
|
|
763
551
|
}
|
|
764
|
-
return "complete";
|
|
765
|
-
}
|
|
766
|
-
if (params.since === params.until) {
|
|
767
|
-
const reservation = await params.reserveCapacity(page.events.length);
|
|
768
|
-
if (!reservation) return "aborted";
|
|
769
|
-
try {
|
|
770
|
-
for (const event of page.events) params.onEvent(event, reservation);
|
|
771
|
-
} finally {
|
|
772
|
-
reservation.release();
|
|
773
|
-
}
|
|
774
|
-
return "timestamp-over-limit";
|
|
552
|
+
return page.overLimit ? "timestamp-over-limit" : "complete";
|
|
775
553
|
}
|
|
776
554
|
const midpoint = Math.floor((params.since + params.until) / 2);
|
|
777
555
|
const newer = await drainBuzzRoomHistoryRange({
|
|
@@ -1216,16 +994,6 @@ async function createBuzzRoomMembershipTracker(params) {
|
|
|
1216
994
|
};
|
|
1217
995
|
}
|
|
1218
996
|
//#endregion
|
|
1219
|
-
//#region extensions/buzz/src/subscription-budget.ts
|
|
1220
|
-
const BUZZ_DIRECTORY_MAX_PROFILE_SUBSCRIPTIONS = 10;
|
|
1221
|
-
const BUZZ_MAX_CONFIGURED_ROOMS = 1020;
|
|
1222
|
-
function resolveBuzzSubscriptionBudget(roomCount) {
|
|
1223
|
-
if (!Number.isSafeInteger(roomCount) || roomCount < 0) throw new Error("Buzz configured room count must be a non-negative integer");
|
|
1224
|
-
const availableProfileSubscriptions = 1020 - roomCount;
|
|
1225
|
-
if (availableProfileSubscriptions < 0) throw new Error(`Buzz supports at most ${BUZZ_MAX_CONFIGURED_ROOMS} configured rooms per account`);
|
|
1226
|
-
return { profileLimit: Math.min(BUZZ_DIRECTORY_MAX_PROFILE_SUBSCRIPTIONS, availableProfileSubscriptions) * 200 };
|
|
1227
|
-
}
|
|
1228
|
-
//#endregion
|
|
1229
997
|
//#region extensions/buzz/src/buzz-bus.ts
|
|
1230
998
|
const PRESENCE_KIND = 20001;
|
|
1231
999
|
const PRESENCE_HEARTBEAT_INTERVAL_MS = 3e4;
|
|
@@ -1233,6 +1001,7 @@ const REPLAY_TTL_MS = 2592e6;
|
|
|
1233
1001
|
const REPLAY_MAX_ENTRIES = 1e4;
|
|
1234
1002
|
const REPLAY_STATE_MAX_ENTRIES = 5e4;
|
|
1235
1003
|
const REPLAY_NAMESPACE_PREFIX = "buzz.inbound-dedupe";
|
|
1004
|
+
const THREAD_ROOT_CACHE_MAX_ENTRIES = 1024;
|
|
1236
1005
|
function buildBuzzTextEvent(params) {
|
|
1237
1006
|
return finalizeEvent({
|
|
1238
1007
|
kind: 9,
|
|
@@ -1396,10 +1165,55 @@ async function startBuzzBus(options) {
|
|
|
1396
1165
|
let stopPresenceHeartbeat = () => {};
|
|
1397
1166
|
let profileTask;
|
|
1398
1167
|
let membershipTracker;
|
|
1168
|
+
const threadRoots = /* @__PURE__ */ new Map();
|
|
1169
|
+
const rememberThreadRoot = (event) => {
|
|
1170
|
+
const root = parseBuzzMessageEvent(event);
|
|
1171
|
+
if (!root || root.threadId || !verifyEvent(event)) return;
|
|
1172
|
+
threadRoots.set(event.id, {
|
|
1173
|
+
channelId: root.channelId.toLowerCase(),
|
|
1174
|
+
isBotOwned: event.pubkey === publicKey
|
|
1175
|
+
});
|
|
1176
|
+
if (threadRoots.size > THREAD_ROOT_CACHE_MAX_ENTRIES) {
|
|
1177
|
+
const oldest = threadRoots.keys().next().value;
|
|
1178
|
+
if (oldest) threadRoots.delete(oldest);
|
|
1179
|
+
}
|
|
1180
|
+
};
|
|
1399
1181
|
const bus = {
|
|
1400
1182
|
publicKey,
|
|
1401
1183
|
directory,
|
|
1402
1184
|
refreshDirectory: async () => await directoryRelay?.refreshRooms(options.channelIds),
|
|
1185
|
+
isBotOwnedThread: async ({ channelId, threadId }) => {
|
|
1186
|
+
signal.throwIfAborted();
|
|
1187
|
+
if (!threadRoots.has(threadId)) try {
|
|
1188
|
+
await queryBuzzRelaySnapshot({
|
|
1189
|
+
relay,
|
|
1190
|
+
filters: [{
|
|
1191
|
+
ids: [threadId],
|
|
1192
|
+
kinds: [...BUZZ_INBOUND_MESSAGE_KINDS],
|
|
1193
|
+
limit: 1
|
|
1194
|
+
}],
|
|
1195
|
+
signal,
|
|
1196
|
+
timeoutMessage: "Timed out loading Buzz thread root",
|
|
1197
|
+
abortMessage: "Buzz thread root query aborted",
|
|
1198
|
+
failureMessage: "Buzz thread root query failed",
|
|
1199
|
+
closeReason: "thread root loaded",
|
|
1200
|
+
closeMessage: (reason) => `Buzz thread root query closed: ${reason}`,
|
|
1201
|
+
onEvent: (event) => {
|
|
1202
|
+
if (event.id === threadId) rememberThreadRoot(event);
|
|
1203
|
+
},
|
|
1204
|
+
result: () => {},
|
|
1205
|
+
onTimeout: reportFatalError,
|
|
1206
|
+
checkAbortAfterSubscribe: true
|
|
1207
|
+
});
|
|
1208
|
+
} catch (error) {
|
|
1209
|
+
signal.throwIfAborted();
|
|
1210
|
+
options.onMessageError?.(error instanceof Error ? error : new Error("Buzz thread root query failed", { cause: error }));
|
|
1211
|
+
return false;
|
|
1212
|
+
}
|
|
1213
|
+
signal.throwIfAborted();
|
|
1214
|
+
const root = threadRoots.get(threadId);
|
|
1215
|
+
return root?.channelId === channelId && root.isBotOwned;
|
|
1216
|
+
},
|
|
1403
1217
|
sendText: async ({ channelId, text, threadId, replyToId }) => {
|
|
1404
1218
|
signal.throwIfAborted();
|
|
1405
1219
|
const mentionSyntax = inspectBuzzMentionSyntax(text);
|
|
@@ -1417,6 +1231,7 @@ async function startBuzzBus(options) {
|
|
|
1417
1231
|
mentionedPubkeys
|
|
1418
1232
|
});
|
|
1419
1233
|
await relay.publish(event);
|
|
1234
|
+
rememberThreadRoot(event);
|
|
1420
1235
|
return event.id;
|
|
1421
1236
|
},
|
|
1422
1237
|
sendTyping: async ({ channelId, threadId, replyToId }) => {
|
|
@@ -1435,6 +1250,7 @@ async function startBuzzBus(options) {
|
|
|
1435
1250
|
stopPresenceHeartbeat();
|
|
1436
1251
|
directoryRelay?.close();
|
|
1437
1252
|
replayGuard.clearMemory();
|
|
1253
|
+
threadRoots.clear();
|
|
1438
1254
|
relay.close();
|
|
1439
1255
|
await membershipTracker?.close();
|
|
1440
1256
|
await profileTask;
|
|
@@ -1481,7 +1297,11 @@ async function startBuzzBus(options) {
|
|
|
1481
1297
|
onHistoryError: options.onHistoryError,
|
|
1482
1298
|
onRoomUnavailable: options.onRoomUnavailable,
|
|
1483
1299
|
onMessageEvent: (event, isMember, reservation) => {
|
|
1484
|
-
if (signal.aborted
|
|
1300
|
+
if (signal.aborted) return;
|
|
1301
|
+
if (event.pubkey === publicKey) {
|
|
1302
|
+
rememberThreadRoot(event);
|
|
1303
|
+
return;
|
|
1304
|
+
}
|
|
1485
1305
|
const message = parseBuzzMessageEvent(event);
|
|
1486
1306
|
if (!message || !isMember(message.channelId, event.pubkey)) return;
|
|
1487
1307
|
if ((reservation ?? dispatchQueue).enqueue(async () => {
|
|
@@ -1545,6 +1365,18 @@ async function startBuzzBus(options) {
|
|
|
1545
1365
|
}
|
|
1546
1366
|
}
|
|
1547
1367
|
//#endregion
|
|
1368
|
+
//#region src/channels/mention-gating.ts
|
|
1369
|
+
function resolveBotThreadMentionPolicy(params) {
|
|
1370
|
+
if (!params.isBotOwnedThread || params.requireMentionInBotThreads === void 0) return {
|
|
1371
|
+
requireMention: params.requireMention,
|
|
1372
|
+
implicitMentionKinds: params.implicitMentionKinds
|
|
1373
|
+
};
|
|
1374
|
+
return {
|
|
1375
|
+
requireMention: params.requireMentionInBotThreads,
|
|
1376
|
+
implicitMentionKinds: params.requireMentionInBotThreads ? params.implicitMentionKinds?.filter((kind) => kind === "native") : params.implicitMentionKinds
|
|
1377
|
+
};
|
|
1378
|
+
}
|
|
1379
|
+
//#endregion
|
|
1548
1380
|
//#region extensions/buzz/src/pending-history.ts
|
|
1549
1381
|
const ENTRY_BYTES = 512;
|
|
1550
1382
|
const CONTEXT_BYTES = 1024;
|
|
@@ -1634,6 +1466,17 @@ async function handleBuzzInbound(params) {
|
|
|
1634
1466
|
const shouldComputeCommandAuthorized = supportsTextInterpretation && runtime.channel.commands.shouldComputeCommandAuthorized(message.text, cfg);
|
|
1635
1467
|
const hasControlCommand = shouldComputeCommandAuthorized && runtime.channel.text.hasControlCommand(message.text, cfg);
|
|
1636
1468
|
const groupConfig = account.config.groups?.[channelId];
|
|
1469
|
+
const requireMention = groupConfig?.requireMention ?? true;
|
|
1470
|
+
const isBotOwnedThread = message.threadId && !wasMentioned && groupConfig?.requireMentionInBotThreads !== void 0 && groupConfig.requireMentionInBotThreads !== requireMention ? await bus.isBotOwnedThread({
|
|
1471
|
+
channelId,
|
|
1472
|
+
threadId: message.threadId
|
|
1473
|
+
}) : false;
|
|
1474
|
+
params.assertCurrent();
|
|
1475
|
+
const mentionPolicy = resolveBotThreadMentionPolicy({
|
|
1476
|
+
isBotOwnedThread,
|
|
1477
|
+
requireMentionInBotThreads: groupConfig?.requireMentionInBotThreads,
|
|
1478
|
+
requireMention
|
|
1479
|
+
});
|
|
1637
1480
|
const access = await runtime.channel.inbound.ingress.resolveStable({
|
|
1638
1481
|
channelId: "buzz",
|
|
1639
1482
|
accountId: account.accountId,
|
|
@@ -1661,7 +1504,7 @@ async function handleBuzzInbound(params) {
|
|
|
1661
1504
|
groupPolicy: groupConfig?.groupPolicy ?? account.config.groupPolicy,
|
|
1662
1505
|
groupAllowFrom: groupConfig?.groupAllowFrom ?? account.config.groupAllowFrom,
|
|
1663
1506
|
policy: { activation: {
|
|
1664
|
-
requireMention:
|
|
1507
|
+
requireMention: mentionPolicy.requireMention,
|
|
1665
1508
|
allowTextCommands: true
|
|
1666
1509
|
} },
|
|
1667
1510
|
command: shouldComputeCommandAuthorized ? {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { i as resolveBuzzAccount, m as parseBuzzTarget, u as buildBuzzTarget } from "./types-DRUZMyTx.mjs";
|
|
2
|
+
import { t as applyBuzzDirectoryQueryAndLimit } from "./directory-query-D1BNu8nO.mjs";
|
|
3
|
+
//#region extensions/buzz/src/directory-config.ts
|
|
4
|
+
async function listBuzzDirectoryPeersFromConfig(_params) {
|
|
5
|
+
return [];
|
|
6
|
+
}
|
|
7
|
+
async function listBuzzDirectoryGroupsFromConfig(params) {
|
|
8
|
+
const account = resolveBuzzAccount({
|
|
9
|
+
cfg: params.cfg,
|
|
10
|
+
accountId: params.accountId
|
|
11
|
+
});
|
|
12
|
+
const entries = Object.entries(account.config.groups ?? {}).filter(([, config]) => config.enabled !== false).map(([roomId]) => {
|
|
13
|
+
const id = parseBuzzTarget(roomId);
|
|
14
|
+
return {
|
|
15
|
+
kind: "group",
|
|
16
|
+
id: buildBuzzTarget(id),
|
|
17
|
+
name: id,
|
|
18
|
+
raw: { roomId: id }
|
|
19
|
+
};
|
|
20
|
+
}).toSorted((a, b) => a.id.localeCompare(b.id));
|
|
21
|
+
return applyBuzzDirectoryQueryAndLimit(entries, params);
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
export { listBuzzDirectoryPeersFromConfig as n, listBuzzDirectoryGroupsFromConfig as t };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region extensions/buzz/src/directory-query.ts
|
|
2
|
+
function matchesDirectoryQuery(entry, query) {
|
|
3
|
+
if (!query) return true;
|
|
4
|
+
return [
|
|
5
|
+
entry.id,
|
|
6
|
+
entry.name,
|
|
7
|
+
entry.handle
|
|
8
|
+
].some((value) => value?.toLowerCase().includes(query));
|
|
9
|
+
}
|
|
10
|
+
function applyBuzzDirectoryQueryAndLimit(entries, params) {
|
|
11
|
+
const query = params.query?.trim().toLowerCase() ?? "";
|
|
12
|
+
const limit = typeof params.limit === "number" && params.limit > 0 ? Math.floor(params.limit) : void 0;
|
|
13
|
+
const result = [];
|
|
14
|
+
for (const entry of entries) {
|
|
15
|
+
if (!matchesDirectoryQuery(entry, query)) continue;
|
|
16
|
+
result.push(entry);
|
|
17
|
+
if (limit !== void 0 && result.length >= limit) break;
|
|
18
|
+
}
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { applyBuzzDirectoryQueryAndLimit as t };
|