applesauce-actions 0.12.0 → 1.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.
package/README.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # Applesauce Actions
2
2
 
3
- A collection of pre-built actions nostr clients can use. built on top of `applesauce-core` and `applesauce-factory`
3
+ A collection of pre-built actions nostr clients can use. Built on top of `applesauce-core` and `applesauce-factory`.
4
4
 
5
- [Documentation](https://hzrd149.github.io/applesauce/typedoc/modules/applesauce_actions.html)
5
+ [Documentation](https://hzrd149.github.io/applesauce/typedoc/modules/applesauce-actions.html)
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install applesauce-actions
11
+ ```
12
+
13
+ ## Overview
14
+
15
+ Actions are common pre-built async operations that apps can perform. They use:
16
+
17
+ - `EventStore` for access to known nostr events
18
+ - `EventFactory` to build and sign new nostr events
19
+ - A `publish` method to publish or save the resulting events
20
+
21
+ The package provides an `ActionHub` class that combines these components into a single manager for easier action execution.
22
+
23
+ ## Basic Usage
24
+
25
+ ```typescript
26
+ import { ActionHub } from "applesauce-actions";
27
+ import { FollowUser } from "applesauce-actions/actions";
28
+
29
+ async function publishEvent(event: NostrEvent) {
30
+ await relayPool.publish(event, ["wss://relay.example.com"]);
31
+ }
32
+
33
+ // Create an action hub with your event store, factory and publish method
34
+ const hub = new ActionHub(eventStore, eventFactory, publishEvent);
35
+
36
+ // Example: Follow a user
37
+ await hub
38
+ .exec(FollowUser, "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d")
39
+ .forEach((event) => publishEvent(event));
40
+ ```
41
+
42
+ For more detailed documentation and examples, visit the [full documentation](https://hzrd149.github.io/applesauce/overview/actions.html).
@@ -1,6 +1,6 @@
1
1
  import { Observable } from "rxjs";
2
2
  import { NostrEvent } from "nostr-tools";
3
- import { IEventStore } from "applesauce-core/event-store";
3
+ import { ISyncEventStore } from "applesauce-core/event-store";
4
4
  import { EventFactory } from "applesauce-factory";
5
5
  /**
6
6
  * A callback used to tell the upstream app to publish an event
@@ -10,7 +10,7 @@ export type PublishMethod = (event: NostrEvent) => void | Promise<void>;
10
10
  /** The context that is passed to actions for them to use to preform actions */
11
11
  export type ActionContext = {
12
12
  /** The event store to load events from */
13
- events: IEventStore;
13
+ events: ISyncEventStore;
14
14
  /** The pubkey of the signer in the event factory */
15
15
  self: string;
16
16
  /** The event factory used to build and modify events */
@@ -21,10 +21,10 @@ export type Action = (ctx: ActionContext) => Observable<NostrEvent> | AsyncGener
21
21
  export type ActionConstructor<Args extends Array<any>> = (...args: Args) => Action;
22
22
  /** The main class that runs actions */
23
23
  export declare class ActionHub {
24
- events: IEventStore;
24
+ events: ISyncEventStore;
25
25
  factory: EventFactory;
26
26
  publish?: PublishMethod | undefined;
27
- constructor(events: IEventStore, factory: EventFactory, publish?: PublishMethod | undefined);
27
+ constructor(events: ISyncEventStore, factory: EventFactory, publish?: PublishMethod | undefined);
28
28
  protected context: ActionContext | undefined;
29
29
  protected getContext(): Promise<ActionContext>;
30
30
  /** Runs an action in a ActionContext and converts the result to an Observable */
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,93 @@
1
+ import { EventStore } from "applesauce-core";
2
+ import { BLOSSOM_SERVER_LIST_KIND } from "applesauce-core/helpers/blossom";
3
+ import { EventFactory } from "applesauce-factory";
4
+ import { firstValueFrom, lastValueFrom } from "rxjs";
5
+ import { toArray } from "rxjs/operators";
6
+ import { beforeEach, describe, expect, it } from "vitest";
7
+ import { FakeUser } from "../../__tests__/fake-user.js";
8
+ import { ActionHub } from "../../action-hub.js";
9
+ import { AddBlossomServer, NewBlossomServers, RemoveBlossomServer, SetDefaultBlossomServer } from "../blossom.js";
10
+ const user = new FakeUser();
11
+ let events;
12
+ let factory;
13
+ let hub;
14
+ beforeEach(() => {
15
+ events = new EventStore();
16
+ factory = new EventFactory({ signer: user });
17
+ hub = new ActionHub(events, factory);
18
+ });
19
+ describe("NewBlossomServers", () => {
20
+ it("should publish a kind 10063 blossom server list", async () => {
21
+ const result = await lastValueFrom(hub.exec(NewBlossomServers, ["https://cdn.example.com/"]).pipe(toArray()));
22
+ expect(result[0]).toMatchObject({ kind: BLOSSOM_SERVER_LIST_KIND, tags: [["server", "https://cdn.example.com/"]] });
23
+ });
24
+ it("should throw if a blossom servers event already exists", async () => {
25
+ // Create the initial event
26
+ await hub.exec(NewBlossomServers, ["https://cdn.example.com/"]).forEach((e) => events.add(e));
27
+ // Attempt to create another one
28
+ await expect(lastValueFrom(hub.exec(NewBlossomServers, ["https://other.example.com/"]).pipe(toArray()))).rejects.toThrow("Blossom servers event already exists");
29
+ });
30
+ });
31
+ describe("AddBlossomServer", () => {
32
+ beforeEach(async () => {
33
+ // Create an initial empty server list
34
+ await hub.exec(NewBlossomServers, ["https://cdn.example.com/"]).forEach((e) => events.add(e));
35
+ });
36
+ it("should add a single server to the list", async () => {
37
+ const result = await firstValueFrom(hub.exec(AddBlossomServer, "https://other.example.com/"));
38
+ expect(result).toMatchObject({
39
+ kind: BLOSSOM_SERVER_LIST_KIND,
40
+ tags: expect.arrayContaining([["server", expect.stringContaining("other.example.com")]]),
41
+ });
42
+ });
43
+ it("should add multiple servers to the list", async () => {
44
+ const result = await firstValueFrom(hub.exec(AddBlossomServer, ["https://other.example.com/", "https://other2.example.com/"]));
45
+ expect(result).toMatchObject({
46
+ kind: BLOSSOM_SERVER_LIST_KIND,
47
+ tags: expect.arrayContaining([
48
+ ["server", expect.stringContaining("other.example.com")],
49
+ ["server", expect.stringContaining("other2.example.com")],
50
+ ]),
51
+ });
52
+ });
53
+ });
54
+ describe("RemoveBlossomServer", () => {
55
+ beforeEach(async () => {
56
+ // Create an initial server list with two servers
57
+ await hub
58
+ .exec(NewBlossomServers, ["https://cdn.example.com/", "https://other.example.com/"])
59
+ .forEach((e) => events.add(e));
60
+ });
61
+ it("should remove a server from the list", async () => {
62
+ const result = await firstValueFrom(hub.exec(RemoveBlossomServer, "https://cdn.example.com/"));
63
+ expect(result).toMatchObject({
64
+ kind: BLOSSOM_SERVER_LIST_KIND,
65
+ tags: expect.arrayContaining([["server", expect.stringContaining("other.example.com")]]),
66
+ });
67
+ });
68
+ it("should remove multiple servers from the list", async () => {
69
+ const result = await firstValueFrom(hub.exec(RemoveBlossomServer, ["https://cdn.example.com/", "https://other.example.com/"]));
70
+ expect(result).toMatchObject({
71
+ kind: BLOSSOM_SERVER_LIST_KIND,
72
+ tags: [],
73
+ });
74
+ });
75
+ });
76
+ describe("SetDefaultBlossomServer", () => {
77
+ beforeEach(async () => {
78
+ // Create an initial server list with two servers
79
+ await hub
80
+ .exec(NewBlossomServers, ["https://cdn.example.com/", "https://other.example.com/"])
81
+ .forEach((e) => events.add(e));
82
+ });
83
+ it("should move the specified server to the top of the list", async () => {
84
+ const result = await firstValueFrom(hub.exec(SetDefaultBlossomServer, "https://other.example.com/"));
85
+ expect(result).toMatchObject({
86
+ kind: BLOSSOM_SERVER_LIST_KIND,
87
+ tags: expect.arrayContaining([
88
+ ["server", expect.stringContaining("other.example.com")],
89
+ ["server", expect.stringContaining("cdn.example.com")],
90
+ ]),
91
+ });
92
+ });
93
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,67 @@
1
+ import { beforeEach, describe, expect, it } from "vitest";
2
+ import { EventStore } from "applesauce-core";
3
+ import { EventFactory } from "applesauce-factory";
4
+ import { kinds } from "nostr-tools";
5
+ import { getMutedThings, getHiddenMutedThings } from "applesauce-core/helpers";
6
+ import { subscribeSpyTo } from "@hirez_io/observer-spy";
7
+ import { FakeUser } from "../../__tests__/fake-user.js";
8
+ import { ActionHub } from "../../action-hub.js";
9
+ import { MuteThread, UnmuteThread } from "../mute.js";
10
+ const user = new FakeUser();
11
+ const testEventId = "test-event-id";
12
+ let events;
13
+ let factory;
14
+ let hub;
15
+ beforeEach(() => {
16
+ events = new EventStore();
17
+ factory = new EventFactory({ signer: user });
18
+ hub = new ActionHub(events, factory);
19
+ // Add a mute list event to work with
20
+ const muteList = user.event({
21
+ kind: kinds.Mutelist,
22
+ tags: [],
23
+ content: "",
24
+ created_at: Math.floor(Date.now() / 1000),
25
+ });
26
+ events.add(muteList);
27
+ });
28
+ describe("MuteThread", () => {
29
+ it("should add an event to public tags in mute list", async () => {
30
+ const spy = subscribeSpyTo(hub.exec(MuteThread, testEventId), { expectErrors: false });
31
+ await spy.onComplete();
32
+ const emittedEvent = spy.getLastValue();
33
+ const mutedThings = getMutedThings(emittedEvent);
34
+ expect(mutedThings.threads).toContain(testEventId);
35
+ });
36
+ it("should add an event to hidden tags in mute list", async () => {
37
+ const spy = subscribeSpyTo(hub.exec(MuteThread, testEventId, true), { expectErrors: false });
38
+ await spy.onComplete();
39
+ const emittedEvent = spy.getLastValue();
40
+ const hiddenMutedThings = await getHiddenMutedThings(emittedEvent);
41
+ expect(hiddenMutedThings?.threads).toContain(testEventId);
42
+ });
43
+ });
44
+ describe("UnmuteThread", () => {
45
+ it("should remove an event from public tags in mute list", async () => {
46
+ // First add the thread to mute list
47
+ const addSpy = subscribeSpyTo(hub.exec(MuteThread, testEventId), { expectErrors: false });
48
+ await addSpy.onComplete();
49
+ // Then unmute it
50
+ const spy = subscribeSpyTo(hub.exec(UnmuteThread, testEventId), { expectErrors: false });
51
+ await spy.onComplete();
52
+ const emittedEvent = spy.getLastValue();
53
+ const mutedThings = getMutedThings(emittedEvent);
54
+ expect(mutedThings.threads).not.toContain(testEventId);
55
+ });
56
+ it("should remove an event from hidden tags in mute list", async () => {
57
+ // First add the thread to hidden mute list
58
+ const addSpy = subscribeSpyTo(hub.exec(MuteThread, testEventId, true), { expectErrors: false });
59
+ await addSpy.onComplete();
60
+ // Then unmute it
61
+ const spy = subscribeSpyTo(hub.exec(UnmuteThread, testEventId, true), { expectErrors: false });
62
+ await spy.onComplete();
63
+ const emittedEvent = spy.getLastValue();
64
+ const hiddenMutedThings = await getHiddenMutedThings(emittedEvent);
65
+ expect(hiddenMutedThings?.threads).not.toContain(testEventId);
66
+ });
67
+ });
@@ -0,0 +1,10 @@
1
+ import { Action } from "../action-hub.js";
2
+ /** An action that adds a relay to the 10006 blocked relays event */
3
+ export declare function AddBlockedRelay(relay: string | string[], hidden?: boolean): Action;
4
+ /** An action that removes a relay from the 10006 blocked relays event */
5
+ export declare function RemoveBlockedRelay(relay: string | string[], hidden?: boolean): Action;
6
+ /** Creates a new blocked relays event */
7
+ export declare function NewBlockedRelays(relays?: string[] | {
8
+ public?: string[];
9
+ hidden?: string[];
10
+ }): Action;
@@ -0,0 +1,48 @@
1
+ import { modifyHiddenTags, modifyPublicTags } from "applesauce-factory/operations/event";
2
+ import { addRelayTag, removeRelayTag } from "applesauce-factory/operations/tag/relay";
3
+ import { kinds } from "nostr-tools";
4
+ function getBlockedRelaysEvent(events, self) {
5
+ const event = events.getReplaceable(kinds.BlockedRelaysList, self);
6
+ if (!event)
7
+ throw new Error("Can't find blocked relays event");
8
+ return event;
9
+ }
10
+ /** An action that adds a relay to the 10006 blocked relays event */
11
+ export function AddBlockedRelay(relay, hidden = false) {
12
+ return async function* ({ events, factory, self }) {
13
+ const blocked = getBlockedRelaysEvent(events, self);
14
+ const operation = Array.isArray(relay) ? relay.map((r) => addRelayTag(r)) : addRelayTag(relay);
15
+ const draft = await factory.modifyTags(blocked, hidden ? { hidden: operation } : operation);
16
+ yield await factory.sign(draft);
17
+ };
18
+ }
19
+ /** An action that removes a relay from the 10006 blocked relays event */
20
+ export function RemoveBlockedRelay(relay, hidden = false) {
21
+ return async function* ({ events, factory, self }) {
22
+ const blocked = getBlockedRelaysEvent(events, self);
23
+ const operation = Array.isArray(relay) ? relay.map((r) => removeRelayTag(r)) : removeRelayTag(relay);
24
+ const draft = await factory.modifyTags(blocked, hidden ? { hidden: operation } : operation);
25
+ yield await factory.sign(draft);
26
+ };
27
+ }
28
+ /** Creates a new blocked relays event */
29
+ export function NewBlockedRelays(relays) {
30
+ return async function* ({ events, factory, self }) {
31
+ const blocked = events.getReplaceable(kinds.BlockedRelaysList, self);
32
+ if (blocked)
33
+ throw new Error("Blocked relays event already exists");
34
+ let publicOperations = [];
35
+ let hiddenOperations = [];
36
+ if (Array.isArray(relays)) {
37
+ publicOperations.push(...relays.map((r) => addRelayTag(r)));
38
+ }
39
+ else {
40
+ if (relays?.public)
41
+ publicOperations.push(...(relays?.public ?? []).map((r) => addRelayTag(r)));
42
+ if (relays?.hidden)
43
+ hiddenOperations.push(...(relays?.hidden ?? []).map((r) => addRelayTag(r)));
44
+ }
45
+ const draft = await factory.build({ kind: kinds.BlockedRelaysList }, publicOperations.length ? modifyPublicTags(...publicOperations) : undefined, hiddenOperations.length ? modifyHiddenTags(...hiddenOperations) : undefined);
46
+ yield await factory.sign(draft);
47
+ };
48
+ }
@@ -0,0 +1,9 @@
1
+ import { Action } from "../action-hub.js";
2
+ /** An action that adds a server to the Blossom servers event */
3
+ export declare function AddBlossomServer(server: string | URL | (string | URL)[]): Action;
4
+ /** An action that removes a server from the Blossom servers event */
5
+ export declare function RemoveBlossomServer(server: string | URL | (string | URL)[]): Action;
6
+ /** Makes a specific Blossom server the default server (move it to the top of the list) */
7
+ export declare function SetDefaultBlossomServer(server: string | URL): Action;
8
+ /** Creates a new Blossom servers event */
9
+ export declare function NewBlossomServers(servers?: (string | URL)[]): Action;
@@ -0,0 +1,52 @@
1
+ import { BLOSSOM_SERVER_LIST_KIND } from "applesauce-core/helpers/blossom";
2
+ import { modifyPublicTags } from "applesauce-factory/operations/event";
3
+ import { addBlossomServerTag, removeBlossomServerTag } from "applesauce-factory/operations/tag/blossom";
4
+ function getBlossomServersEvent(events, self) {
5
+ const event = events.getReplaceable(BLOSSOM_SERVER_LIST_KIND, self);
6
+ if (!event)
7
+ throw new Error("Can't find Blossom servers event");
8
+ return event;
9
+ }
10
+ /** An action that adds a server to the Blossom servers event */
11
+ export function AddBlossomServer(server) {
12
+ return async function* ({ events, factory, self }) {
13
+ const servers = getBlossomServersEvent(events, self);
14
+ const operation = Array.isArray(server) ? server.map((s) => addBlossomServerTag(s)) : addBlossomServerTag(server);
15
+ const draft = await factory.modifyTags(servers, operation);
16
+ yield await factory.sign(draft);
17
+ };
18
+ }
19
+ /** An action that removes a server from the Blossom servers event */
20
+ export function RemoveBlossomServer(server) {
21
+ return async function* ({ events, factory, self }) {
22
+ const servers = getBlossomServersEvent(events, self);
23
+ const operation = Array.isArray(server)
24
+ ? server.map((s) => removeBlossomServerTag(s))
25
+ : removeBlossomServerTag(server);
26
+ const draft = await factory.modifyTags(servers, operation);
27
+ yield await factory.sign(draft);
28
+ };
29
+ }
30
+ /** Makes a specific Blossom server the default server (move it to the top of the list) */
31
+ export function SetDefaultBlossomServer(server) {
32
+ return async function* ({ events, factory, self }) {
33
+ const servers = getBlossomServersEvent(events, self);
34
+ const prependTag = (tag) => (tags) => [tag, ...tags];
35
+ const draft = await factory.modifyTags(servers, [
36
+ removeBlossomServerTag(server),
37
+ prependTag(["server", String(server)]),
38
+ ]);
39
+ yield await factory.sign(draft);
40
+ };
41
+ }
42
+ /** Creates a new Blossom servers event */
43
+ export function NewBlossomServers(servers) {
44
+ return async function* ({ events, factory, self }) {
45
+ const existing = events.getReplaceable(BLOSSOM_SERVER_LIST_KIND, self);
46
+ if (existing)
47
+ throw new Error("Blossom servers event already exists");
48
+ const operations = servers ? servers.map((s) => addBlossomServerTag(s)) : [];
49
+ const draft = await factory.build({ kind: BLOSSOM_SERVER_LIST_KIND }, modifyPublicTags(...operations));
50
+ yield await factory.sign(draft);
51
+ };
52
+ }
@@ -1,6 +1,6 @@
1
- import { kinds } from "nostr-tools";
2
- import { addEventBookmarkTag, removeEventBookmarkTag } from "applesauce-factory/operations/tag";
3
1
  import { modifyHiddenTags, modifyPublicTags, setListDescription, setListImage, setListTitle, } from "applesauce-factory/operations/event";
2
+ import { addEventBookmarkTag, removeEventBookmarkTag } from "applesauce-factory/operations/tag";
3
+ import { kinds } from "nostr-tools";
4
4
  function getBookmarkEvent(events, self, identifier) {
5
5
  return events.getReplaceable(identifier ? kinds.Bookmarksets : kinds.BookmarkList, self, identifier);
6
6
  }
@@ -0,0 +1,7 @@
1
+ import { Action } from "../action-hub.js";
2
+ /** An action that adds a relay to the 10050 DM relays event */
3
+ export declare function AddDMRelay(relay: string | string[]): Action;
4
+ /** An action that removes a relay from the 10050 DM relays event */
5
+ export declare function RemoveDMRelay(relay: string | string[]): Action;
6
+ /** Creates a new DM relays event */
7
+ export declare function NewDMRelays(relays?: string[]): Action;
@@ -0,0 +1,38 @@
1
+ import { modifyPublicTags } from "applesauce-factory/operations/event";
2
+ import { addRelayTag, removeRelayTag } from "applesauce-factory/operations/tag/relay";
3
+ import { kinds } from "nostr-tools";
4
+ function getDMRelaysEvent(events, self) {
5
+ const event = events.getReplaceable(kinds.DirectMessageRelaysList, self);
6
+ if (!event)
7
+ throw new Error("Can't find DM relays event");
8
+ return event;
9
+ }
10
+ /** An action that adds a relay to the 10050 DM relays event */
11
+ export function AddDMRelay(relay) {
12
+ return async function* ({ events, factory, self }) {
13
+ const dmRelays = getDMRelaysEvent(events, self);
14
+ const operation = Array.isArray(relay) ? relay.map((r) => addRelayTag(r)) : addRelayTag(relay);
15
+ const draft = await factory.modifyTags(dmRelays, operation);
16
+ yield await factory.sign(draft);
17
+ };
18
+ }
19
+ /** An action that removes a relay from the 10050 DM relays event */
20
+ export function RemoveDMRelay(relay) {
21
+ return async function* ({ events, factory, self }) {
22
+ const dmRelays = getDMRelaysEvent(events, self);
23
+ const operation = Array.isArray(relay) ? relay.map((r) => removeRelayTag(r)) : removeRelayTag(relay);
24
+ const draft = await factory.modifyTags(dmRelays, operation);
25
+ yield await factory.sign(draft);
26
+ };
27
+ }
28
+ /** Creates a new DM relays event */
29
+ export function NewDMRelays(relays) {
30
+ return async function* ({ events, factory, self }) {
31
+ const dmRelays = events.getReplaceable(kinds.DirectMessageRelaysList, self);
32
+ if (dmRelays)
33
+ throw new Error("DM relays event already exists");
34
+ const operations = relays?.map((r) => addRelayTag(r)) ?? [];
35
+ const draft = await factory.build({ kind: kinds.DirectMessageRelaysList }, modifyPublicTags(...operations));
36
+ yield await factory.sign(draft);
37
+ };
38
+ }
@@ -0,0 +1,18 @@
1
+ import { AddressPointer } from "nostr-tools/nip19";
2
+ import { Action } from "../action-hub.js";
3
+ /** An action that adds a relay to the 10012 favorite relays event */
4
+ export declare function AddFavoriteRelay(relay: string | string[], hidden?: boolean): Action;
5
+ /** An action that removes a relay from the 10012 favorite relays event */
6
+ export declare function RemoveFavoriteRelay(relay: string | string[], hidden?: boolean): Action;
7
+ /** An action that adds a relay set to the 10012 favorite relays event */
8
+ export declare function AddFavoriteRelaySet(addr: AddressPointer[] | AddressPointer, hidden?: boolean): Action;
9
+ /** An action that removes a relay set from the 10012 favorite relays event */
10
+ export declare function RemoveFavoriteRelaySet(addr: AddressPointer[] | AddressPointer, hidden?: boolean): Action;
11
+ /** Creates a new favorite relays event */
12
+ export declare function NewFavoriteRelays(relays?: string[] | {
13
+ public?: string[];
14
+ hidden?: string[];
15
+ }, sets?: AddressPointer[] | {
16
+ public?: AddressPointer[];
17
+ hidden?: AddressPointer[];
18
+ }): Action;
@@ -0,0 +1,75 @@
1
+ import { FAVORITE_RELAYS_KIND } from "applesauce-core/helpers/lists";
2
+ import { modifyHiddenTags, modifyPublicTags } from "applesauce-factory/operations/event";
3
+ import { addCoordinateTag, addRelayTag, removeCoordinateTag, removeRelayTag } from "applesauce-factory/operations/tag";
4
+ function getFavoriteRelaysEvent(events, self) {
5
+ const event = events.getReplaceable(FAVORITE_RELAYS_KIND, self);
6
+ if (!event)
7
+ throw new Error("Can't find favorite relays event");
8
+ return event;
9
+ }
10
+ /** An action that adds a relay to the 10012 favorite relays event */
11
+ export function AddFavoriteRelay(relay, hidden = false) {
12
+ return async function* ({ events, factory, self }) {
13
+ const favorites = getFavoriteRelaysEvent(events, self);
14
+ const operation = Array.isArray(relay) ? relay.map((r) => addRelayTag(r)) : addRelayTag(relay);
15
+ const draft = await factory.modifyTags(favorites, hidden ? { hidden: operation } : operation);
16
+ yield await factory.sign(draft);
17
+ };
18
+ }
19
+ /** An action that removes a relay from the 10012 favorite relays event */
20
+ export function RemoveFavoriteRelay(relay, hidden = false) {
21
+ return async function* ({ events, factory, self }) {
22
+ const favorites = getFavoriteRelaysEvent(events, self);
23
+ const operation = Array.isArray(relay) ? relay.map((r) => removeRelayTag(r)) : removeRelayTag(relay);
24
+ const draft = await factory.modifyTags(favorites, hidden ? { hidden: operation } : operation);
25
+ yield await factory.sign(draft);
26
+ };
27
+ }
28
+ /** An action that adds a relay set to the 10012 favorite relays event */
29
+ export function AddFavoriteRelaySet(addr, hidden = false) {
30
+ return async function* ({ events, factory, self }) {
31
+ const favorites = getFavoriteRelaysEvent(events, self);
32
+ const operation = Array.isArray(addr) ? addr.map((a) => addCoordinateTag(a)) : addCoordinateTag(addr);
33
+ const draft = await factory.modifyTags(favorites, hidden ? { hidden: operation } : operation);
34
+ yield await factory.sign(draft);
35
+ };
36
+ }
37
+ /** An action that removes a relay set from the 10012 favorite relays event */
38
+ export function RemoveFavoriteRelaySet(addr, hidden = false) {
39
+ return async function* ({ events, factory, self }) {
40
+ const favorites = getFavoriteRelaysEvent(events, self);
41
+ const operation = Array.isArray(addr) ? addr.map((a) => removeCoordinateTag(a)) : removeCoordinateTag(addr);
42
+ const draft = await factory.modifyTags(favorites, hidden ? { hidden: operation } : operation);
43
+ yield await factory.sign(draft);
44
+ };
45
+ }
46
+ /** Creates a new favorite relays event */
47
+ export function NewFavoriteRelays(relays, sets) {
48
+ return async function* ({ events, factory, self }) {
49
+ const favorites = events.getReplaceable(FAVORITE_RELAYS_KIND, self);
50
+ if (favorites)
51
+ throw new Error("Favorite relays event already exists");
52
+ let publicOperations = [];
53
+ let hiddenOperations = [];
54
+ if (Array.isArray(relays)) {
55
+ publicOperations.push(...relays.map((r) => addRelayTag(r)));
56
+ }
57
+ else {
58
+ if (relays?.public)
59
+ publicOperations.push(...(relays?.public ?? []).map((r) => addRelayTag(r)));
60
+ if (relays?.hidden)
61
+ hiddenOperations.push(...(relays?.hidden ?? []).map((r) => addRelayTag(r)));
62
+ }
63
+ if (Array.isArray(sets)) {
64
+ publicOperations.push(...sets.map((s) => addCoordinateTag(s)));
65
+ }
66
+ else {
67
+ if (sets?.public)
68
+ publicOperations.push(...(sets?.public ?? []).map((s) => addCoordinateTag(s)));
69
+ if (sets?.hidden)
70
+ hiddenOperations.push(...(sets?.hidden ?? []).map((s) => addCoordinateTag(s)));
71
+ }
72
+ const draft = await factory.build({ kind: FAVORITE_RELAYS_KIND }, publicOperations.length ? modifyPublicTags(...publicOperations) : undefined, hiddenOperations.length ? modifyHiddenTags(...hiddenOperations) : undefined);
73
+ yield await factory.sign(draft);
74
+ };
75
+ }
@@ -1,17 +1,43 @@
1
1
  import { NostrEvent } from "nostr-tools";
2
- import { Action } from "../action-hub.js";
3
2
  import { ProfilePointer } from "nostr-tools/nip19";
3
+ import { Action } from "../action-hub.js";
4
+ /**
5
+ * An action that creates a new follow set
6
+ * @param identifier the "d" tag of the follow set
7
+ * @param pubkeys the pubkeys to add to the follow set
8
+ * @param hidden set to true to create a hidden follow set
9
+ * @throws if a follow set already exists
10
+ */
11
+ export declare function CreateFollowSet(title: string, options?: {
12
+ description?: string;
13
+ image?: string;
14
+ public?: (string | ProfilePointer)[];
15
+ hidden?: (string | ProfilePointer)[];
16
+ }): Action;
4
17
  /**
5
18
  * An action that adds a pubkey to a follow set
6
19
  * @param pubkey the pubkey to add to the set
7
20
  * @param identifier the "d" tag of the follow set
8
21
  * @param hidden set to true to add to hidden follows
22
+ * @throws if the follow set does not exist
9
23
  */
10
- export declare function AddUserToFollowSet(pubkey: string | ProfilePointer, identifier: NostrEvent | string, hidden?: boolean): Action;
24
+ export declare function AddUserToFollowSet(pubkey: (string | ProfilePointer)[] | string | ProfilePointer, identifier: NostrEvent | string, hidden?: boolean): Action;
11
25
  /**
12
26
  * An action that removes a pubkey from a follow set
13
27
  * @param pubkey the pubkey to remove from the set
14
28
  * @param identifier the "d" tag of the follow set
15
29
  * @param hidden set to true to remove from hidden follows
30
+ * @throws if the follow set does not exist
31
+ */
32
+ export declare function RemoveUserFromFollowSet(pubkey: (string | ProfilePointer)[] | string | ProfilePointer, identifier: NostrEvent | string, hidden?: boolean): Action;
33
+ /**
34
+ * An action that updates the title, description, or image of a follow set
35
+ * @param identifier the "d" tag of the follow set
36
+ * @param info the new information for the follow set
37
+ * @throws if the follow set does not exist
16
38
  */
17
- export declare function RemoveUserFromFollowSet(pubkey: string | ProfilePointer, identifier: NostrEvent | string, hidden?: boolean): Action;
39
+ export declare function UpdateFollowSetInformation(identifier: string, info: {
40
+ title?: string;
41
+ description?: string;
42
+ image?: string;
43
+ }): Action;
@@ -1,5 +1,6 @@
1
- import { kinds } from "nostr-tools";
1
+ import { modifyHiddenTags, modifyPublicTags, setListDescription, setListImage, setListTitle, } from "applesauce-factory/operations/event";
2
2
  import { addPubkeyTag, removePubkeyTag } from "applesauce-factory/operations/tag";
3
+ import { kinds } from "nostr-tools";
3
4
  function getFollowSetEvent(events, self, identifier) {
4
5
  const set = typeof identifier === "string" ? events.getReplaceable(kinds.Followsets, self, identifier) : identifier;
5
6
  if (!set)
@@ -8,17 +9,35 @@ function getFollowSetEvent(events, self, identifier) {
8
9
  throw new Error("Event is not a follow set");
9
10
  return set;
10
11
  }
12
+ /**
13
+ * An action that creates a new follow set
14
+ * @param identifier the "d" tag of the follow set
15
+ * @param pubkeys the pubkeys to add to the follow set
16
+ * @param hidden set to true to create a hidden follow set
17
+ * @throws if a follow set already exists
18
+ */
19
+ export function CreateFollowSet(title, options) {
20
+ return async function* ({ factory }) {
21
+ const draft = await factory.build({ kind: kinds.Followsets },
22
+ // set list information
23
+ setListTitle(title), options?.description ? setListDescription(options.description) : undefined, options?.image ? setListImage(options.image) : undefined,
24
+ // add pubkey tags
25
+ options?.public ? modifyPublicTags(...options.public.map((p) => addPubkeyTag(p))) : undefined, options?.hidden ? modifyHiddenTags(...options.hidden.map((p) => addPubkeyTag(p))) : undefined);
26
+ yield await factory.sign(draft);
27
+ };
28
+ }
11
29
  /**
12
30
  * An action that adds a pubkey to a follow set
13
31
  * @param pubkey the pubkey to add to the set
14
32
  * @param identifier the "d" tag of the follow set
15
33
  * @param hidden set to true to add to hidden follows
34
+ * @throws if the follow set does not exist
16
35
  */
17
36
  export function AddUserToFollowSet(pubkey, identifier, hidden = false) {
18
37
  return async function* ({ events, factory, self }) {
19
38
  const follows = getFollowSetEvent(events, self, identifier);
20
- const operation = addPubkeyTag(pubkey);
21
- const draft = await factory.modifyTags(follows, hidden ? { hidden: operation } : operation);
39
+ const operations = Array.isArray(pubkey) ? pubkey.map((p) => addPubkeyTag(p)) : addPubkeyTag(pubkey);
40
+ const draft = await factory.modifyTags(follows, hidden ? { hidden: operations } : operations);
22
41
  yield await factory.sign(draft);
23
42
  };
24
43
  }
@@ -27,12 +46,26 @@ export function AddUserToFollowSet(pubkey, identifier, hidden = false) {
27
46
  * @param pubkey the pubkey to remove from the set
28
47
  * @param identifier the "d" tag of the follow set
29
48
  * @param hidden set to true to remove from hidden follows
49
+ * @throws if the follow set does not exist
30
50
  */
31
51
  export function RemoveUserFromFollowSet(pubkey, identifier, hidden = false) {
32
52
  return async function* ({ events, factory, self }) {
33
53
  const follows = getFollowSetEvent(events, self, identifier);
34
- const operation = removePubkeyTag(pubkey);
35
- const draft = await factory.modifyTags(follows, hidden ? { hidden: operation } : operation);
54
+ const operations = Array.isArray(pubkey) ? pubkey.map((p) => removePubkeyTag(p)) : removePubkeyTag(pubkey);
55
+ const draft = await factory.modifyTags(follows, hidden ? { hidden: operations } : operations);
56
+ yield await factory.sign(draft);
57
+ };
58
+ }
59
+ /**
60
+ * An action that updates the title, description, or image of a follow set
61
+ * @param identifier the "d" tag of the follow set
62
+ * @param info the new information for the follow set
63
+ * @throws if the follow set does not exist
64
+ */
65
+ export function UpdateFollowSetInformation(identifier, info) {
66
+ return async function* ({ events, factory, self }) {
67
+ const follows = getFollowSetEvent(events, self, identifier);
68
+ const draft = await factory.modify(follows, info?.title ? setListTitle(info.title) : undefined, info?.description ? setListDescription(info.description) : undefined, info?.image ? setListImage(info.image) : undefined);
36
69
  yield await factory.sign(draft);
37
70
  };
38
71
  }
@@ -1,5 +1,14 @@
1
+ export * from "./blocked-relays.js";
2
+ export * from "./blossom.js";
1
3
  export * from "./bookmarks.js";
2
4
  export * from "./contacts.js";
5
+ export * from "./dm-relays.js";
6
+ export * from "./favorite-relays.js";
3
7
  export * from "./follow-sets.js";
8
+ export * from "./lists.js";
9
+ export * from "./mailboxes.js";
10
+ export * from "./mute.js";
4
11
  export * from "./pins.js";
5
12
  export * from "./profile.js";
13
+ export * from "./relay-sets.js";
14
+ export * from "./search-relays.js";
@@ -1,5 +1,14 @@
1
+ export * from "./blocked-relays.js";
2
+ export * from "./blossom.js";
1
3
  export * from "./bookmarks.js";
2
4
  export * from "./contacts.js";
5
+ export * from "./dm-relays.js";
6
+ export * from "./favorite-relays.js";
3
7
  export * from "./follow-sets.js";
8
+ export * from "./lists.js";
9
+ export * from "./mailboxes.js";
10
+ export * from "./mute.js";
4
11
  export * from "./pins.js";
5
12
  export * from "./profile.js";
13
+ export * from "./relay-sets.js";
14
+ export * from "./search-relays.js";
@@ -0,0 +1,9 @@
1
+ import { NostrEvent } from "nostr-tools";
2
+ import { AddressPointer } from "nostr-tools/nip19";
3
+ import { Action } from "../action-hub.js";
4
+ /** An action that sets or removes a NIP-15 list information */
5
+ export declare function SetListMetadata(list: NostrEvent | AddressPointer, info: {
6
+ title?: string;
7
+ description?: string;
8
+ image?: string;
9
+ }): Action;
@@ -0,0 +1,18 @@
1
+ import { isAddressPointer } from "applesauce-core/helpers";
2
+ import { setListDescription, setListImage, setListTitle } from "applesauce-factory/operations/event";
3
+ function getList(events, address) {
4
+ const list = isAddressPointer(address)
5
+ ? events.getReplaceable(address.kind, address.pubkey, address.identifier)
6
+ : address;
7
+ if (!list)
8
+ throw new Error("Can't find list");
9
+ return list;
10
+ }
11
+ /** An action that sets or removes a NIP-15 list information */
12
+ export function SetListMetadata(list, info) {
13
+ return async function* ({ events, factory }) {
14
+ list = getList(events, list);
15
+ const draft = await factory.modify(list, setListTitle(info.title ?? null), setListDescription(info.description ?? null), setListImage(info.image ?? null));
16
+ yield await factory.sign(draft);
17
+ };
18
+ }
@@ -0,0 +1,11 @@
1
+ import { Action } from "../action-hub.js";
2
+ /** An action to create a new kind 10002 relay list event */
3
+ export declare function CreateMailboxes(inboxes: string[], outboxes: string[]): Action;
4
+ /** An action to add an inbox relay to the kind 10002 relay list */
5
+ export declare function AddInboxRelay(relay: string | string[]): Action;
6
+ /** An action to remove an inbox relay from the kind 10002 relay list */
7
+ export declare function RemoveInboxRelay(relay: string | string[]): Action;
8
+ /** An action to add an outbox relay to the kind 10002 relay list */
9
+ export declare function AddOutboxRelay(relay: string | string[]): Action;
10
+ /** An action to remove an outbox relay from the kind 10002 relay list */
11
+ export declare function RemoveOutboxRelay(relay: string | string[]): Action;
@@ -0,0 +1,66 @@
1
+ import { kinds } from "nostr-tools";
2
+ import { addInboxRelay, addOutboxRelay, removeInboxRelay, removeOutboxRelay } from "applesauce-factory/operations/tag";
3
+ import { modifyPublicTags } from "applesauce-factory/operations/event";
4
+ /** An action to create a new kind 10002 relay list event */
5
+ export function CreateMailboxes(inboxes, outboxes) {
6
+ return async function* ({ events, factory, self }) {
7
+ const mailboxes = events.getReplaceable(kinds.RelayList, self);
8
+ if (mailboxes)
9
+ throw new Error("Mailbox event already exists");
10
+ const draft = await factory.build({ kind: kinds.RelayList }, modifyPublicTags(...inboxes.map(addInboxRelay), ...outboxes.map(addOutboxRelay)));
11
+ const signed = await factory.sign(draft);
12
+ yield signed;
13
+ };
14
+ }
15
+ /** An action to add an inbox relay to the kind 10002 relay list */
16
+ export function AddInboxRelay(relay) {
17
+ return async function* ({ events, factory, self }) {
18
+ if (typeof relay === "string")
19
+ relay = [relay];
20
+ const mailboxes = events.getReplaceable(kinds.RelayList, self);
21
+ if (!mailboxes)
22
+ throw new Error("Missing mailboxes event");
23
+ const draft = await factory.modifyTags(mailboxes, ...relay.map(addInboxRelay));
24
+ const signed = await factory.sign(draft);
25
+ yield signed;
26
+ };
27
+ }
28
+ /** An action to remove an inbox relay from the kind 10002 relay list */
29
+ export function RemoveInboxRelay(relay) {
30
+ return async function* ({ events, factory, self }) {
31
+ if (typeof relay === "string")
32
+ relay = [relay];
33
+ const mailboxes = events.getReplaceable(kinds.RelayList, self);
34
+ if (!mailboxes)
35
+ throw new Error("Missing mailboxes event");
36
+ const draft = await factory.modifyTags(mailboxes, ...relay.map(removeInboxRelay));
37
+ const signed = await factory.sign(draft);
38
+ yield signed;
39
+ };
40
+ }
41
+ /** An action to add an outbox relay to the kind 10002 relay list */
42
+ export function AddOutboxRelay(relay) {
43
+ return async function* ({ events, factory, self }) {
44
+ if (typeof relay === "string")
45
+ relay = [relay];
46
+ const mailboxes = events.getReplaceable(kinds.RelayList, self);
47
+ if (!mailboxes)
48
+ throw new Error("Missing mailboxes event");
49
+ const draft = await factory.modifyTags(mailboxes, ...relay.map(addOutboxRelay));
50
+ const signed = await factory.sign(draft);
51
+ yield signed;
52
+ };
53
+ }
54
+ /** An action to remove an outbox relay from the kind 10002 relay list */
55
+ export function RemoveOutboxRelay(relay) {
56
+ return async function* ({ events, factory, self }) {
57
+ if (typeof relay === "string")
58
+ relay = [relay];
59
+ const mailboxes = events.getReplaceable(kinds.RelayList, self);
60
+ if (!mailboxes)
61
+ throw new Error("Missing mailboxes event");
62
+ const draft = await factory.modifyTags(mailboxes, ...relay.map(removeOutboxRelay));
63
+ const signed = await factory.sign(draft);
64
+ yield signed;
65
+ };
66
+ }
@@ -0,0 +1,19 @@
1
+ import { NostrEvent } from "nostr-tools";
2
+ import { EventPointer } from "nostr-tools/nip19";
3
+ import { Action } from "../action-hub.js";
4
+ /** An action that adds a pubkey to the mute list */
5
+ export declare function MuteUser(pubkey: string, hidden?: boolean): Action;
6
+ /** Removes a pubkey from the mute list */
7
+ export declare function UnmuteUser(pubkey: string, hidden?: boolean): Action;
8
+ /** Add a thread to the mute list */
9
+ export declare function MuteThread(thread: string | NostrEvent | EventPointer, hidden?: boolean): Action;
10
+ /** Removes a thread from the mute list */
11
+ export declare function UnmuteThread(thread: string | NostrEvent | EventPointer, hidden?: boolean): Action;
12
+ /** Add a word to the mute list */
13
+ export declare function MuteWord(word: string, hidden?: boolean): Action;
14
+ /** Removes a word from the mute list */
15
+ export declare function UnmuteWord(word: string, hidden?: boolean): Action;
16
+ /** Add a hashtag to the mute list */
17
+ export declare function MuteHashtag(hashtag: string, hidden?: boolean): Action;
18
+ /** Removes a hashtag from the mute list */
19
+ export declare function UnmuteHashtag(hashtag: string, hidden?: boolean): Action;
@@ -0,0 +1,79 @@
1
+ import { addEventTag, addNameValueTag, addPubkeyTag, removeEventTag, removeNameValueTag, removePubkeyTag, } from "applesauce-factory/operations/tag";
2
+ import { kinds } from "nostr-tools";
3
+ function ensureMuteList(events, self) {
4
+ const mute = events.getReplaceable(kinds.Mutelist, self);
5
+ if (!mute)
6
+ throw new Error("No mute list found");
7
+ return mute;
8
+ }
9
+ /** An action that adds a pubkey to the mute list */
10
+ export function MuteUser(pubkey, hidden = false) {
11
+ return async function* ({ events, factory, self }) {
12
+ const mute = ensureMuteList(events, self);
13
+ const operation = addPubkeyTag(pubkey);
14
+ const draft = await factory.modifyTags(mute, hidden ? { hidden: operation } : operation);
15
+ yield await factory.sign(draft);
16
+ };
17
+ }
18
+ /** Removes a pubkey from the mute list */
19
+ export function UnmuteUser(pubkey, hidden = false) {
20
+ return async function* ({ events, factory, self }) {
21
+ const mute = ensureMuteList(events, self);
22
+ const draft = await factory.modifyTags(mute, hidden ? { hidden: removePubkeyTag(pubkey) } : removePubkeyTag(pubkey));
23
+ yield await factory.sign(draft);
24
+ };
25
+ }
26
+ /** Add a thread to the mute list */
27
+ export function MuteThread(thread, hidden = false) {
28
+ return async function* ({ events, factory, self }) {
29
+ const mute = ensureMuteList(events, self);
30
+ const operation = addEventTag(thread);
31
+ const draft = await factory.modifyTags(mute, hidden ? { hidden: operation } : operation);
32
+ yield await factory.sign(draft);
33
+ };
34
+ }
35
+ /** Removes a thread from the mute list */
36
+ export function UnmuteThread(thread, hidden = false) {
37
+ return async function* ({ events, factory, self }) {
38
+ const mute = ensureMuteList(events, self);
39
+ const operation = removeEventTag(thread);
40
+ const draft = await factory.modifyTags(mute, hidden ? { hidden: operation } : operation);
41
+ yield await factory.sign(draft);
42
+ };
43
+ }
44
+ /** Add a word to the mute list */
45
+ export function MuteWord(word, hidden = false) {
46
+ return async function* ({ events, factory, self }) {
47
+ const mute = ensureMuteList(events, self);
48
+ const operation = addNameValueTag(["word", word.toLocaleLowerCase()], true);
49
+ const draft = await factory.modifyTags(mute, hidden ? { hidden: operation } : operation);
50
+ yield await factory.sign(draft);
51
+ };
52
+ }
53
+ /** Removes a word from the mute list */
54
+ export function UnmuteWord(word, hidden = false) {
55
+ return async function* ({ events, factory, self }) {
56
+ const mute = ensureMuteList(events, self);
57
+ const operation = removeNameValueTag(["word", word.toLocaleLowerCase()]);
58
+ const draft = await factory.modifyTags(mute, hidden ? { hidden: operation } : operation);
59
+ yield await factory.sign(draft);
60
+ };
61
+ }
62
+ /** Add a hashtag to the mute list */
63
+ export function MuteHashtag(hashtag, hidden = false) {
64
+ return async function* ({ events, factory, self }) {
65
+ const mute = ensureMuteList(events, self);
66
+ const operation = addNameValueTag(["t", hashtag.toLocaleLowerCase()], true);
67
+ const draft = await factory.modifyTags(mute, hidden ? { hidden: operation } : operation);
68
+ yield await factory.sign(draft);
69
+ };
70
+ }
71
+ /** Removes a hashtag from the mute list */
72
+ export function UnmuteHashtag(hashtag, hidden = false) {
73
+ return async function* ({ events, factory, self }) {
74
+ const mute = ensureMuteList(events, self);
75
+ const operation = removeNameValueTag(["t", hashtag.toLocaleLowerCase()]);
76
+ const draft = await factory.modifyTags(mute, hidden ? { hidden: operation } : operation);
77
+ yield await factory.sign(draft);
78
+ };
79
+ }
@@ -0,0 +1,19 @@
1
+ import { NostrEvent } from "nostr-tools";
2
+ import { Action } from "../action-hub.js";
3
+ /** An action that adds a relay to a relay set*/
4
+ export declare function AddRelayToRelaySet(relay: string | string[], identifier: NostrEvent | string, hidden?: boolean): Action;
5
+ /** An action that removes a relay from a relay set */
6
+ export declare function RemoveRelayFromRelaySet(relay: string | string[], identifier: NostrEvent | string, hidden?: boolean): Action;
7
+ /** An action that creates a new relay set */
8
+ export declare function CreateRelaySet(title: string, options?: {
9
+ description?: string;
10
+ image?: string;
11
+ public?: string[];
12
+ hidden?: string[];
13
+ }): Action;
14
+ /** An action that updates the title, description, or image of a relay set */
15
+ export declare function UpdateRelaySetInformation(identifier: string, info: {
16
+ title?: string;
17
+ description?: string;
18
+ image?: string;
19
+ }): Action;
@@ -0,0 +1,44 @@
1
+ import { modifyHiddenTags, modifyPublicTags, setListDescription, setListImage, setListTitle, } from "applesauce-factory/operations/event";
2
+ import { addRelayTag, removeRelayTag } from "applesauce-factory/operations/tag";
3
+ import { kinds } from "nostr-tools";
4
+ function getRelaySetEvent(events, self, identifier) {
5
+ const set = typeof identifier === "string" ? events.getReplaceable(kinds.Relaysets, self, identifier) : identifier;
6
+ if (!set)
7
+ throw new Error("Can't find relay set");
8
+ if (set.kind !== kinds.Relaysets)
9
+ throw new Error("Event is not a relay set");
10
+ return set;
11
+ }
12
+ /** An action that adds a relay to a relay set*/
13
+ export function AddRelayToRelaySet(relay, identifier, hidden = false) {
14
+ return async function* ({ events, factory, self }) {
15
+ const relays = getRelaySetEvent(events, self, identifier);
16
+ const operations = Array.isArray(relay) ? relay.map((r) => addRelayTag(r)) : addRelayTag(relay);
17
+ const draft = await factory.modifyTags(relays, hidden ? { hidden: operations } : operations);
18
+ yield await factory.sign(draft);
19
+ };
20
+ }
21
+ /** An action that removes a relay from a relay set */
22
+ export function RemoveRelayFromRelaySet(relay, identifier, hidden = false) {
23
+ return async function* ({ events, factory, self }) {
24
+ const relays = getRelaySetEvent(events, self, identifier);
25
+ const operations = Array.isArray(relay) ? relay.map((r) => removeRelayTag(r)) : removeRelayTag(relay);
26
+ const draft = await factory.modifyTags(relays, hidden ? { hidden: operations } : operations);
27
+ yield await factory.sign(draft);
28
+ };
29
+ }
30
+ /** An action that creates a new relay set */
31
+ export function CreateRelaySet(title, options) {
32
+ return async function* ({ factory }) {
33
+ const draft = await factory.build({ kind: kinds.Relaysets }, setListTitle(title), options?.description ? setListDescription(options.description) : undefined, options?.image ? setListImage(options.image) : undefined, options?.public ? modifyPublicTags(...options.public.map((r) => addRelayTag(r))) : undefined, options?.hidden ? modifyHiddenTags(...options.hidden.map((r) => addRelayTag(r))) : undefined);
34
+ yield await factory.sign(draft);
35
+ };
36
+ }
37
+ /** An action that updates the title, description, or image of a relay set */
38
+ export function UpdateRelaySetInformation(identifier, info) {
39
+ return async function* ({ events, factory, self }) {
40
+ const relays = getRelaySetEvent(events, self, identifier);
41
+ const draft = await factory.modify(relays, info?.title ? setListTitle(info.title) : undefined, info?.description ? setListDescription(info.description) : undefined, info?.image ? setListImage(info.image) : undefined);
42
+ yield await factory.sign(draft);
43
+ };
44
+ }
@@ -0,0 +1,10 @@
1
+ import { Action } from "../action-hub.js";
2
+ /** An action that adds a relay to the 10007 search relays event */
3
+ export declare function AddSearchRelay(relay: string | string[], hidden?: boolean): Action;
4
+ /** An action that removes a relay from the 10007 search relays event */
5
+ export declare function RemoveSearchRelay(relay: string | string[], hidden?: boolean): Action;
6
+ /** Creates a new search relays event */
7
+ export declare function NewSearchRelays(relays?: string[] | {
8
+ public?: string[];
9
+ hidden?: string[];
10
+ }): Action;
@@ -0,0 +1,48 @@
1
+ import { modifyHiddenTags, modifyPublicTags } from "applesauce-factory/operations/event";
2
+ import { addRelayTag, removeRelayTag } from "applesauce-factory/operations/tag/relay";
3
+ import { kinds } from "nostr-tools";
4
+ function getSearchRelaysEvent(events, self) {
5
+ const event = events.getReplaceable(kinds.SearchRelaysList, self);
6
+ if (!event)
7
+ throw new Error("Can't find search relays event");
8
+ return event;
9
+ }
10
+ /** An action that adds a relay to the 10007 search relays event */
11
+ export function AddSearchRelay(relay, hidden = false) {
12
+ return async function* ({ events, factory, self }) {
13
+ const search = getSearchRelaysEvent(events, self);
14
+ const operation = Array.isArray(relay) ? relay.map((r) => addRelayTag(r)) : addRelayTag(relay);
15
+ const draft = await factory.modifyTags(search, hidden ? { hidden: operation } : operation);
16
+ yield await factory.sign(draft);
17
+ };
18
+ }
19
+ /** An action that removes a relay from the 10007 search relays event */
20
+ export function RemoveSearchRelay(relay, hidden = false) {
21
+ return async function* ({ events, factory, self }) {
22
+ const search = getSearchRelaysEvent(events, self);
23
+ const operation = Array.isArray(relay) ? relay.map((r) => removeRelayTag(r)) : removeRelayTag(relay);
24
+ const draft = await factory.modifyTags(search, hidden ? { hidden: operation } : operation);
25
+ yield await factory.sign(draft);
26
+ };
27
+ }
28
+ /** Creates a new search relays event */
29
+ export function NewSearchRelays(relays) {
30
+ return async function* ({ events, factory, self }) {
31
+ const search = events.getReplaceable(kinds.SearchRelaysList, self);
32
+ if (search)
33
+ throw new Error("Search relays event already exists");
34
+ let publicOperations = [];
35
+ let hiddenOperations = [];
36
+ if (Array.isArray(relays)) {
37
+ publicOperations.push(...relays.map((r) => addRelayTag(r)));
38
+ }
39
+ else {
40
+ if (relays?.public)
41
+ publicOperations.push(...(relays?.public ?? []).map((r) => addRelayTag(r)));
42
+ if (relays?.hidden)
43
+ hiddenOperations.push(...(relays?.hidden ?? []).map((r) => addRelayTag(r)));
44
+ }
45
+ const draft = await factory.build({ kind: kinds.SearchRelaysList }, publicOperations.length ? modifyPublicTags(...publicOperations) : undefined, hiddenOperations.length ? modifyHiddenTags(...hiddenOperations) : undefined);
46
+ yield await factory.sign(draft);
47
+ };
48
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "applesauce-actions",
3
- "version": "0.12.0",
3
+ "version": "1.1.0",
4
4
  "description": "A package for performing common nostr actions",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -32,18 +32,18 @@
32
32
  }
33
33
  },
34
34
  "dependencies": {
35
- "applesauce-core": "^0.12.0",
36
- "applesauce-factory": "^0.12.0",
35
+ "applesauce-core": "^1.0.0",
36
+ "applesauce-factory": "^1.1.0",
37
37
  "nostr-tools": "^2.10.4",
38
38
  "rxjs": "^7.8.1"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@hirez_io/observer-spy": "^2.2.0",
42
42
  "@types/debug": "^4.1.12",
43
- "applesauce-signers": "^0.12.0",
44
- "nanoid": "^5.0.9",
45
- "typescript": "^5.7.3",
46
- "vitest": "^3.0.5"
43
+ "applesauce-signers": "^1.0.0",
44
+ "nanoid": "^5.1.5",
45
+ "typescript": "^5.8.3",
46
+ "vitest": "^3.1.1"
47
47
  },
48
48
  "funding": {
49
49
  "type": "lightning",