Merge branch 'feat-create-mute-policy' into 'main'
Create MuteListPolicy See merge request soapbox-pub/ditto!242
This commit is contained in:
commit
d142ce618d
|
@ -0,0 +1,72 @@
|
|||
import { MockRelay } from '@nostrify/nostrify/test';
|
||||
|
||||
import { assertEquals } from '@/deps-test.ts';
|
||||
import { UserStore } from '@/storages/UserStore.ts';
|
||||
import { MuteListPolicy } from '@/policies/MuteListPolicy.ts';
|
||||
|
||||
import userBlack from '~/fixtures/events/kind-0-black.json' with { type: 'json' };
|
||||
import userMe from '~/fixtures/events/event-0-makes-repost-with-quote-repost.json' with { type: 'json' };
|
||||
import blockEvent from '~/fixtures/events/kind-10000-black-blocks-user-me.json' with { type: 'json' };
|
||||
import event1authorUserMe from '~/fixtures/events/event-1-quote-repost-will-be-reposted.json' with { type: 'json' };
|
||||
import event1 from '~/fixtures/events/event-1.json' with { type: 'json' };
|
||||
|
||||
Deno.test('block event: muted user cannot post', async () => {
|
||||
const userBlackCopy = structuredClone(userBlack);
|
||||
const userMeCopy = structuredClone(userMe);
|
||||
const blockEventCopy = structuredClone(blockEvent);
|
||||
const event1authorUserMeCopy = structuredClone(event1authorUserMe);
|
||||
|
||||
const db = new MockRelay();
|
||||
|
||||
const store = new UserStore(userBlackCopy.pubkey, db);
|
||||
const policy = new MuteListPolicy(userBlack.pubkey, db);
|
||||
|
||||
await store.event(blockEventCopy);
|
||||
await store.event(userBlackCopy);
|
||||
await store.event(userMeCopy);
|
||||
|
||||
const ok = await policy.call(event1authorUserMeCopy);
|
||||
|
||||
assertEquals(ok, ['OK', event1authorUserMeCopy.id, false, 'You are banned in this server.']);
|
||||
});
|
||||
|
||||
Deno.test('allow event: user is NOT muted because there is no muted event', async () => {
|
||||
const userBlackCopy = structuredClone(userBlack);
|
||||
const userMeCopy = structuredClone(userMe);
|
||||
const event1authorUserMeCopy = structuredClone(event1authorUserMe);
|
||||
|
||||
const db = new MockRelay();
|
||||
|
||||
const store = new UserStore(userBlackCopy.pubkey, db);
|
||||
const policy = new MuteListPolicy(userBlack.pubkey, db);
|
||||
|
||||
await store.event(userBlackCopy);
|
||||
await store.event(userMeCopy);
|
||||
|
||||
const ok = await policy.call(event1authorUserMeCopy);
|
||||
|
||||
assertEquals(ok, ['OK', event1authorUserMeCopy.id, true, '']);
|
||||
});
|
||||
|
||||
Deno.test('allow event: user is NOT muted because he is not in mute event', async () => {
|
||||
const userBlackCopy = structuredClone(userBlack);
|
||||
const userMeCopy = structuredClone(userMe);
|
||||
const event1authorUserMeCopy = structuredClone(event1authorUserMe);
|
||||
const blockEventCopy = structuredClone(blockEvent);
|
||||
const event1copy = structuredClone(event1);
|
||||
|
||||
const db = new MockRelay();
|
||||
|
||||
const store = new UserStore(userBlackCopy.pubkey, db);
|
||||
const policy = new MuteListPolicy(userBlack.pubkey, db);
|
||||
|
||||
await store.event(userBlackCopy);
|
||||
await store.event(blockEventCopy);
|
||||
await store.event(userMeCopy);
|
||||
await store.event(event1copy);
|
||||
await store.event(event1authorUserMeCopy);
|
||||
|
||||
const ok = await policy.call(event1copy);
|
||||
|
||||
assertEquals(ok, ['OK', event1.id, true, '']);
|
||||
});
|
|
@ -0,0 +1,18 @@
|
|||
import { NostrEvent, NostrRelayOK, NPolicy, NStore } from '@nostrify/nostrify';
|
||||
|
||||
import { getTagSet } from '@/tags.ts';
|
||||
|
||||
export class MuteListPolicy implements NPolicy {
|
||||
constructor(private pubkey: string, private store: NStore) {}
|
||||
|
||||
async call(event: NostrEvent): Promise<NostrRelayOK> {
|
||||
const [muteList] = await this.store.query([{ authors: [this.pubkey], kinds: [10000], limit: 1 }]);
|
||||
const pubkeys = getTagSet(muteList?.tags ?? [], 'p');
|
||||
|
||||
if (pubkeys.has(event.pubkey)) {
|
||||
return ['OK', event.id, false, 'You are banned in this server.'];
|
||||
}
|
||||
|
||||
return ['OK', event.id, true, ''];
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ import userMe from '~/fixtures/events/event-0-makes-repost-with-quote-repost.jso
|
|||
import blockEvent from '~/fixtures/events/kind-10000-black-blocks-user-me.json' with { type: 'json' };
|
||||
import event1authorUserMe from '~/fixtures/events/event-1-quote-repost-will-be-reposted.json' with { type: 'json' };
|
||||
|
||||
Deno.test('query events of users that are not blocked', async () => {
|
||||
Deno.test('query events of users that are not muted', async () => {
|
||||
const userBlackCopy = structuredClone(userBlack);
|
||||
const userMeCopy = structuredClone(userMe);
|
||||
const blockEventCopy = structuredClone(blockEvent);
|
||||
|
@ -25,3 +25,17 @@ Deno.test('query events of users that are not blocked', async () => {
|
|||
|
||||
assertEquals(await store.query([{ kinds: [1] }], { limit: 1 }), []);
|
||||
});
|
||||
|
||||
Deno.test('user never muted anyone', async () => {
|
||||
const userBlackCopy = structuredClone(userBlack);
|
||||
const userMeCopy = structuredClone(userMe);
|
||||
|
||||
const db = new MockRelay();
|
||||
|
||||
const store = new UserStore(userBlackCopy.pubkey, db);
|
||||
|
||||
await store.event(userBlackCopy);
|
||||
await store.event(userMeCopy);
|
||||
|
||||
assertEquals(await store.query([{ kinds: [0], authors: [userMeCopy.pubkey] }], { limit: 1 }), [userMeCopy]);
|
||||
});
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { NostrEvent, NostrFilter, NStore } from '@nostrify/nostrify';
|
||||
|
||||
import { DittoEvent } from '@/interfaces/DittoEvent.ts';
|
||||
import { getTagSet } from '@/tags.ts';
|
||||
|
||||
|
@ -16,17 +17,13 @@ export class UserStore implements NStore {
|
|||
}
|
||||
|
||||
/**
|
||||
* Query events that `pubkey` did not block
|
||||
* Query events that `pubkey` did not mute
|
||||
* https://github.com/nostr-protocol/nips/blob/master/51.md#standard-lists
|
||||
*/
|
||||
async query(filters: NostrFilter[], opts: { signal?: AbortSignal; limit?: number } = {}): Promise<DittoEvent[]> {
|
||||
const allEvents = await this.store.query(filters, opts);
|
||||
|
||||
const mutedPubkeysEvent = await this.getMuteList();
|
||||
if (!mutedPubkeysEvent) {
|
||||
return allEvents;
|
||||
}
|
||||
const mutedPubkeys = getTagSet(mutedPubkeysEvent.tags, 'p');
|
||||
const mutedPubkeys = await this.getMutedPubkeys();
|
||||
|
||||
return allEvents.filter((event) => {
|
||||
return event.kind === 0 || mutedPubkeys.has(event.pubkey) === false;
|
||||
|
@ -37,4 +34,12 @@ export class UserStore implements NStore {
|
|||
const [muteList] = await this.store.query([{ authors: [this.pubkey], kinds: [10000], limit: 1 }]);
|
||||
return muteList;
|
||||
}
|
||||
|
||||
private async getMutedPubkeys(): Promise<Set<string>> {
|
||||
const mutedPubkeysEvent = await this.getMuteList();
|
||||
if (!mutedPubkeysEvent) {
|
||||
return new Set();
|
||||
}
|
||||
return getTagSet(mutedPubkeysEvent.tags, 'p');
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue