mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-18 16:41:45 -06:00
fix: invalidate removed Buzz mention members
This commit is contained in:
@@ -14,6 +14,11 @@ const relayMocks = vi.hoisted(() => ({
|
||||
membershipEvents: [] as Event[],
|
||||
subscriptions: [] as Array<{
|
||||
filters: Filter[];
|
||||
handlers: {
|
||||
onevent: (event: Event) => void;
|
||||
oneose?: () => void;
|
||||
onclose?: (reason: string) => void;
|
||||
};
|
||||
close: ReturnType<typeof vi.fn>;
|
||||
}>,
|
||||
}));
|
||||
@@ -39,10 +44,11 @@ vi.mock("nostr-tools", async (importOriginal) => {
|
||||
handlers: {
|
||||
onevent: (event: Event) => void;
|
||||
oneose?: () => void;
|
||||
onclose?: (reason: string) => void;
|
||||
},
|
||||
) {
|
||||
const close = vi.fn();
|
||||
relayMocks.subscriptions.push({ filters, close });
|
||||
relayMocks.subscriptions.push({ filters, handlers, close });
|
||||
if (filters.some((filter) => filter.kinds?.includes(39002))) {
|
||||
for (const event of relayMocks.membershipEvents) {
|
||||
handlers.onevent(event);
|
||||
@@ -251,6 +257,41 @@ describe("Buzz mention delivery", () => {
|
||||
await bus.close();
|
||||
});
|
||||
|
||||
it("stops mentioning a removed member before the signed roster refresh completes", async () => {
|
||||
const explicitSender = `nostr:${nip19.npubEncode(SENDER_PUBLIC_KEY)}`;
|
||||
const bus = await startBuzzBus({
|
||||
accountId: ACCOUNT_ID,
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
channelIds: [CHANNEL_ID],
|
||||
onMessage: async () => {},
|
||||
});
|
||||
|
||||
await bus.sendText({ channelId: CHANNEL_ID, text: explicitSender });
|
||||
relayMocks.publish.mockClear();
|
||||
relayMocks.subscriptions
|
||||
.find((entry) => subscriptionIncludesKind(entry, 40_099))
|
||||
?.handlers.onevent({
|
||||
id: "member-removed-1",
|
||||
kind: 40_099,
|
||||
pubkey: RELAY_PUBLIC_KEY,
|
||||
created_at: 1_700_000_001,
|
||||
content: JSON.stringify({ type: "member_removed", target: SENDER_PUBLIC_KEY }),
|
||||
sig: "e".repeat(128),
|
||||
tags: [["h", CHANNEL_ID]],
|
||||
});
|
||||
|
||||
expect(bus.directory.mentionMembers(CHANNEL_ID)).toEqual([
|
||||
expect.objectContaining({ publicKey: BOT_PUBLIC_KEY }),
|
||||
]);
|
||||
await expect(bus.sendText({ channelId: CHANNEL_ID, text: explicitSender })).rejects.toThrow(
|
||||
"is not a current room member",
|
||||
);
|
||||
expect(relayMocks.publish).not.toHaveBeenCalled();
|
||||
|
||||
await bus.close();
|
||||
});
|
||||
|
||||
it("keeps mention-free active sends off the room roster path", async () => {
|
||||
const bus = await startBuzzBus({
|
||||
accountId: ACCOUNT_ID,
|
||||
|
||||
@@ -90,6 +90,28 @@ export async function createBuzzRoomMembershipTracker(params: {
|
||||
const refreshes = new Map<string, RefreshState>();
|
||||
let membershipQueryTail = Promise.resolve();
|
||||
const memberships = await queryBuzzRoomMemberships(params);
|
||||
const effectiveMemberships = (): ReadonlyMap<string, BuzzRoomMembership> => {
|
||||
if (blockedRooms.size === 0 && deniedMembers.size === 0) {
|
||||
return memberships;
|
||||
}
|
||||
const effective = new Map<string, BuzzRoomMembership>();
|
||||
for (const [channelId, membership] of memberships) {
|
||||
if (blockedRooms.has(channelId)) {
|
||||
continue;
|
||||
}
|
||||
const denied = deniedMembers.get(channelId);
|
||||
if (!denied || denied.size === 0) {
|
||||
effective.set(channelId, membership);
|
||||
continue;
|
||||
}
|
||||
effective.set(channelId, {
|
||||
...membership,
|
||||
members: new Set([...membership.members].filter((publicKey) => !denied.has(publicKey))),
|
||||
roles: new Map([...membership.roles].filter(([publicKey]) => !denied.has(publicKey))),
|
||||
});
|
||||
}
|
||||
return effective;
|
||||
};
|
||||
const isMember = (channelId: string, publicKey: string) =>
|
||||
!blockedRooms.has(channelId) &&
|
||||
!deniedMembers.get(channelId)?.has(publicKey.trim().toLowerCase()) &&
|
||||
@@ -181,7 +203,7 @@ export async function createBuzzRoomMembershipTracker(params: {
|
||||
pendingMemberships.delete(channelId);
|
||||
deniedMembers.delete(channelId);
|
||||
blockedRooms.delete(channelId);
|
||||
params.onMembershipsChanged?.(memberships);
|
||||
params.onMembershipsChanged?.(effectiveMemberships());
|
||||
return;
|
||||
}
|
||||
if (state.generation !== state.lastAttemptedGeneration) {
|
||||
@@ -255,6 +277,7 @@ export async function createBuzzRoomMembershipTracker(params: {
|
||||
if (change.targetPublicKey === params.botPublicKey) {
|
||||
blockedRooms.add(channelId);
|
||||
}
|
||||
params.onMembershipsChanged?.(effectiveMemberships());
|
||||
return refreshMembershipOnce(channelId);
|
||||
};
|
||||
const handleRoomEvent = (event: Event, reservation?: BuzzReplayDispatchReservation) => {
|
||||
@@ -358,7 +381,7 @@ export async function createBuzzRoomMembershipTracker(params: {
|
||||
}
|
||||
|
||||
return {
|
||||
memberships: () => memberships,
|
||||
memberships: effectiveMemberships,
|
||||
catchUpHistory: async () => {
|
||||
for (const channelId of params.channelIds) {
|
||||
const page = historyPages.get(channelId);
|
||||
|
||||
Reference in New Issue
Block a user