@fedify/botkit 0.3.0-dev.108
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/LICENSE +661 -0
- package/README.md +75 -0
- package/dist/bot-impl.d.ts +111 -0
- package/dist/bot-impl.d.ts.map +1 -0
- package/dist/bot-impl.js +602 -0
- package/dist/bot-impl.js.map +1 -0
- package/dist/bot-impl.test.d.ts +2 -0
- package/dist/bot-impl.test.js +1642 -0
- package/dist/bot-impl.test.js.map +1 -0
- package/dist/bot.d.ts +270 -0
- package/dist/bot.d.ts.map +1 -0
- package/dist/bot.js +118 -0
- package/dist/bot.js.map +1 -0
- package/dist/bot.test.d.ts +2 -0
- package/dist/bot.test.js +80 -0
- package/dist/bot.test.js.map +1 -0
- package/dist/components/Layout.d.ts +26 -0
- package/dist/components/Layout.d.ts.map +1 -0
- package/dist/components/Layout.js +36 -0
- package/dist/components/Layout.js.map +1 -0
- package/dist/components/Message.d.ts +21 -0
- package/dist/components/Message.d.ts.map +1 -0
- package/dist/components/Message.js +99 -0
- package/dist/components/Message.js.map +1 -0
- package/dist/components/Message.test.d.ts +2 -0
- package/dist/components/Message.test.js +32 -0
- package/dist/components/Message.test.js.map +1 -0
- package/dist/deno.js +73 -0
- package/dist/deno.js.map +1 -0
- package/dist/emoji.d.ts +87 -0
- package/dist/emoji.d.ts.map +1 -0
- package/dist/emoji.js +37 -0
- package/dist/emoji.js.map +1 -0
- package/dist/emoji.test.d.ts +2 -0
- package/dist/emoji.test.js +109 -0
- package/dist/emoji.test.js.map +1 -0
- package/dist/events.d.ts +110 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +4 -0
- package/dist/follow-impl.d.ts +23 -0
- package/dist/follow-impl.d.ts.map +1 -0
- package/dist/follow-impl.js +51 -0
- package/dist/follow-impl.js.map +1 -0
- package/dist/follow-impl.test.d.ts +2 -0
- package/dist/follow-impl.test.js +110 -0
- package/dist/follow-impl.test.js.map +1 -0
- package/dist/follow.d.ts +44 -0
- package/dist/follow.d.ts.map +1 -0
- package/dist/follow.js +4 -0
- package/dist/message-impl.d.ts +54 -0
- package/dist/message-impl.d.ts.map +1 -0
- package/dist/message-impl.js +439 -0
- package/dist/message-impl.js.map +1 -0
- package/dist/message-impl.test.d.ts +2 -0
- package/dist/message-impl.test.js +519 -0
- package/dist/message-impl.test.js.map +1 -0
- package/dist/message.d.ts +223 -0
- package/dist/message.d.ts.map +1 -0
- package/dist/message.js +8 -0
- package/dist/mod.d.ts +13 -0
- package/dist/mod.js +13 -0
- package/dist/pages.d.ts +20 -0
- package/dist/pages.d.ts.map +1 -0
- package/dist/pages.js +361 -0
- package/dist/pages.js.map +1 -0
- package/dist/reaction.d.ts +90 -0
- package/dist/reaction.d.ts.map +1 -0
- package/dist/reaction.js +7 -0
- package/dist/repository.d.ts +323 -0
- package/dist/repository.d.ts.map +1 -0
- package/dist/repository.js +483 -0
- package/dist/repository.js.map +1 -0
- package/dist/repository.test.d.ts +2 -0
- package/dist/repository.test.js +336 -0
- package/dist/repository.test.js.map +1 -0
- package/dist/session-impl.d.ts +32 -0
- package/dist/session-impl.d.ts.map +1 -0
- package/dist/session-impl.js +195 -0
- package/dist/session-impl.js.map +1 -0
- package/dist/session-impl.test.d.ts +20 -0
- package/dist/session-impl.test.d.ts.map +1 -0
- package/dist/session-impl.test.js +464 -0
- package/dist/session-impl.test.js.map +1 -0
- package/dist/session.d.ts +139 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +4 -0
- package/dist/text.d.ts +391 -0
- package/dist/text.d.ts.map +1 -0
- package/dist/text.js +640 -0
- package/dist/text.js.map +1 -0
- package/dist/text.test.d.ts +2 -0
- package/dist/text.test.js +473 -0
- package/dist/text.test.js.map +1 -0
- package/package.json +137 -0
|
@@ -0,0 +1,1642 @@
|
|
|
1
|
+
|
|
2
|
+
import { Temporal, toTemporalInstant } from "@js-temporal/polyfill";
|
|
3
|
+
Date.prototype.toTemporalInstant = toTemporalInstant;
|
|
4
|
+
|
|
5
|
+
import { MemoryRepository } from "./repository.js";
|
|
6
|
+
import { SessionImpl } from "./session-impl.js";
|
|
7
|
+
import { BotImpl } from "./bot-impl.js";
|
|
8
|
+
import { parseSemVer } from "./bot.js";
|
|
9
|
+
import { mention, strong, text } from "./text.js";
|
|
10
|
+
import { MemoryKvStore } from "@fedify/fedify/federation";
|
|
11
|
+
import { Accept, Announce, Article, Create, CryptographicKey, Emoji, EmojiReact, Follow, Image, Like, Mention, Note, PUBLIC_COLLECTION, Person, Place, PropertyValue, Reject, Service, Undo } from "@fedify/fedify/vocab";
|
|
12
|
+
import assert from "node:assert";
|
|
13
|
+
import { describe, test } from "node:test";
|
|
14
|
+
|
|
15
|
+
//#region src/bot-impl.test.ts
|
|
16
|
+
describe("BotImpl.getActorSummary()", () => {
|
|
17
|
+
test("without summary", async () => {
|
|
18
|
+
const bot = new BotImpl({
|
|
19
|
+
kv: new MemoryKvStore(),
|
|
20
|
+
username: "bot"
|
|
21
|
+
});
|
|
22
|
+
const session = bot.getSession("https://example.com");
|
|
23
|
+
assert.deepStrictEqual(await bot.getActorSummary(session), null);
|
|
24
|
+
});
|
|
25
|
+
test("with summary", async () => {
|
|
26
|
+
const actor = new Person({
|
|
27
|
+
id: new URL("https://example.com/actor/john"),
|
|
28
|
+
preferredUsername: "john"
|
|
29
|
+
});
|
|
30
|
+
const bot = new BotImpl({
|
|
31
|
+
kv: new MemoryKvStore(),
|
|
32
|
+
username: "bot",
|
|
33
|
+
summary: text`A summary with a mention: ${actor}.`
|
|
34
|
+
});
|
|
35
|
+
const session = bot.getSession("https://example.com");
|
|
36
|
+
const expected = {
|
|
37
|
+
tags: [new Mention({
|
|
38
|
+
href: new URL("https://example.com/actor/john"),
|
|
39
|
+
name: "@john@example.com"
|
|
40
|
+
})],
|
|
41
|
+
text: "<p>A summary with a mention: <a href=\"https://example.com/actor/john\" translate=\"no\" class=\"h-card u-url mention\" target=\"_blank\">@<span>john@example.com</span></a>.</p>"
|
|
42
|
+
};
|
|
43
|
+
assert.deepStrictEqual(await bot.getActorSummary(session), expected);
|
|
44
|
+
assert.deepStrictEqual(await bot.getActorSummary(session), expected);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
test("BotImpl.getActorProperties()", async () => {
|
|
48
|
+
const actor = new Person({
|
|
49
|
+
id: new URL("https://example.com/actor/john"),
|
|
50
|
+
preferredUsername: "john"
|
|
51
|
+
});
|
|
52
|
+
const bot = new BotImpl({
|
|
53
|
+
kv: new MemoryKvStore(),
|
|
54
|
+
username: "bot",
|
|
55
|
+
properties: {
|
|
56
|
+
Foo: strong("bar"),
|
|
57
|
+
Baz: mention(actor),
|
|
58
|
+
Qux: text`quux`
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
const session = bot.getSession("https://example.com");
|
|
62
|
+
const expected = {
|
|
63
|
+
pairs: [
|
|
64
|
+
new PropertyValue({
|
|
65
|
+
name: "Foo",
|
|
66
|
+
value: "<strong>bar</strong>"
|
|
67
|
+
}),
|
|
68
|
+
new PropertyValue({
|
|
69
|
+
name: "Baz",
|
|
70
|
+
value: "<a href=\"https://example.com/actor/john\" translate=\"no\" class=\"h-card u-url mention\" target=\"_blank\">@<span>john@example.com</span></a>"
|
|
71
|
+
}),
|
|
72
|
+
new PropertyValue({
|
|
73
|
+
name: "Qux",
|
|
74
|
+
value: "<p>quux</p>"
|
|
75
|
+
})
|
|
76
|
+
],
|
|
77
|
+
tags: [new Mention({
|
|
78
|
+
href: new URL("https://example.com/actor/john"),
|
|
79
|
+
name: "@john@example.com"
|
|
80
|
+
})]
|
|
81
|
+
};
|
|
82
|
+
assert.deepStrictEqual(await bot.getActorProperties(session), expected);
|
|
83
|
+
assert.deepStrictEqual(await bot.getActorProperties(session), expected);
|
|
84
|
+
});
|
|
85
|
+
test("BotImpl.dispatchActor()", async () => {
|
|
86
|
+
const mentionActor = new Person({
|
|
87
|
+
id: new URL("https://example.com/actor/john"),
|
|
88
|
+
preferredUsername: "john"
|
|
89
|
+
});
|
|
90
|
+
const repository = new MemoryRepository();
|
|
91
|
+
const bot = new BotImpl({
|
|
92
|
+
kv: new MemoryKvStore(),
|
|
93
|
+
repository,
|
|
94
|
+
username: "test",
|
|
95
|
+
name: "Test Bot",
|
|
96
|
+
summary: text`A summary with a mention: ${mentionActor}.`,
|
|
97
|
+
properties: {
|
|
98
|
+
Foo: strong("bar"),
|
|
99
|
+
Baz: mention(mentionActor),
|
|
100
|
+
Qux: text`quux`
|
|
101
|
+
},
|
|
102
|
+
icon: new URL("https://example.com/icon.png"),
|
|
103
|
+
image: new URL("https://example.com/image.png")
|
|
104
|
+
});
|
|
105
|
+
const ctx = bot.federation.createContext(new URL("https://example.com"), void 0);
|
|
106
|
+
assert.deepStrictEqual(await bot.dispatchActor(ctx, "non-existent"), null);
|
|
107
|
+
const actor = await bot.dispatchActor(ctx, "bot");
|
|
108
|
+
assert.ok(actor instanceof Service);
|
|
109
|
+
assert.deepStrictEqual(actor.id, new URL("https://example.com/ap/actor/bot"));
|
|
110
|
+
assert.deepStrictEqual(actor.preferredUsername, "test");
|
|
111
|
+
assert.deepStrictEqual(actor.name, "Test Bot");
|
|
112
|
+
assert.deepStrictEqual(actor.summary, "<p>A summary with a mention: <a href=\"https://example.com/actor/john\" translate=\"no\" class=\"h-card u-url mention\" target=\"_blank\">@<span>john@example.com</span></a>.</p>");
|
|
113
|
+
const attachments = await Array.fromAsync(actor.getAttachments());
|
|
114
|
+
assert.deepStrictEqual(attachments.length, 3);
|
|
115
|
+
assert.ok(attachments[0] instanceof PropertyValue);
|
|
116
|
+
assert.deepStrictEqual(attachments[0].name, "Foo");
|
|
117
|
+
assert.deepStrictEqual(attachments[0].value, "<strong>bar</strong>");
|
|
118
|
+
assert.ok(attachments[1] instanceof PropertyValue);
|
|
119
|
+
assert.deepStrictEqual(attachments[1].name, "Baz");
|
|
120
|
+
assert.deepStrictEqual(attachments[1].value, "<a href=\"https://example.com/actor/john\" translate=\"no\" class=\"h-card u-url mention\" target=\"_blank\">@<span>john@example.com</span></a>");
|
|
121
|
+
assert.ok(attachments[2] instanceof PropertyValue);
|
|
122
|
+
assert.deepStrictEqual(attachments[2].name, "Qux");
|
|
123
|
+
assert.deepStrictEqual(attachments[2].value, "<p>quux</p>");
|
|
124
|
+
const tags = await Array.fromAsync(actor.getTags());
|
|
125
|
+
assert.deepStrictEqual(tags.length, 1);
|
|
126
|
+
assert.ok(tags[0] instanceof Mention);
|
|
127
|
+
assert.deepStrictEqual(tags[0].href, new URL("https://example.com/actor/john"));
|
|
128
|
+
assert.deepStrictEqual(tags[0].name, "@john@example.com");
|
|
129
|
+
const icon = await actor.getIcon();
|
|
130
|
+
assert.ok(icon instanceof Image);
|
|
131
|
+
assert.deepStrictEqual(icon.url, new URL("https://example.com/icon.png"));
|
|
132
|
+
const image = await actor.getImage();
|
|
133
|
+
assert.ok(image instanceof Image);
|
|
134
|
+
assert.deepStrictEqual(image.url, new URL("https://example.com/image.png"));
|
|
135
|
+
assert.deepStrictEqual(actor.inboxId, new URL("https://example.com/ap/actor/bot/inbox"));
|
|
136
|
+
assert.deepStrictEqual(actor.endpoints?.sharedInbox, new URL("https://example.com/ap/inbox"));
|
|
137
|
+
assert.deepStrictEqual(actor.followersId, new URL("https://example.com/ap/actor/bot/followers"));
|
|
138
|
+
assert.deepStrictEqual(actor.outboxId, new URL("https://example.com/ap/actor/bot/outbox"));
|
|
139
|
+
const publicKey = await actor.getPublicKey();
|
|
140
|
+
assert.ok(publicKey != null);
|
|
141
|
+
assert.deepStrictEqual(publicKey.ownerId, actor.id);
|
|
142
|
+
assert.ok(publicKey.publicKey != null);
|
|
143
|
+
const keys = await repository.getKeyPairs();
|
|
144
|
+
assert.ok(keys != null);
|
|
145
|
+
assert.deepStrictEqual(publicKey.publicKey, keys[0].publicKey);
|
|
146
|
+
const assertionMethods = await Array.fromAsync(actor.getAssertionMethods());
|
|
147
|
+
assert.deepStrictEqual(assertionMethods.length, 2);
|
|
148
|
+
assert.deepStrictEqual(assertionMethods.map((mk) => mk.controllerId), [actor.id, actor.id]);
|
|
149
|
+
assert.deepStrictEqual(await Promise.all(assertionMethods.map((mk) => mk.publicKey)), keys.map((k) => k.publicKey));
|
|
150
|
+
});
|
|
151
|
+
test("BotImpl.mapHandle()", () => {
|
|
152
|
+
const bot = new BotImpl({
|
|
153
|
+
kv: new MemoryKvStore(),
|
|
154
|
+
username: "username"
|
|
155
|
+
});
|
|
156
|
+
const ctx = bot.federation.createContext(new URL("https://example.com"), void 0);
|
|
157
|
+
assert.deepStrictEqual(bot.mapHandle(ctx, "non-existent"), null);
|
|
158
|
+
assert.deepStrictEqual(bot.mapHandle(ctx, "username"), "bot");
|
|
159
|
+
});
|
|
160
|
+
test("BotImpl.dispatchActorKeyPairs()", async () => {
|
|
161
|
+
const repository = new MemoryRepository();
|
|
162
|
+
const bot = new BotImpl({
|
|
163
|
+
kv: new MemoryKvStore(),
|
|
164
|
+
repository,
|
|
165
|
+
username: "bot"
|
|
166
|
+
});
|
|
167
|
+
const ctx = bot.federation.createContext(new URL("https://example.com"), void 0);
|
|
168
|
+
assert.deepStrictEqual(await bot.dispatchActorKeyPairs(ctx, "non-existent"), []);
|
|
169
|
+
const keyPairs = await bot.dispatchActorKeyPairs(ctx, "bot");
|
|
170
|
+
const storedKeyPairs = await repository.getKeyPairs();
|
|
171
|
+
assert.deepStrictEqual(keyPairs, storedKeyPairs);
|
|
172
|
+
const keyPairs2 = await bot.dispatchActorKeyPairs(ctx, "bot");
|
|
173
|
+
assert.deepStrictEqual(keyPairs2, storedKeyPairs);
|
|
174
|
+
});
|
|
175
|
+
test("BotImpl.dispatchFollowers()", async () => {
|
|
176
|
+
const repository = new MemoryRepository();
|
|
177
|
+
const bot = new BotImpl({
|
|
178
|
+
kv: new MemoryKvStore(),
|
|
179
|
+
repository,
|
|
180
|
+
username: "bot",
|
|
181
|
+
collectionWindow: 2
|
|
182
|
+
});
|
|
183
|
+
const ctx = bot.federation.createContext(new URL("https://example.com"), void 0);
|
|
184
|
+
assert.deepStrictEqual(await bot.dispatchFollowers(ctx, "non-existent", null), null);
|
|
185
|
+
assert.deepStrictEqual(await bot.dispatchFollowers(ctx, "non-existent", ""), null);
|
|
186
|
+
const empty = await bot.dispatchFollowers(ctx, "bot", null);
|
|
187
|
+
assert.deepStrictEqual(empty, {
|
|
188
|
+
items: [],
|
|
189
|
+
nextCursor: null
|
|
190
|
+
});
|
|
191
|
+
await repository.addFollower(new URL("https://example.com/actor/1#follow"), new Person({
|
|
192
|
+
id: new URL("https://example.com/actor/1"),
|
|
193
|
+
preferredUsername: "john",
|
|
194
|
+
inbox: new URL("https://example.com/actor/1/inbox")
|
|
195
|
+
}));
|
|
196
|
+
await repository.addFollower(new URL("https://example.com/actor/2#follow"), new Person({
|
|
197
|
+
id: new URL("https://example.com/actor/2"),
|
|
198
|
+
preferredUsername: "jane",
|
|
199
|
+
inbox: new URL("https://example.com/actor/2/inbox")
|
|
200
|
+
}));
|
|
201
|
+
await repository.addFollower(new URL("https://example.com/actor/3#follow"), new Person({
|
|
202
|
+
id: new URL("https://example.com/actor/3"),
|
|
203
|
+
preferredUsername: "joe",
|
|
204
|
+
inbox: new URL("https://example.com/actor/3/inbox")
|
|
205
|
+
}));
|
|
206
|
+
const full = await bot.dispatchFollowers(ctx, "bot", null);
|
|
207
|
+
assert.ok(full != null);
|
|
208
|
+
assert.deepStrictEqual(full.nextCursor, null);
|
|
209
|
+
assert.deepStrictEqual(full.items.length, 3);
|
|
210
|
+
const items = full.items.toSorted((a, b) => (a.id?.href ?? "").localeCompare(b.id?.href ?? ""));
|
|
211
|
+
assert.ok(items[0] instanceof Person);
|
|
212
|
+
assert.deepStrictEqual(items[0].id, new URL("https://example.com/actor/1"));
|
|
213
|
+
assert.ok(items[1] instanceof Person);
|
|
214
|
+
assert.deepStrictEqual(items[1].id, new URL("https://example.com/actor/2"));
|
|
215
|
+
assert.ok(items[2] instanceof Person);
|
|
216
|
+
assert.deepStrictEqual(items[2].id, new URL("https://example.com/actor/3"));
|
|
217
|
+
const firstPage = await bot.dispatchFollowers(ctx, "bot", "0");
|
|
218
|
+
assert.ok(firstPage != null);
|
|
219
|
+
assert.deepStrictEqual(firstPage.nextCursor, "2");
|
|
220
|
+
assert.deepStrictEqual(firstPage.items.length, 2);
|
|
221
|
+
assert.ok(firstPage.items[0] instanceof Person);
|
|
222
|
+
assert.deepStrictEqual(firstPage.items[0].id, new URL("https://example.com/actor/3"));
|
|
223
|
+
assert.ok(firstPage.items[1] instanceof Person);
|
|
224
|
+
assert.deepStrictEqual(firstPage.items[1].id, new URL("https://example.com/actor/2"));
|
|
225
|
+
const lastPage = await bot.dispatchFollowers(ctx, "bot", "2");
|
|
226
|
+
assert.ok(lastPage != null);
|
|
227
|
+
assert.deepStrictEqual(lastPage.nextCursor, null);
|
|
228
|
+
assert.deepStrictEqual(lastPage.items.length, 1);
|
|
229
|
+
assert.ok(lastPage.items[0] instanceof Person);
|
|
230
|
+
assert.deepStrictEqual(lastPage.items[0].id, new URL("https://example.com/actor/1"));
|
|
231
|
+
});
|
|
232
|
+
test("BotImpl.getFollowersFirstCursor()", () => {
|
|
233
|
+
const kv = new MemoryKvStore();
|
|
234
|
+
const bot = new BotImpl({
|
|
235
|
+
kv,
|
|
236
|
+
username: "bot",
|
|
237
|
+
collectionWindow: 2
|
|
238
|
+
});
|
|
239
|
+
const ctx = bot.federation.createContext(new URL("https://example.com"), void 0);
|
|
240
|
+
assert.deepStrictEqual(bot.getFollowersFirstCursor(ctx, "non-existent"), null);
|
|
241
|
+
assert.deepStrictEqual(bot.getFollowersFirstCursor(ctx, "bot"), "0");
|
|
242
|
+
});
|
|
243
|
+
test("BotImpl.countFollowers()", async () => {
|
|
244
|
+
const repository = new MemoryRepository();
|
|
245
|
+
const bot = new BotImpl({
|
|
246
|
+
kv: new MemoryKvStore(),
|
|
247
|
+
repository,
|
|
248
|
+
username: "bot"
|
|
249
|
+
});
|
|
250
|
+
const ctx = bot.federation.createContext(new URL("https://example.com"), void 0);
|
|
251
|
+
assert.deepStrictEqual(await bot.countFollowers(ctx, "non-existent"), null);
|
|
252
|
+
assert.deepStrictEqual(await bot.countFollowers(ctx, "bot"), 0);
|
|
253
|
+
await repository.addFollower(new URL("https://example.com/actor/1#follow"), new Person({
|
|
254
|
+
id: new URL("https://example.com/actor/1"),
|
|
255
|
+
preferredUsername: "john",
|
|
256
|
+
inbox: new URL("https://example.com/actor/1/inbox")
|
|
257
|
+
}));
|
|
258
|
+
await repository.addFollower(new URL("https://example.com/actor/2#follow"), new Person({
|
|
259
|
+
id: new URL("https://example.com/actor/2"),
|
|
260
|
+
preferredUsername: "jane",
|
|
261
|
+
inbox: new URL("https://example.com/actor/2/inbox")
|
|
262
|
+
}));
|
|
263
|
+
await repository.addFollower(new URL("https://example.com/actor/3#follow"), new Person({
|
|
264
|
+
id: new URL("https://example.com/actor/3"),
|
|
265
|
+
preferredUsername: "joe",
|
|
266
|
+
inbox: new URL("https://example.com/actor/3/inbox")
|
|
267
|
+
}));
|
|
268
|
+
assert.deepStrictEqual(await bot.countFollowers(ctx, "non-existent"), null);
|
|
269
|
+
assert.deepStrictEqual(await bot.countFollowers(ctx, "bot"), 3);
|
|
270
|
+
});
|
|
271
|
+
test("BotImpl.getPermissionChecker()", async () => {
|
|
272
|
+
const repository = new MemoryRepository();
|
|
273
|
+
const bot = new BotImpl({
|
|
274
|
+
kv: new MemoryKvStore(),
|
|
275
|
+
repository,
|
|
276
|
+
username: "bot"
|
|
277
|
+
});
|
|
278
|
+
const ctx = bot.federation.createContext(new Request("https://example.com/"), void 0);
|
|
279
|
+
const publicPost = new Note({
|
|
280
|
+
to: PUBLIC_COLLECTION,
|
|
281
|
+
cc: new URL("https://example.com/ap/actor/bot/followers")
|
|
282
|
+
});
|
|
283
|
+
const unlistedPost = new Note({
|
|
284
|
+
to: new URL("https://example.com/ap/actor/bot/followers"),
|
|
285
|
+
cc: PUBLIC_COLLECTION
|
|
286
|
+
});
|
|
287
|
+
const followersPost = new Note({ tos: [new URL("https://example.com/ap/actor/bot/followers"), new URL("https://example.com/ap/actor/mentioned")] });
|
|
288
|
+
const directPost = new Note({ to: new URL("https://example.com/ap/actor/mentioned") });
|
|
289
|
+
const anonymous = await bot.getPermissionChecker(ctx);
|
|
290
|
+
assert.deepStrictEqual(anonymous(publicPost), true);
|
|
291
|
+
assert.deepStrictEqual(anonymous(unlistedPost), true);
|
|
292
|
+
assert.deepStrictEqual(anonymous(followersPost), false);
|
|
293
|
+
assert.deepStrictEqual(anonymous(directPost), false);
|
|
294
|
+
const actor = new Person({ id: new URL("https://example.com/actor/john") });
|
|
295
|
+
const ctx2 = bot.federation.createContext(new Request("https://example.com/"), void 0);
|
|
296
|
+
ctx2.getSignedKeyOwner = () => Promise.resolve(actor);
|
|
297
|
+
const nonFollower = await bot.getPermissionChecker(ctx2);
|
|
298
|
+
assert.deepStrictEqual(nonFollower(publicPost), true);
|
|
299
|
+
assert.deepStrictEqual(nonFollower(unlistedPost), true);
|
|
300
|
+
assert.deepStrictEqual(nonFollower(followersPost), false);
|
|
301
|
+
assert.deepStrictEqual(nonFollower(directPost), false);
|
|
302
|
+
await repository.addFollower(new URL("https://example.com/actor/john#follow"), new Person({
|
|
303
|
+
id: new URL("https://example.com/actor/john"),
|
|
304
|
+
preferredUsername: "john"
|
|
305
|
+
}));
|
|
306
|
+
const ctx3 = bot.federation.createContext(new Request("https://example.com/"), void 0);
|
|
307
|
+
ctx3.getSignedKeyOwner = () => Promise.resolve(actor);
|
|
308
|
+
const follower = await bot.getPermissionChecker(ctx3);
|
|
309
|
+
assert.deepStrictEqual(follower(publicPost), true);
|
|
310
|
+
assert.deepStrictEqual(follower(unlistedPost), true);
|
|
311
|
+
assert.deepStrictEqual(follower(followersPost), true);
|
|
312
|
+
assert.deepStrictEqual(follower(directPost), false);
|
|
313
|
+
const mentionedActor = new Person({ id: new URL("https://example.com/ap/actor/mentioned") });
|
|
314
|
+
const ctx4 = bot.federation.createContext(new Request("https://example.com/"), void 0);
|
|
315
|
+
ctx4.getSignedKeyOwner = () => Promise.resolve(mentionedActor);
|
|
316
|
+
const mentioned = await bot.getPermissionChecker(ctx4);
|
|
317
|
+
assert.deepStrictEqual(mentioned(publicPost), true);
|
|
318
|
+
assert.deepStrictEqual(mentioned(unlistedPost), true);
|
|
319
|
+
assert.deepStrictEqual(mentioned(followersPost), true);
|
|
320
|
+
assert.deepStrictEqual(mentioned(directPost), true);
|
|
321
|
+
});
|
|
322
|
+
test("BotImpl.dispatchOutbox()", async () => {
|
|
323
|
+
const repository = new MemoryRepository();
|
|
324
|
+
const bot = new BotImpl({
|
|
325
|
+
kv: new MemoryKvStore(),
|
|
326
|
+
repository,
|
|
327
|
+
username: "bot",
|
|
328
|
+
collectionWindow: 2
|
|
329
|
+
});
|
|
330
|
+
const ctx = bot.federation.createContext(new Request("https://example.com/"), void 0);
|
|
331
|
+
assert.deepStrictEqual(await bot.dispatchOutbox(ctx, "non-existent", null), null);
|
|
332
|
+
assert.deepStrictEqual(await bot.dispatchOutbox(ctx, "non-existent", ""), null);
|
|
333
|
+
assert.deepStrictEqual(await bot.dispatchOutbox(ctx, "bot", null), {
|
|
334
|
+
items: [],
|
|
335
|
+
nextCursor: null
|
|
336
|
+
});
|
|
337
|
+
assert.deepStrictEqual(await bot.dispatchOutbox(ctx, "bot", ""), {
|
|
338
|
+
items: [],
|
|
339
|
+
nextCursor: null
|
|
340
|
+
});
|
|
341
|
+
await repository.addMessage("78acb1ea-4ac6-46b7-bcd4-3a8965d8126e", new Create({
|
|
342
|
+
id: new URL("https://example.com/ap/actor/bot/create/78acb1ea-4ac6-46b7-bcd4-3a8965d8126e"),
|
|
343
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
344
|
+
to: PUBLIC_COLLECTION,
|
|
345
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
346
|
+
object: new Note({
|
|
347
|
+
id: new URL("https://example.com/ap/actor/bot/note/1"),
|
|
348
|
+
attribution: new URL("https://example.com/ap/actor/bot"),
|
|
349
|
+
to: PUBLIC_COLLECTION,
|
|
350
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
351
|
+
content: "Hello, world!",
|
|
352
|
+
published: Temporal.Instant.from("2025-01-01T00:00:00Z")
|
|
353
|
+
}),
|
|
354
|
+
published: Temporal.Instant.from("2025-01-01T00:00:00Z")
|
|
355
|
+
}));
|
|
356
|
+
await repository.addMessage("46442170-836d-4a0d-9142-f31242abe2f9", new Create({
|
|
357
|
+
id: new URL("https://example.com/ap/actor/bot/create/46442170-836d-4a0d-9142-f31242abe2f9"),
|
|
358
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
359
|
+
to: PUBLIC_COLLECTION,
|
|
360
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
361
|
+
object: new Note({
|
|
362
|
+
id: new URL("https://example.com/ap/actor/bot/note/2"),
|
|
363
|
+
attribution: new URL("https://example.com/ap/actor/bot"),
|
|
364
|
+
to: PUBLIC_COLLECTION,
|
|
365
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
366
|
+
content: "Hello, followers!",
|
|
367
|
+
published: Temporal.Instant.from("2025-01-02T00:00:00Z")
|
|
368
|
+
}),
|
|
369
|
+
published: Temporal.Instant.from("2025-01-02T00:00:00Z")
|
|
370
|
+
}));
|
|
371
|
+
await repository.addMessage("8386a4c7-06f8-409f-ad72-2bba43e83363", new Create({
|
|
372
|
+
id: new URL("https://example.com/ap/actor/bot/create/8386a4c7-06f8-409f-ad72-2bba43e83363"),
|
|
373
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
374
|
+
to: new URL("https://example.com/ap/actor/john"),
|
|
375
|
+
object: new Note({
|
|
376
|
+
id: new URL("https://example.com/ap/actor/bot/note/3"),
|
|
377
|
+
attribution: new URL("https://example.com/ap/actor/bot"),
|
|
378
|
+
to: new URL("https://example.com/ap/actor/john"),
|
|
379
|
+
content: "Hello, followers!",
|
|
380
|
+
published: Temporal.Instant.from("2025-01-03T00:00:00Z")
|
|
381
|
+
}),
|
|
382
|
+
published: Temporal.Instant.from("2025-01-03T00:00:00Z")
|
|
383
|
+
}));
|
|
384
|
+
assert.deepStrictEqual(await bot.dispatchOutbox(ctx, "non-existent", null), null);
|
|
385
|
+
assert.deepStrictEqual(await bot.dispatchOutbox(ctx, "non-existent", ""), null);
|
|
386
|
+
const anonymous1 = await bot.dispatchOutbox(ctx, "bot", "");
|
|
387
|
+
assert.ok(anonymous1 != null);
|
|
388
|
+
assert.deepStrictEqual(anonymous1.nextCursor, "2025-01-01T00:00:00Z");
|
|
389
|
+
assert.deepStrictEqual(anonymous1.items.length, 1);
|
|
390
|
+
assert.ok(anonymous1.items[0] instanceof Create);
|
|
391
|
+
assert.deepStrictEqual(anonymous1.items[0].id, new URL("https://example.com/ap/actor/bot/create/46442170-836d-4a0d-9142-f31242abe2f9"));
|
|
392
|
+
const anonymous2 = await bot.dispatchOutbox(ctx, "bot", "2025-01-01T00:00:00Z");
|
|
393
|
+
assert.ok(anonymous2 != null);
|
|
394
|
+
assert.deepStrictEqual(anonymous2.nextCursor, null);
|
|
395
|
+
assert.deepStrictEqual(anonymous2.items.length, 1);
|
|
396
|
+
assert.ok(anonymous2.items[0] instanceof Create);
|
|
397
|
+
assert.deepStrictEqual(anonymous2.items[0].id, new URL("https://example.com/ap/actor/bot/create/78acb1ea-4ac6-46b7-bcd4-3a8965d8126e"));
|
|
398
|
+
const ctx2 = bot.federation.createContext(new Request("https://example.com/"), void 0);
|
|
399
|
+
const actor = new Person({ id: new URL("https://example.com/ap/actor/john") });
|
|
400
|
+
ctx2.getSignedKeyOwner = () => Promise.resolve(actor);
|
|
401
|
+
const mentioned = await bot.dispatchOutbox(ctx2, "bot", null);
|
|
402
|
+
assert.ok(mentioned != null);
|
|
403
|
+
assert.deepStrictEqual(mentioned.nextCursor, null);
|
|
404
|
+
assert.deepStrictEqual(mentioned.items.length, 3);
|
|
405
|
+
assert.ok(mentioned.items[0] instanceof Create);
|
|
406
|
+
assert.deepStrictEqual(mentioned.items[0].id, new URL("https://example.com/ap/actor/bot/create/8386a4c7-06f8-409f-ad72-2bba43e83363"));
|
|
407
|
+
assert.ok(mentioned.items[1] instanceof Create);
|
|
408
|
+
assert.deepStrictEqual(mentioned.items[1].id, new URL("https://example.com/ap/actor/bot/create/46442170-836d-4a0d-9142-f31242abe2f9"));
|
|
409
|
+
assert.ok(mentioned.items[2] instanceof Create);
|
|
410
|
+
assert.deepStrictEqual(mentioned.items[2].id, new URL("https://example.com/ap/actor/bot/create/78acb1ea-4ac6-46b7-bcd4-3a8965d8126e"));
|
|
411
|
+
});
|
|
412
|
+
test("BotImpl.getOutboxFirstCursor()", () => {
|
|
413
|
+
const bot = new BotImpl({
|
|
414
|
+
kv: new MemoryKvStore(),
|
|
415
|
+
username: "bot"
|
|
416
|
+
});
|
|
417
|
+
const ctx = bot.federation.createContext(new URL("https://example.com/"), void 0);
|
|
418
|
+
assert.deepStrictEqual(bot.getOutboxFirstCursor(ctx, "non-existent"), null);
|
|
419
|
+
assert.deepStrictEqual(bot.getOutboxFirstCursor(ctx, "bot"), "");
|
|
420
|
+
});
|
|
421
|
+
test("BotImpl.countOutbox()", async () => {
|
|
422
|
+
const repository = new MemoryRepository();
|
|
423
|
+
const bot = new BotImpl({
|
|
424
|
+
kv: new MemoryKvStore(),
|
|
425
|
+
repository,
|
|
426
|
+
username: "bot"
|
|
427
|
+
});
|
|
428
|
+
const ctx = bot.federation.createContext(new URL("https://example.com"), void 0);
|
|
429
|
+
assert.deepStrictEqual(await bot.countOutbox(ctx, "non-existent"), null);
|
|
430
|
+
assert.deepStrictEqual(await bot.countOutbox(ctx, "bot"), 0);
|
|
431
|
+
await repository.addMessage("78acb1ea-4ac6-46b7-bcd4-3a8965d8126e", new Create({
|
|
432
|
+
id: new URL("https://example.com/ap/actor/bot/create/78acb1ea-4ac6-46b7-bcd4-3a8965d8126e"),
|
|
433
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
434
|
+
to: PUBLIC_COLLECTION,
|
|
435
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
436
|
+
object: new Note({
|
|
437
|
+
id: new URL("https://example.com/ap/actor/bot/note/1"),
|
|
438
|
+
attribution: new URL("https://example.com/ap/actor/bot"),
|
|
439
|
+
to: PUBLIC_COLLECTION,
|
|
440
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
441
|
+
content: "Hello, world!"
|
|
442
|
+
})
|
|
443
|
+
}));
|
|
444
|
+
await repository.addMessage("46442170-836d-4a0d-9142-f31242abe2f9", new Create({
|
|
445
|
+
id: new URL("https://example.com/ap/actor/bot/create/46442170-836d-4a0d-9142-f31242abe2f9"),
|
|
446
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
447
|
+
to: PUBLIC_COLLECTION,
|
|
448
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
449
|
+
object: new Note({
|
|
450
|
+
id: new URL("https://example.com/ap/actor/bot/note/2"),
|
|
451
|
+
attribution: new URL("https://example.com/ap/actor/bot"),
|
|
452
|
+
to: PUBLIC_COLLECTION,
|
|
453
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
454
|
+
content: "Hello, followers!"
|
|
455
|
+
})
|
|
456
|
+
}));
|
|
457
|
+
await repository.addMessage("8386a4c7-06f8-409f-ad72-2bba43e83363", new Create({
|
|
458
|
+
id: new URL("https://example.com/ap/actor/bot/create/8386a4c7-06f8-409f-ad72-2bba43e83363"),
|
|
459
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
460
|
+
to: new URL("https://example.com/ap/actor/john"),
|
|
461
|
+
object: new Note({
|
|
462
|
+
id: new URL("https://example.com/ap/actor/bot/note/3"),
|
|
463
|
+
attribution: new URL("https://example.com/ap/actor/bot"),
|
|
464
|
+
to: new URL("https://example.com/ap/actor/john"),
|
|
465
|
+
content: "Hello, followers!"
|
|
466
|
+
})
|
|
467
|
+
}));
|
|
468
|
+
assert.deepStrictEqual(await bot.countOutbox(ctx, "non-existent"), null);
|
|
469
|
+
assert.deepStrictEqual(await bot.countOutbox(ctx, "bot"), 3);
|
|
470
|
+
});
|
|
471
|
+
test("BotImpl.dispatchFollow()", async () => {
|
|
472
|
+
const repository = new MemoryRepository();
|
|
473
|
+
const bot = new BotImpl({
|
|
474
|
+
kv: new MemoryKvStore(),
|
|
475
|
+
repository,
|
|
476
|
+
username: "bot"
|
|
477
|
+
});
|
|
478
|
+
const ctx = bot.federation.createContext(new Request("https://example.com/"), void 0);
|
|
479
|
+
assert.deepStrictEqual(await bot.dispatchFollow(ctx, { id: crypto.randomUUID() }), null);
|
|
480
|
+
await repository.addSentFollow("b51f6ca8-53e6-4f7d-ac1f-d039e8c6df5a", new Follow({
|
|
481
|
+
id: new URL("https://example.com/ap/follow/b51f6ca8-53e6-4f7d-ac1f-d039e8c6df5a"),
|
|
482
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
483
|
+
object: new URL("https://example.com/ap/actor/john"),
|
|
484
|
+
to: new URL("https://example.com/ap/actor/john")
|
|
485
|
+
}));
|
|
486
|
+
const follow = await bot.dispatchFollow(ctx, { id: "b51f6ca8-53e6-4f7d-ac1f-d039e8c6df5a" });
|
|
487
|
+
assert.ok(follow instanceof Follow);
|
|
488
|
+
assert.deepStrictEqual(follow.id, new URL("https://example.com/ap/follow/b51f6ca8-53e6-4f7d-ac1f-d039e8c6df5a"));
|
|
489
|
+
assert.deepStrictEqual(follow.actorId, new URL("https://example.com/ap/actor/bot"));
|
|
490
|
+
assert.deepStrictEqual(follow.objectId, new URL("https://example.com/ap/actor/john"));
|
|
491
|
+
assert.deepStrictEqual(follow.toId, new URL("https://example.com/ap/actor/john"));
|
|
492
|
+
});
|
|
493
|
+
test("BotImpl.authorizeFollow()", async () => {
|
|
494
|
+
const repository = new MemoryRepository();
|
|
495
|
+
const bot = new BotImpl({
|
|
496
|
+
kv: new MemoryKvStore(),
|
|
497
|
+
repository,
|
|
498
|
+
username: "bot"
|
|
499
|
+
});
|
|
500
|
+
const ctx = bot.federation.createContext(new Request("https://example.com/"), void 0);
|
|
501
|
+
await repository.addSentFollow("b51f6ca8-53e6-4f7d-ac1f-d039e8c6df5a", new Follow({
|
|
502
|
+
id: new URL("https://example.com/ap/follow/b51f6ca8-53e6-4f7d-ac1f-d039e8c6df5a"),
|
|
503
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
504
|
+
object: new URL("https://example.com/ap/actor/john"),
|
|
505
|
+
to: new URL("https://example.com/ap/actor/john")
|
|
506
|
+
}));
|
|
507
|
+
assert.ok(await bot.authorizeFollow(ctx, { id: "b51f6ca8-53e6-4f7d-ac1f-d039e8c6df5a" }, new CryptographicKey({}), new Person({ id: new URL("https://example.com/ap/actor/john") })));
|
|
508
|
+
assert.ok(await bot.authorizeFollow(ctx, { id: "b51f6ca8-53e6-4f7d-ac1f-d039e8c6df5a" }, new CryptographicKey({}), await new SessionImpl(bot, ctx).getActor()));
|
|
509
|
+
assert.deepStrictEqual(await bot.authorizeFollow(ctx, { id: "b51f6ca8-53e6-4f7d-ac1f-d039e8c6df5a" }, new CryptographicKey({}), new Person({ id: new URL("https://example.com/ap/actor/alice") })), false);
|
|
510
|
+
assert.deepStrictEqual(await bot.authorizeFollow(ctx, { id: crypto.randomUUID() }, new CryptographicKey({}), new Person({ id: new URL("https://example.com/ap/actor/john") })), false);
|
|
511
|
+
});
|
|
512
|
+
test("BotImpl.dispatchCreate()", async () => {
|
|
513
|
+
const repository = new MemoryRepository();
|
|
514
|
+
const bot = new BotImpl({
|
|
515
|
+
kv: new MemoryKvStore(),
|
|
516
|
+
repository,
|
|
517
|
+
username: "bot"
|
|
518
|
+
});
|
|
519
|
+
const ctx = bot.federation.createContext(new Request("https://example.com/"), void 0);
|
|
520
|
+
assert.deepStrictEqual(await bot.dispatchCreate(ctx, { id: "non-existent" }), null);
|
|
521
|
+
await repository.addMessage("78acb1ea-4ac6-46b7-bcd4-3a8965d8126e", new Create({
|
|
522
|
+
id: new URL("https://example.com/ap/actor/bot/create/78acb1ea-4ac6-46b7-bcd4-3a8965d8126e"),
|
|
523
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
524
|
+
to: PUBLIC_COLLECTION,
|
|
525
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
526
|
+
object: new Note({
|
|
527
|
+
id: new URL("https://example.com/ap/actor/bot/note/1"),
|
|
528
|
+
attribution: new URL("https://example.com/ap/actor/bot"),
|
|
529
|
+
to: PUBLIC_COLLECTION,
|
|
530
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
531
|
+
content: "Hello, world!"
|
|
532
|
+
})
|
|
533
|
+
}));
|
|
534
|
+
const create = await bot.dispatchCreate(ctx, { id: "78acb1ea-4ac6-46b7-bcd4-3a8965d8126e" });
|
|
535
|
+
assert.ok(create instanceof Create);
|
|
536
|
+
assert.deepStrictEqual(create.id, new URL("https://example.com/ap/actor/bot/create/78acb1ea-4ac6-46b7-bcd4-3a8965d8126e"));
|
|
537
|
+
const ctx2 = bot.federation.createContext(new Request("https://example.com/"), void 0);
|
|
538
|
+
const actor = new Person({ id: new URL("https://example.com/ap/actor/john") });
|
|
539
|
+
ctx2.getSignedKeyOwner = () => Promise.resolve(actor);
|
|
540
|
+
assert.deepStrictEqual(await bot.dispatchCreate(ctx2, { id: "78acb1ea-4ac6-46b7-bcd4-3a8965d8126e" }), create);
|
|
541
|
+
await repository.addMessage("8386a4c7-06f8-409f-ad72-2bba43e83363", new Create({
|
|
542
|
+
id: new URL("https://example.com/ap/actor/bot/create/8386a4c7-06f8-409f-ad72-2bba43e83363"),
|
|
543
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
544
|
+
to: new URL("https://example.com/ap/actor/john"),
|
|
545
|
+
object: new Note({
|
|
546
|
+
id: new URL("https://example.com/ap/actor/bot/note/3"),
|
|
547
|
+
attribution: new URL("https://example.com/ap/actor/bot"),
|
|
548
|
+
to: new URL("https://example.com/ap/actor/john"),
|
|
549
|
+
content: "Hello, followers!"
|
|
550
|
+
})
|
|
551
|
+
}));
|
|
552
|
+
assert.deepStrictEqual(await bot.dispatchCreate(ctx, { id: "8386a4c7-06f8-409f-ad72-2bba43e83363" }), null);
|
|
553
|
+
const create2 = await bot.dispatchCreate(ctx2, { id: "8386a4c7-06f8-409f-ad72-2bba43e83363" });
|
|
554
|
+
assert.ok(create2 instanceof Create);
|
|
555
|
+
assert.deepStrictEqual(create2.id, new URL("https://example.com/ap/actor/bot/create/8386a4c7-06f8-409f-ad72-2bba43e83363"));
|
|
556
|
+
await repository.addMessage("ce8081ac-f238-484b-9a70-5d8a4b66d829", new Announce({
|
|
557
|
+
id: new URL("https://example.com/ap/actor/bot/announce/ce8081ac-f238-484b-9a70-5d8a4b66d829"),
|
|
558
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
559
|
+
to: PUBLIC_COLLECTION,
|
|
560
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
561
|
+
object: new URL("https://example.com/ap/actor/bot/note/2")
|
|
562
|
+
}));
|
|
563
|
+
assert.deepStrictEqual(await bot.dispatchCreate(ctx, { id: "ce8081ac-f238-484b-9a70-5d8a4b66d829" }), null);
|
|
564
|
+
});
|
|
565
|
+
test("BotImpl.dispatchMessage()", async () => {
|
|
566
|
+
const repository = new MemoryRepository();
|
|
567
|
+
const bot = new BotImpl({
|
|
568
|
+
kv: new MemoryKvStore(),
|
|
569
|
+
repository,
|
|
570
|
+
username: "bot"
|
|
571
|
+
});
|
|
572
|
+
const ctx = bot.federation.createContext(new Request("https://example.com/"), void 0);
|
|
573
|
+
assert.deepStrictEqual(await bot.dispatchMessage(Note, ctx, "non-existent"), null);
|
|
574
|
+
await repository.addMessage("78acb1ea-4ac6-46b7-bcd4-3a8965d8126e", new Create({
|
|
575
|
+
id: new URL("https://example.com/ap/actor/bot/create/78acb1ea-4ac6-46b7-bcd4-3a8965d8126e"),
|
|
576
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
577
|
+
to: PUBLIC_COLLECTION,
|
|
578
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
579
|
+
object: new Note({
|
|
580
|
+
id: new URL("https://example.com/ap/actor/bot/note/1"),
|
|
581
|
+
attribution: new URL("https://example.com/ap/actor/bot"),
|
|
582
|
+
to: PUBLIC_COLLECTION,
|
|
583
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
584
|
+
content: "Hello, world!"
|
|
585
|
+
})
|
|
586
|
+
}));
|
|
587
|
+
const note = await bot.dispatchMessage(Note, ctx, "78acb1ea-4ac6-46b7-bcd4-3a8965d8126e");
|
|
588
|
+
assert.ok(note instanceof Note);
|
|
589
|
+
assert.deepStrictEqual(note.id, new URL("https://example.com/ap/actor/bot/note/1"));
|
|
590
|
+
const ctx2 = bot.federation.createContext(new Request("https://example.com/"), void 0);
|
|
591
|
+
const actor = new Person({ id: new URL("https://example.com/ap/actor/john") });
|
|
592
|
+
ctx2.getSignedKeyOwner = () => Promise.resolve(actor);
|
|
593
|
+
assert.deepStrictEqual(await bot.dispatchMessage(Note, ctx2, "78acb1ea-4ac6-46b7-bcd4-3a8965d8126e"), note);
|
|
594
|
+
assert.deepStrictEqual(await bot.dispatchMessage(Article, ctx, "78acb1ea-4ac6-46b7-bcd4-3a8965d8126e"), null);
|
|
595
|
+
await repository.addMessage("8386a4c7-06f8-409f-ad72-2bba43e83363", new Create({
|
|
596
|
+
id: new URL("https://example.com/ap/actor/bot/create/8386a4c7-06f8-409f-ad72-2bba43e83363"),
|
|
597
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
598
|
+
to: new URL("https://example.com/ap/actor/john"),
|
|
599
|
+
object: new Note({
|
|
600
|
+
id: new URL("https://example.com/ap/actor/bot/note/3"),
|
|
601
|
+
attribution: new URL("https://example.com/ap/actor/bot"),
|
|
602
|
+
to: new URL("https://example.com/ap/actor/john"),
|
|
603
|
+
content: "Hello, followers!"
|
|
604
|
+
})
|
|
605
|
+
}));
|
|
606
|
+
assert.deepStrictEqual(await bot.dispatchMessage(Note, ctx, "8386a4c7-06f8-409f-ad72-2bba43e83363"), null);
|
|
607
|
+
const note2 = await bot.dispatchMessage(Note, ctx2, "8386a4c7-06f8-409f-ad72-2bba43e83363");
|
|
608
|
+
assert.ok(note2 instanceof Note);
|
|
609
|
+
assert.deepStrictEqual(note2.id, new URL("https://example.com/ap/actor/bot/note/3"));
|
|
610
|
+
await repository.addMessage("ce8081ac-f238-484b-9a70-5d8a4b66d829", new Announce({
|
|
611
|
+
id: new URL("https://example.com/ap/actor/bot/announce/ce8081ac-f238-484b-9a70-5d8a4b66d829"),
|
|
612
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
613
|
+
to: PUBLIC_COLLECTION,
|
|
614
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
615
|
+
object: new URL("https://example.com/ap/actor/bot/note/2")
|
|
616
|
+
}));
|
|
617
|
+
assert.deepStrictEqual(await bot.dispatchMessage(Note, ctx, "ce8081ac-f238-484b-9a70-5d8a4b66d829"), null);
|
|
618
|
+
});
|
|
619
|
+
test("BotImpl.dispatchAnnounce()", async () => {
|
|
620
|
+
const repository = new MemoryRepository();
|
|
621
|
+
const bot = new BotImpl({
|
|
622
|
+
kv: new MemoryKvStore(),
|
|
623
|
+
repository,
|
|
624
|
+
username: "bot"
|
|
625
|
+
});
|
|
626
|
+
const ctx = bot.federation.createContext(new Request("https://example.com/"), void 0);
|
|
627
|
+
assert.deepStrictEqual(await bot.dispatchAnnounce(ctx, { id: "non-existent" }), null);
|
|
628
|
+
await repository.addMessage("ce8081ac-f238-484b-9a70-5d8a4b66d829", new Announce({
|
|
629
|
+
id: new URL("https://example.com/ap/actor/bot/announce/ce8081ac-f238-484b-9a70-5d8a4b66d829"),
|
|
630
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
631
|
+
to: PUBLIC_COLLECTION,
|
|
632
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
633
|
+
object: new URL("https://example.com/ap/actor/bot/note/2")
|
|
634
|
+
}));
|
|
635
|
+
const announce = await bot.dispatchAnnounce(ctx, { id: "ce8081ac-f238-484b-9a70-5d8a4b66d829" });
|
|
636
|
+
assert.ok(announce instanceof Announce);
|
|
637
|
+
assert.deepStrictEqual(announce.id, new URL("https://example.com/ap/actor/bot/announce/ce8081ac-f238-484b-9a70-5d8a4b66d829"));
|
|
638
|
+
await repository.addMessage("78acb1ea-4ac6-46b7-bcd4-3a8965d8126e", new Create({
|
|
639
|
+
id: new URL("https://example.com/ap/actor/bot/create/78acb1ea-4ac6-46b7-bcd4-3a8965d8126e"),
|
|
640
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
641
|
+
to: PUBLIC_COLLECTION,
|
|
642
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
643
|
+
object: new Note({
|
|
644
|
+
id: new URL("https://example.com/ap/actor/bot/note/1"),
|
|
645
|
+
attribution: new URL("https://example.com/ap/actor/bot"),
|
|
646
|
+
to: PUBLIC_COLLECTION,
|
|
647
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
648
|
+
content: "Hello, world!"
|
|
649
|
+
})
|
|
650
|
+
}));
|
|
651
|
+
assert.deepStrictEqual(await bot.dispatchAnnounce(ctx, { id: "78acb1ea-4ac6-46b7-bcd4-3a8965d8126e" }), null);
|
|
652
|
+
await repository.addMessage("d4a7ef9b-682c-4de9-b23c-87747d6725cb", new Announce({
|
|
653
|
+
id: new URL("https://example.com/ap/actor/bot/announce/d4a7ef9b-682c-4de9-b23c-87747d6725cb"),
|
|
654
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
655
|
+
to: new URL("https://example.com/ap/actor/john"),
|
|
656
|
+
object: new URL("https://example.com/ap/actor/bot/note/2")
|
|
657
|
+
}));
|
|
658
|
+
assert.deepStrictEqual(await bot.dispatchAnnounce(ctx, { id: "d4a7ef9b-682c-4de9-b23c-87747d6725cb" }), null);
|
|
659
|
+
const ctx2 = bot.federation.createContext(new Request("https://example.com/"), void 0);
|
|
660
|
+
const actor = new Person({ id: new URL("https://example.com/ap/actor/john") });
|
|
661
|
+
ctx2.getSignedKeyOwner = () => Promise.resolve(actor);
|
|
662
|
+
const announce2 = await bot.dispatchAnnounce(ctx2, { id: "d4a7ef9b-682c-4de9-b23c-87747d6725cb" });
|
|
663
|
+
assert.ok(announce2 instanceof Announce);
|
|
664
|
+
assert.deepStrictEqual(announce2.id, new URL("https://example.com/ap/actor/bot/announce/d4a7ef9b-682c-4de9-b23c-87747d6725cb"));
|
|
665
|
+
});
|
|
666
|
+
test("BotImpl.dispatchSharedKey()", () => {
|
|
667
|
+
const identifier = crypto.randomUUID();
|
|
668
|
+
const bot = new BotImpl({
|
|
669
|
+
kv: new MemoryKvStore(),
|
|
670
|
+
username: "bot",
|
|
671
|
+
identifier
|
|
672
|
+
});
|
|
673
|
+
const ctx = bot.federation.createContext(new URL("https://example.com"));
|
|
674
|
+
assert.deepStrictEqual(bot.dispatchSharedKey(ctx), { identifier });
|
|
675
|
+
});
|
|
676
|
+
for (const policy of [
|
|
677
|
+
"accept",
|
|
678
|
+
"reject",
|
|
679
|
+
"manual"
|
|
680
|
+
]) test(`BotImpl.onFollowed() [followerPolicy: ${policy}]`, async (t) => {
|
|
681
|
+
const repository = new MemoryRepository();
|
|
682
|
+
const bot = new BotImpl({
|
|
683
|
+
kv: new MemoryKvStore(),
|
|
684
|
+
repository,
|
|
685
|
+
username: "bot",
|
|
686
|
+
followerPolicy: policy
|
|
687
|
+
});
|
|
688
|
+
const followRequests = [];
|
|
689
|
+
bot.onFollow = (session, fr) => void followRequests.push([session, fr]);
|
|
690
|
+
const ctx = createMockInboxContext(bot, "https://example.com", "bot");
|
|
691
|
+
await t.test("without actor", async () => {
|
|
692
|
+
const followWithoutActor = new Follow({
|
|
693
|
+
id: new URL("https://example.com/ap/actor/john/follows/bot"),
|
|
694
|
+
object: new URL("https://example.com/ap/actor/bot")
|
|
695
|
+
});
|
|
696
|
+
await bot.onFollowed(ctx, followWithoutActor);
|
|
697
|
+
assert.deepStrictEqual(await repository.countFollowers(), 0);
|
|
698
|
+
});
|
|
699
|
+
await t.test("with wrong actor", async () => {
|
|
700
|
+
const followWithWrongActor = new Follow({
|
|
701
|
+
id: new URL("https://example.com/ap/actor/bot/follows/bot"),
|
|
702
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
703
|
+
object: new URL("https://example.com/ap/actor/bot")
|
|
704
|
+
});
|
|
705
|
+
await bot.onFollowed(ctx, followWithWrongActor);
|
|
706
|
+
assert.deepStrictEqual(await repository.countFollowers(), 0);
|
|
707
|
+
});
|
|
708
|
+
const actor = new Person({
|
|
709
|
+
id: new URL("https://example.com/ap/actor/john"),
|
|
710
|
+
preferredUsername: "john"
|
|
711
|
+
});
|
|
712
|
+
await t.test("with wrong recipient", async () => {
|
|
713
|
+
const followWithWrongRecipient = new Follow({
|
|
714
|
+
id: new URL("https://example.com/ap/actor/john/follows/bot"),
|
|
715
|
+
actor,
|
|
716
|
+
object: new URL("https://example.com/ap/actor/non-existent")
|
|
717
|
+
});
|
|
718
|
+
await bot.onFollowed(ctx, followWithWrongRecipient);
|
|
719
|
+
assert.deepStrictEqual(await repository.countFollowers(), 0);
|
|
720
|
+
});
|
|
721
|
+
await t.test("with correct follow", async () => {
|
|
722
|
+
const follow = new Follow({
|
|
723
|
+
id: new URL("https://example.com/ap/actor/john/follows/bot"),
|
|
724
|
+
actor,
|
|
725
|
+
object: new URL("https://example.com/ap/actor/bot")
|
|
726
|
+
});
|
|
727
|
+
await bot.onFollowed(ctx, follow);
|
|
728
|
+
if (policy === "accept") {
|
|
729
|
+
assert.deepStrictEqual(await repository.countFollowers(), 1);
|
|
730
|
+
assert.ok(await repository.hasFollower(new URL("https://example.com/ap/actor/john")));
|
|
731
|
+
const [storedFollower] = await Array.fromAsync(repository.getFollowers());
|
|
732
|
+
assert.ok(storedFollower instanceof Person);
|
|
733
|
+
assert.deepStrictEqual(storedFollower.id, actor.id);
|
|
734
|
+
assert.deepStrictEqual(ctx.sentActivities.length, 1);
|
|
735
|
+
const { activity, recipients } = ctx.sentActivities[0];
|
|
736
|
+
assert.ok(activity instanceof Accept);
|
|
737
|
+
assert.deepStrictEqual(activity.actorId, new URL("https://example.com/ap/actor/bot"));
|
|
738
|
+
assert.deepStrictEqual(activity.objectId, follow.id);
|
|
739
|
+
assert.deepStrictEqual(recipients.length, 1);
|
|
740
|
+
assert.deepStrictEqual(recipients[0], actor);
|
|
741
|
+
assert.deepStrictEqual(ctx.forwardedRecipients, []);
|
|
742
|
+
assert.deepStrictEqual(followRequests.length, 1);
|
|
743
|
+
} else {
|
|
744
|
+
assert.deepStrictEqual(await repository.countFollowers(), 0);
|
|
745
|
+
if (policy === "reject") {
|
|
746
|
+
assert.deepStrictEqual(ctx.sentActivities.length, 1);
|
|
747
|
+
const { activity, recipients } = ctx.sentActivities[0];
|
|
748
|
+
assert.ok(activity instanceof Reject);
|
|
749
|
+
assert.deepStrictEqual(activity.actorId, new URL("https://example.com/ap/actor/bot"));
|
|
750
|
+
assert.deepStrictEqual(activity.objectId, follow.id);
|
|
751
|
+
assert.deepStrictEqual(recipients.length, 1);
|
|
752
|
+
assert.deepStrictEqual(recipients[0], actor);
|
|
753
|
+
assert.deepStrictEqual(ctx.forwardedRecipients, []);
|
|
754
|
+
} else assert.deepStrictEqual(ctx.sentActivities, []);
|
|
755
|
+
}
|
|
756
|
+
const [session, followRequest] = followRequests[0];
|
|
757
|
+
assert.deepStrictEqual(session.bot, bot);
|
|
758
|
+
assert.deepStrictEqual(session.context, ctx);
|
|
759
|
+
assert.ok(followRequest.follower instanceof Person);
|
|
760
|
+
assert.deepStrictEqual(followRequest.follower.id, actor.id);
|
|
761
|
+
});
|
|
762
|
+
});
|
|
763
|
+
test("BotImpl.onUnfollowed()", async (t) => {
|
|
764
|
+
const repository = new MemoryRepository();
|
|
765
|
+
const bot = new BotImpl({
|
|
766
|
+
kv: new MemoryKvStore(),
|
|
767
|
+
repository,
|
|
768
|
+
username: "bot"
|
|
769
|
+
});
|
|
770
|
+
const unfollowed = [];
|
|
771
|
+
bot.onUnfollow = (session, actor) => void unfollowed.push([session, actor]);
|
|
772
|
+
const ctx = createMockInboxContext(bot, "https://example.com", "bot");
|
|
773
|
+
await repository.addFollower(new URL("https://example.com/ap/actor/john/follows/bot"), new Person({
|
|
774
|
+
id: new URL("https://example.com/ap/actor/john"),
|
|
775
|
+
preferredUsername: "john"
|
|
776
|
+
}));
|
|
777
|
+
async function assertNoEffect() {
|
|
778
|
+
assert.deepStrictEqual(await repository.countFollowers(), 1);
|
|
779
|
+
const [follower] = await Array.fromAsync(repository.getFollowers());
|
|
780
|
+
assert.ok(follower instanceof Person);
|
|
781
|
+
assert.deepStrictEqual(follower.id, new URL("https://example.com/ap/actor/john"));
|
|
782
|
+
assert.deepStrictEqual(follower.preferredUsername, "john");
|
|
783
|
+
assert.deepStrictEqual(ctx.sentActivities, []);
|
|
784
|
+
assert.deepStrictEqual(ctx.forwardedRecipients, []);
|
|
785
|
+
assert.deepStrictEqual(unfollowed, []);
|
|
786
|
+
}
|
|
787
|
+
await t.test("without Follow object", async () => {
|
|
788
|
+
const undo = new Undo({ actor: new URL("https://example.com/ap/actor/john") });
|
|
789
|
+
await bot.onUnfollowed(ctx, undo);
|
|
790
|
+
await assertNoEffect();
|
|
791
|
+
});
|
|
792
|
+
await t.test("without Follow.id", async () => {
|
|
793
|
+
const undo = new Undo({
|
|
794
|
+
actor: new URL("https://example.com/ap/actor/john"),
|
|
795
|
+
object: new Follow({})
|
|
796
|
+
});
|
|
797
|
+
await bot.onUnfollowed(ctx, undo);
|
|
798
|
+
await assertNoEffect();
|
|
799
|
+
});
|
|
800
|
+
await t.test("with non-existent Follow.id", async () => {
|
|
801
|
+
const undo = new Undo({
|
|
802
|
+
actor: new URL("https://example.com/ap/actor/john"),
|
|
803
|
+
object: new Follow({ id: new URL("https://example.com/ap/actor/john/follows/non-existent") })
|
|
804
|
+
});
|
|
805
|
+
await bot.onUnfollowed(ctx, undo);
|
|
806
|
+
await assertNoEffect();
|
|
807
|
+
});
|
|
808
|
+
await t.test("with incorrect Follow.actorId", async () => {
|
|
809
|
+
const undo = new Undo({
|
|
810
|
+
actor: new URL("https://example.com/ap/actor/wrong-actor"),
|
|
811
|
+
object: new Follow({ id: new URL("https://example.com/ap/actor/john/follows/bot") })
|
|
812
|
+
});
|
|
813
|
+
await bot.onUnfollowed(ctx, undo);
|
|
814
|
+
await assertNoEffect();
|
|
815
|
+
});
|
|
816
|
+
await t.test("with correct Follow object", async () => {
|
|
817
|
+
const undo = new Undo({
|
|
818
|
+
actor: new Person({
|
|
819
|
+
id: new URL("https://example.com/ap/actor/john"),
|
|
820
|
+
preferredUsername: "john"
|
|
821
|
+
}),
|
|
822
|
+
object: new Follow({ id: new URL("https://example.com/ap/actor/john/follows/bot") })
|
|
823
|
+
});
|
|
824
|
+
await bot.onUnfollowed(ctx, undo);
|
|
825
|
+
assert.deepStrictEqual(await repository.countFollowers(), 0);
|
|
826
|
+
assert.deepStrictEqual(ctx.sentActivities, []);
|
|
827
|
+
assert.deepStrictEqual(ctx.forwardedRecipients, []);
|
|
828
|
+
assert.deepStrictEqual(unfollowed.length, 1);
|
|
829
|
+
const [session, follower] = unfollowed[0];
|
|
830
|
+
assert.deepStrictEqual(session.bot, bot);
|
|
831
|
+
assert.deepStrictEqual(session.context, ctx);
|
|
832
|
+
assert.ok(follower instanceof Person);
|
|
833
|
+
assert.deepStrictEqual(follower.id, new URL("https://example.com/ap/actor/john"));
|
|
834
|
+
});
|
|
835
|
+
});
|
|
836
|
+
test("BotImpl.onFollowAccepted()", async (t) => {
|
|
837
|
+
const repository = new MemoryRepository();
|
|
838
|
+
const bot = new BotImpl({
|
|
839
|
+
kv: new MemoryKvStore(),
|
|
840
|
+
repository,
|
|
841
|
+
username: "bot"
|
|
842
|
+
});
|
|
843
|
+
const accepted = [];
|
|
844
|
+
bot.onAcceptFollow = (session, actor) => void accepted.push([session, actor]);
|
|
845
|
+
const ctx = createMockInboxContext(bot, "https://example.com", "bot");
|
|
846
|
+
await t.test("without object", async () => {
|
|
847
|
+
await bot.onFollowAccepted(ctx, new Accept({ actor: new URL("https://example.com/ap/actor/john") }));
|
|
848
|
+
assert.deepStrictEqual(accepted, []);
|
|
849
|
+
});
|
|
850
|
+
await t.test("with invalid object URI", async () => {
|
|
851
|
+
await bot.onFollowAccepted(ctx, new Accept({
|
|
852
|
+
actor: new URL("https://example.com/ap/actor/john"),
|
|
853
|
+
object: new URL("https://example.com/")
|
|
854
|
+
}));
|
|
855
|
+
assert.deepStrictEqual(accepted, []);
|
|
856
|
+
});
|
|
857
|
+
await t.test("with non-existent object", async () => {
|
|
858
|
+
await bot.onFollowAccepted(ctx, new Accept({
|
|
859
|
+
actor: new URL("https://example.com/ap/actor/john"),
|
|
860
|
+
object: new URL(`https://example.com/ap/follow/${crypto.randomUUID()}`)
|
|
861
|
+
}));
|
|
862
|
+
assert.deepStrictEqual(accepted, []);
|
|
863
|
+
});
|
|
864
|
+
await t.test("with non-actor", async () => {
|
|
865
|
+
await repository.addSentFollow("2ca58e2a-a34a-43e6-81af-c4f21ffed0c5", new Follow({
|
|
866
|
+
id: new URL("https://example.com/ap/follow/2ca58e2a-a34a-43e6-81af-c4f21ffed0c5"),
|
|
867
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
868
|
+
object: new Note({})
|
|
869
|
+
}));
|
|
870
|
+
await bot.onFollowAccepted(ctx, new Accept({
|
|
871
|
+
actor: new URL("https://example.com/ap/actor/john"),
|
|
872
|
+
object: new URL("https://example.com/ap/follow/2ca58e2a-a34a-43e6-81af-c4f21ffed0c5")
|
|
873
|
+
}));
|
|
874
|
+
assert.deepStrictEqual(accepted, []);
|
|
875
|
+
});
|
|
876
|
+
await t.test("with actor without URI", async () => {
|
|
877
|
+
await repository.addSentFollow("a99ff3bf-72a2-412b-83b9-cba894d38805", new Follow({
|
|
878
|
+
id: new URL("https://example.com/ap/follow/a99ff3bf-72a2-412b-83b9-cba894d38805"),
|
|
879
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
880
|
+
object: new Person({ preferredUsername: "john" })
|
|
881
|
+
}));
|
|
882
|
+
await bot.onFollowAccepted(ctx, new Accept({
|
|
883
|
+
actor: new URL("https://example.com/ap/actor/john"),
|
|
884
|
+
object: new URL("https://example.com/ap/follow/a99ff3bf-72a2-412b-83b9-cba894d38805")
|
|
885
|
+
}));
|
|
886
|
+
assert.deepStrictEqual(accepted, []);
|
|
887
|
+
});
|
|
888
|
+
await t.test("with actor", async () => {
|
|
889
|
+
await repository.addSentFollow("3bca0b8e-503a-47ea-ad69-6b7c29369fbd", new Follow({
|
|
890
|
+
id: new URL("https://example.com/ap/follow/3bca0b8e-503a-47ea-ad69-6b7c29369fbd"),
|
|
891
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
892
|
+
object: new Person({
|
|
893
|
+
id: new URL("https://example.com/ap/actor/john"),
|
|
894
|
+
preferredUsername: "john"
|
|
895
|
+
})
|
|
896
|
+
}));
|
|
897
|
+
await bot.onFollowAccepted(ctx, new Accept({
|
|
898
|
+
actor: new URL("https://example.com/ap/actor/john"),
|
|
899
|
+
object: new URL("https://example.com/ap/follow/3bca0b8e-503a-47ea-ad69-6b7c29369fbd")
|
|
900
|
+
}));
|
|
901
|
+
assert.deepStrictEqual(accepted.length, 1);
|
|
902
|
+
const [session, actor] = accepted[0];
|
|
903
|
+
assert.deepStrictEqual(session.bot, bot);
|
|
904
|
+
assert.deepStrictEqual(session.context, ctx);
|
|
905
|
+
assert.ok(actor instanceof Person);
|
|
906
|
+
assert.deepStrictEqual(actor.id, new URL("https://example.com/ap/actor/john"));
|
|
907
|
+
const follow = await repository.getFollowee(new URL("https://example.com/ap/actor/john"));
|
|
908
|
+
assert.ok(follow != null);
|
|
909
|
+
assert.ok(follow instanceof Follow);
|
|
910
|
+
assert.deepStrictEqual(follow.id, new URL("https://example.com/ap/follow/3bca0b8e-503a-47ea-ad69-6b7c29369fbd"));
|
|
911
|
+
assert.deepStrictEqual(follow.objectId, actor.id);
|
|
912
|
+
});
|
|
913
|
+
});
|
|
914
|
+
test("BotImpl.onFollowRejected()", async (t) => {
|
|
915
|
+
const repository = new MemoryRepository();
|
|
916
|
+
const bot = new BotImpl({
|
|
917
|
+
kv: new MemoryKvStore(),
|
|
918
|
+
repository,
|
|
919
|
+
username: "bot"
|
|
920
|
+
});
|
|
921
|
+
const rejected = [];
|
|
922
|
+
bot.onRejectFollow = (session, actor) => void rejected.push([session, actor]);
|
|
923
|
+
const ctx = createMockInboxContext(bot, "https://example.com", "bot");
|
|
924
|
+
await t.test("without object", async () => {
|
|
925
|
+
await bot.onFollowRejected(ctx, new Reject({ actor: new URL("https://example.com/ap/actor/john") }));
|
|
926
|
+
assert.deepStrictEqual(rejected, []);
|
|
927
|
+
});
|
|
928
|
+
await t.test("with invalid object URI", async () => {
|
|
929
|
+
await bot.onFollowRejected(ctx, new Reject({
|
|
930
|
+
actor: new URL("https://example.com/ap/actor/john"),
|
|
931
|
+
object: new URL("https://example.com/")
|
|
932
|
+
}));
|
|
933
|
+
assert.deepStrictEqual(rejected, []);
|
|
934
|
+
});
|
|
935
|
+
await t.test("with non-existent object", async () => {
|
|
936
|
+
await bot.onFollowRejected(ctx, new Reject({
|
|
937
|
+
actor: new URL("https://example.com/ap/actor/john"),
|
|
938
|
+
object: new URL(`https://example.com/ap/follow/${crypto.randomUUID()}`)
|
|
939
|
+
}));
|
|
940
|
+
assert.deepStrictEqual(rejected, []);
|
|
941
|
+
});
|
|
942
|
+
await t.test("with non-actor", async () => {
|
|
943
|
+
await repository.addSentFollow("2ca58e2a-a34a-43e6-81af-c4f21ffed0c5", new Follow({
|
|
944
|
+
id: new URL("https://example.com/ap/follow/2ca58e2a-a34a-43e6-81af-c4f21ffed0c5"),
|
|
945
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
946
|
+
object: new Note({})
|
|
947
|
+
}));
|
|
948
|
+
await bot.onFollowRejected(ctx, new Reject({
|
|
949
|
+
actor: new URL("https://example.com/ap/actor/john"),
|
|
950
|
+
object: new URL("https://example.com/ap/follow/2ca58e2a-a34a-43e6-81af-c4f21ffed0c5")
|
|
951
|
+
}));
|
|
952
|
+
assert.deepStrictEqual(rejected, []);
|
|
953
|
+
});
|
|
954
|
+
await t.test("with actor without URI", async () => {
|
|
955
|
+
await repository.addSentFollow("a99ff3bf-72a2-412b-83b9-cba894d38805", new Follow({
|
|
956
|
+
id: new URL("https://example.com/ap/follow/a99ff3bf-72a2-412b-83b9-cba894d38805"),
|
|
957
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
958
|
+
object: new Person({ preferredUsername: "john" })
|
|
959
|
+
}));
|
|
960
|
+
await bot.onFollowRejected(ctx, new Reject({
|
|
961
|
+
actor: new URL("https://example.com/ap/actor/john"),
|
|
962
|
+
object: new URL("https://example.com/ap/follow/a99ff3bf-72a2-412b-83b9-cba894d38805")
|
|
963
|
+
}));
|
|
964
|
+
assert.deepStrictEqual(rejected, []);
|
|
965
|
+
});
|
|
966
|
+
await t.test("with actor", async () => {
|
|
967
|
+
await repository.addSentFollow("3bca0b8e-503a-47ea-ad69-6b7c29369fbd", new Follow({
|
|
968
|
+
id: new URL("https://example.com/ap/follow/3bca0b8e-503a-47ea-ad69-6b7c29369fbd"),
|
|
969
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
970
|
+
object: new Person({
|
|
971
|
+
id: new URL("https://example.com/ap/actor/john"),
|
|
972
|
+
preferredUsername: "john"
|
|
973
|
+
})
|
|
974
|
+
}));
|
|
975
|
+
await bot.onFollowRejected(ctx, new Reject({
|
|
976
|
+
actor: new URL("https://example.com/ap/actor/john"),
|
|
977
|
+
object: new URL("https://example.com/ap/follow/3bca0b8e-503a-47ea-ad69-6b7c29369fbd")
|
|
978
|
+
}));
|
|
979
|
+
assert.deepStrictEqual(rejected.length, 1);
|
|
980
|
+
const [session, actor] = rejected[0];
|
|
981
|
+
assert.deepStrictEqual(session.bot, bot);
|
|
982
|
+
assert.deepStrictEqual(session.context, ctx);
|
|
983
|
+
assert.ok(actor instanceof Person);
|
|
984
|
+
assert.deepStrictEqual(actor.id, new URL("https://example.com/ap/actor/john"));
|
|
985
|
+
assert.deepStrictEqual(await repository.getSentFollow("3bca0b8e-503a-47ea-ad69-6b7c29369fbd"), void 0);
|
|
986
|
+
});
|
|
987
|
+
});
|
|
988
|
+
test("BotImpl.onCreated()", async (t) => {
|
|
989
|
+
const repository = new MemoryRepository();
|
|
990
|
+
const bot = new BotImpl({
|
|
991
|
+
kv: new MemoryKvStore(),
|
|
992
|
+
repository,
|
|
993
|
+
username: "bot"
|
|
994
|
+
});
|
|
995
|
+
let replied = [];
|
|
996
|
+
bot.onReply = (session, msg) => void replied.push([session, msg]);
|
|
997
|
+
let mentioned = [];
|
|
998
|
+
bot.onMention = (session, msg) => void mentioned.push([session, msg]);
|
|
999
|
+
const ctx = createMockInboxContext(bot, "https://example.com", "bot");
|
|
1000
|
+
let messaged = [];
|
|
1001
|
+
bot.onMessage = (session, msg) => void messaged.push([session, msg]);
|
|
1002
|
+
await t.test("without object", async () => {
|
|
1003
|
+
const createWithoutObject = new Create({ actor: new URL("https://example.com/ap/actor/john") });
|
|
1004
|
+
await bot.onCreated(ctx, createWithoutObject);
|
|
1005
|
+
assert.deepStrictEqual(replied, []);
|
|
1006
|
+
assert.deepStrictEqual(mentioned, []);
|
|
1007
|
+
assert.deepStrictEqual(messaged, []);
|
|
1008
|
+
assert.deepStrictEqual(ctx.sentActivities, []);
|
|
1009
|
+
assert.deepStrictEqual(ctx.forwardedRecipients, []);
|
|
1010
|
+
});
|
|
1011
|
+
await t.test("with non-message object", async () => {
|
|
1012
|
+
const createWithNonMessageObject = new Create({
|
|
1013
|
+
actor: new URL("https://example.com/ap/actor/john"),
|
|
1014
|
+
object: new Place({})
|
|
1015
|
+
});
|
|
1016
|
+
await bot.onCreated(ctx, createWithNonMessageObject);
|
|
1017
|
+
assert.deepStrictEqual(replied, []);
|
|
1018
|
+
assert.deepStrictEqual(mentioned, []);
|
|
1019
|
+
assert.deepStrictEqual(messaged, []);
|
|
1020
|
+
assert.deepStrictEqual(ctx.sentActivities, []);
|
|
1021
|
+
assert.deepStrictEqual(ctx.forwardedRecipients, []);
|
|
1022
|
+
});
|
|
1023
|
+
await repository.addMessage("a6358f1b-c978-49d3-8065-37a1df6168de", new Create({
|
|
1024
|
+
id: new URL("https://example.com/ap/create/a6358f1b-c978-49d3-8065-37a1df6168de"),
|
|
1025
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
1026
|
+
to: PUBLIC_COLLECTION,
|
|
1027
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
1028
|
+
object: new Note({
|
|
1029
|
+
id: new URL("https://example.com/ap/note/a6358f1b-c978-49d3-8065-37a1df6168de"),
|
|
1030
|
+
attribution: new URL("https://example.com/ap/actor/bot"),
|
|
1031
|
+
to: PUBLIC_COLLECTION,
|
|
1032
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
1033
|
+
content: "Hello, world!"
|
|
1034
|
+
})
|
|
1035
|
+
}));
|
|
1036
|
+
await t.test("on reply", async () => {
|
|
1037
|
+
const create = new Create({
|
|
1038
|
+
id: new URL("https://example.com/ap/create/9cfd7129-4cf0-4505-90d8-3cac2dc42434"),
|
|
1039
|
+
actor: new URL("https://example.com/ap/actor/john"),
|
|
1040
|
+
to: PUBLIC_COLLECTION,
|
|
1041
|
+
cc: new URL("https://example.com/ap/actor/john/followers"),
|
|
1042
|
+
object: new Note({
|
|
1043
|
+
id: new URL("https://example.com/ap/note/9cfd7129-4cf0-4505-90d8-3cac2dc42434"),
|
|
1044
|
+
attribution: new Person({
|
|
1045
|
+
id: new URL("https://example.com/ap/actor/john"),
|
|
1046
|
+
preferredUsername: "john"
|
|
1047
|
+
}),
|
|
1048
|
+
to: PUBLIC_COLLECTION,
|
|
1049
|
+
cc: new URL("https://example.com/ap/actor/john/followers"),
|
|
1050
|
+
content: "It's reply!",
|
|
1051
|
+
replyTarget: new URL("https://example.com/ap/note/a6358f1b-c978-49d3-8065-37a1df6168de")
|
|
1052
|
+
})
|
|
1053
|
+
});
|
|
1054
|
+
await bot.onCreated(ctx, create);
|
|
1055
|
+
assert.deepStrictEqual(replied.length, 1);
|
|
1056
|
+
const [session, msg] = replied[0];
|
|
1057
|
+
assert.deepStrictEqual(session.bot, bot);
|
|
1058
|
+
assert.deepStrictEqual(session.context, ctx);
|
|
1059
|
+
assert.ok(msg.raw instanceof Note);
|
|
1060
|
+
assert.deepStrictEqual(msg.raw.id, create.objectId);
|
|
1061
|
+
assert.ok(msg.replyTarget != null);
|
|
1062
|
+
assert.deepStrictEqual(msg.replyTarget.id, new URL("https://example.com/ap/note/a6358f1b-c978-49d3-8065-37a1df6168de"));
|
|
1063
|
+
assert.deepStrictEqual(mentioned, []);
|
|
1064
|
+
assert.deepStrictEqual(messaged, replied);
|
|
1065
|
+
assert.deepStrictEqual(ctx.sentActivities, []);
|
|
1066
|
+
assert.deepStrictEqual(ctx.forwardedRecipients, ["followers"]);
|
|
1067
|
+
});
|
|
1068
|
+
await t.test("on mention", async () => {
|
|
1069
|
+
replied = [];
|
|
1070
|
+
messaged = [];
|
|
1071
|
+
ctx.forwardedRecipients = [];
|
|
1072
|
+
const create = new Create({
|
|
1073
|
+
id: new URL("https://example.com/ap/create/9cfd7129-4cf0-4505-90d8-3cac2dc42434"),
|
|
1074
|
+
actor: new URL("https://example.com/ap/actor/john"),
|
|
1075
|
+
to: PUBLIC_COLLECTION,
|
|
1076
|
+
cc: new URL("https://example.com/ap/actor/john/followers"),
|
|
1077
|
+
object: new Note({
|
|
1078
|
+
id: new URL("https://example.com/ap/note/9cfd7129-4cf0-4505-90d8-3cac2dc42434"),
|
|
1079
|
+
attribution: new Person({
|
|
1080
|
+
id: new URL("https://example.com/ap/actor/john"),
|
|
1081
|
+
preferredUsername: "john"
|
|
1082
|
+
}),
|
|
1083
|
+
to: PUBLIC_COLLECTION,
|
|
1084
|
+
cc: new URL("https://example.com/ap/actor/john/followers"),
|
|
1085
|
+
content: "<p><a href=\"https://example.com/ap/actor/bot\">@bot</a> Hey!</p>",
|
|
1086
|
+
tags: [new Mention({
|
|
1087
|
+
href: new URL("https://example.com/ap/actor/bot"),
|
|
1088
|
+
name: "@bot"
|
|
1089
|
+
})]
|
|
1090
|
+
})
|
|
1091
|
+
});
|
|
1092
|
+
await bot.onCreated(ctx, create);
|
|
1093
|
+
assert.deepStrictEqual(replied, []);
|
|
1094
|
+
assert.deepStrictEqual(mentioned.length, 1);
|
|
1095
|
+
const [session, msg] = mentioned[0];
|
|
1096
|
+
assert.deepStrictEqual(session.bot, bot);
|
|
1097
|
+
assert.deepStrictEqual(session.context, ctx);
|
|
1098
|
+
assert.ok(msg.raw instanceof Note);
|
|
1099
|
+
assert.deepStrictEqual(msg.raw.id, create.objectId);
|
|
1100
|
+
assert.deepStrictEqual(msg.mentions.length, 1);
|
|
1101
|
+
assert.deepStrictEqual(msg.mentions[0].id, new URL("https://example.com/ap/actor/bot"));
|
|
1102
|
+
assert.deepStrictEqual(messaged, mentioned);
|
|
1103
|
+
assert.deepStrictEqual(ctx.sentActivities, []);
|
|
1104
|
+
assert.deepStrictEqual(ctx.forwardedRecipients, []);
|
|
1105
|
+
});
|
|
1106
|
+
await t.test("on quote", async () => {
|
|
1107
|
+
mentioned = [];
|
|
1108
|
+
messaged = [];
|
|
1109
|
+
ctx.forwardedRecipients = [];
|
|
1110
|
+
const create = new Create({
|
|
1111
|
+
id: new URL("https://example.com/ap/create/9cfd7129-4cf0-4505-90d8-3cac2dc42434"),
|
|
1112
|
+
actor: new URL("https://example.com/ap/actor/john"),
|
|
1113
|
+
to: PUBLIC_COLLECTION,
|
|
1114
|
+
cc: new URL("https://example.com/ap/actor/john/followers"),
|
|
1115
|
+
object: new Note({
|
|
1116
|
+
id: new URL("https://example.com/ap/note/9cfd7129-4cf0-4505-90d8-3cac2dc42434"),
|
|
1117
|
+
attribution: new Person({
|
|
1118
|
+
id: new URL("https://example.com/ap/actor/john"),
|
|
1119
|
+
preferredUsername: "john"
|
|
1120
|
+
}),
|
|
1121
|
+
to: PUBLIC_COLLECTION,
|
|
1122
|
+
cc: new URL("https://example.com/ap/actor/john/followers"),
|
|
1123
|
+
content: "It's a quote!",
|
|
1124
|
+
quoteUrl: new URL("https://example.com/ap/note/a6358f1b-c978-49d3-8065-37a1df6168de")
|
|
1125
|
+
})
|
|
1126
|
+
});
|
|
1127
|
+
let quoted = [];
|
|
1128
|
+
bot.onQuote = (session$1, msg$1) => void quoted.push([session$1, msg$1]);
|
|
1129
|
+
await bot.onCreated(ctx, create);
|
|
1130
|
+
assert.deepStrictEqual(quoted.length, 1);
|
|
1131
|
+
const [session, msg] = quoted[0];
|
|
1132
|
+
assert.deepStrictEqual(session.bot, bot);
|
|
1133
|
+
assert.deepStrictEqual(session.context, ctx);
|
|
1134
|
+
assert.ok(msg.raw instanceof Note);
|
|
1135
|
+
assert.deepStrictEqual(msg.raw.id, create.objectId);
|
|
1136
|
+
assert.ok(msg.quoteTarget != null);
|
|
1137
|
+
assert.deepStrictEqual(msg.quoteTarget.id, new URL("https://example.com/ap/note/a6358f1b-c978-49d3-8065-37a1df6168de"));
|
|
1138
|
+
assert.deepStrictEqual(replied, []);
|
|
1139
|
+
assert.deepStrictEqual(mentioned, []);
|
|
1140
|
+
assert.deepStrictEqual(messaged, quoted);
|
|
1141
|
+
assert.deepStrictEqual(ctx.sentActivities, []);
|
|
1142
|
+
assert.deepStrictEqual(ctx.forwardedRecipients, ["followers"]);
|
|
1143
|
+
quoted = [];
|
|
1144
|
+
messaged = [];
|
|
1145
|
+
ctx.forwardedRecipients = [];
|
|
1146
|
+
});
|
|
1147
|
+
await t.test("on message", async () => {
|
|
1148
|
+
const create = new Create({
|
|
1149
|
+
id: new URL("https://example.com/ap/create/9cfd7129-4cf0-4505-90d8-3cac2dc42434"),
|
|
1150
|
+
actor: new URL("https://example.com/ap/actor/john"),
|
|
1151
|
+
to: PUBLIC_COLLECTION,
|
|
1152
|
+
cc: new URL("https://example.com/ap/actor/john/followers"),
|
|
1153
|
+
object: new Note({
|
|
1154
|
+
id: new URL("https://example.com/ap/note/9cfd7129-4cf0-4505-90d8-3cac2dc42434"),
|
|
1155
|
+
attribution: new Person({
|
|
1156
|
+
id: new URL("https://example.com/ap/actor/john"),
|
|
1157
|
+
preferredUsername: "john"
|
|
1158
|
+
}),
|
|
1159
|
+
to: PUBLIC_COLLECTION,
|
|
1160
|
+
cc: new URL("https://example.com/ap/actor/john/followers"),
|
|
1161
|
+
content: "<p>Hello!</p>"
|
|
1162
|
+
})
|
|
1163
|
+
});
|
|
1164
|
+
await bot.onCreated(ctx, create);
|
|
1165
|
+
assert.deepStrictEqual(replied, []);
|
|
1166
|
+
assert.deepStrictEqual(mentioned, []);
|
|
1167
|
+
assert.deepStrictEqual(messaged.length, 1);
|
|
1168
|
+
const [session, msg] = messaged[0];
|
|
1169
|
+
assert.deepStrictEqual(session.bot, bot);
|
|
1170
|
+
assert.deepStrictEqual(session.context, ctx);
|
|
1171
|
+
assert.ok(msg.raw instanceof Note);
|
|
1172
|
+
assert.deepStrictEqual(msg.raw.id, create.objectId);
|
|
1173
|
+
assert.deepStrictEqual(ctx.sentActivities, []);
|
|
1174
|
+
assert.deepStrictEqual(ctx.forwardedRecipients, []);
|
|
1175
|
+
});
|
|
1176
|
+
messaged = [];
|
|
1177
|
+
});
|
|
1178
|
+
test("BotImpl.onAnnounced()", async () => {
|
|
1179
|
+
const bot = new BotImpl({
|
|
1180
|
+
kv: new MemoryKvStore(),
|
|
1181
|
+
username: "bot"
|
|
1182
|
+
});
|
|
1183
|
+
const shares = [];
|
|
1184
|
+
bot.onSharedMessage = (session$1, sharedMessage$1) => void shares.push([session$1, sharedMessage$1]);
|
|
1185
|
+
const ctx = createMockInboxContext(bot, "https://example.com", "bot");
|
|
1186
|
+
const announce = new Announce({
|
|
1187
|
+
id: new URL("https://example.com/ap/actor/bot/announce/1"),
|
|
1188
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
1189
|
+
to: PUBLIC_COLLECTION,
|
|
1190
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
1191
|
+
object: new Note({
|
|
1192
|
+
id: new URL("https://example.com/ap/actor/bot/note/1"),
|
|
1193
|
+
attribution: new URL("https://example.com/ap/actor/bot"),
|
|
1194
|
+
to: PUBLIC_COLLECTION,
|
|
1195
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
1196
|
+
content: "Hello, world!"
|
|
1197
|
+
})
|
|
1198
|
+
});
|
|
1199
|
+
await bot.onAnnounced(ctx, announce);
|
|
1200
|
+
assert.deepStrictEqual(shares.length, 1);
|
|
1201
|
+
const [session, sharedMessage] = shares[0];
|
|
1202
|
+
assert.deepStrictEqual(session.bot, bot);
|
|
1203
|
+
assert.deepStrictEqual(session.context, ctx);
|
|
1204
|
+
assert.deepStrictEqual(sharedMessage.raw, announce);
|
|
1205
|
+
assert.deepStrictEqual(sharedMessage.id, announce.id);
|
|
1206
|
+
assert.deepStrictEqual(sharedMessage.actor.id, announce.actorId);
|
|
1207
|
+
assert.deepStrictEqual(sharedMessage.visibility, "public");
|
|
1208
|
+
assert.deepStrictEqual(sharedMessage.original.id, announce.objectId);
|
|
1209
|
+
assert.deepStrictEqual(ctx.sentActivities, []);
|
|
1210
|
+
assert.deepStrictEqual(ctx.forwardedRecipients, []);
|
|
1211
|
+
});
|
|
1212
|
+
test("BotImpl.onLiked()", async () => {
|
|
1213
|
+
const bot = new BotImpl({
|
|
1214
|
+
kv: new MemoryKvStore(),
|
|
1215
|
+
username: "bot"
|
|
1216
|
+
});
|
|
1217
|
+
const likes = [];
|
|
1218
|
+
bot.onLike = (session$1, like$1) => void likes.push([session$1, like$1]);
|
|
1219
|
+
const ctx = createMockInboxContext(bot, "https://example.com", "bot");
|
|
1220
|
+
const rawLike = new Like({
|
|
1221
|
+
id: new URL("https://example.com/ap/actor/bot/like/1"),
|
|
1222
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
1223
|
+
object: new Note({
|
|
1224
|
+
id: new URL("https://example.com/ap/actor/bot/note/1"),
|
|
1225
|
+
attribution: new URL("https://example.com/ap/actor/bot"),
|
|
1226
|
+
to: PUBLIC_COLLECTION,
|
|
1227
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
1228
|
+
content: "Hello, world!"
|
|
1229
|
+
})
|
|
1230
|
+
});
|
|
1231
|
+
await bot.onLiked(ctx, rawLike);
|
|
1232
|
+
assert.deepStrictEqual(likes.length, 1);
|
|
1233
|
+
const [session, like] = likes[0];
|
|
1234
|
+
assert.deepStrictEqual(session.bot, bot);
|
|
1235
|
+
assert.deepStrictEqual(session.context, ctx);
|
|
1236
|
+
assert.deepStrictEqual(like.raw, rawLike);
|
|
1237
|
+
assert.deepStrictEqual(like.id, rawLike.id);
|
|
1238
|
+
assert.deepStrictEqual(like.actor.id, rawLike.actorId);
|
|
1239
|
+
assert.deepStrictEqual(like.message.id, rawLike.objectId);
|
|
1240
|
+
assert.deepStrictEqual(ctx.sentActivities, []);
|
|
1241
|
+
assert.deepStrictEqual(ctx.forwardedRecipients, []);
|
|
1242
|
+
});
|
|
1243
|
+
test("BotImpl.onUnliked()", async () => {
|
|
1244
|
+
const bot = new BotImpl({
|
|
1245
|
+
kv: new MemoryKvStore(),
|
|
1246
|
+
username: "bot"
|
|
1247
|
+
});
|
|
1248
|
+
const likes = [];
|
|
1249
|
+
bot.onUnlike = (session$1, like$1) => void likes.push([session$1, like$1]);
|
|
1250
|
+
const ctx = createMockInboxContext(bot, "https://example.com", "bot");
|
|
1251
|
+
const rawLike = new Like({
|
|
1252
|
+
id: new URL("https://example.com/ap/actor/bot/like/1"),
|
|
1253
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
1254
|
+
object: new Note({
|
|
1255
|
+
id: new URL("https://example.com/ap/actor/bot/note/1"),
|
|
1256
|
+
attribution: new URL("https://example.com/ap/actor/bot"),
|
|
1257
|
+
to: PUBLIC_COLLECTION,
|
|
1258
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
1259
|
+
content: "Hello, world!"
|
|
1260
|
+
})
|
|
1261
|
+
});
|
|
1262
|
+
const undo = new Undo({
|
|
1263
|
+
id: new URL("https://example.com/ap/actor/bot/unlike/1"),
|
|
1264
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
1265
|
+
object: rawLike
|
|
1266
|
+
});
|
|
1267
|
+
await bot.onUnliked(ctx, undo);
|
|
1268
|
+
assert.deepStrictEqual(likes.length, 1);
|
|
1269
|
+
const [session, like] = likes[0];
|
|
1270
|
+
assert.deepStrictEqual(session.bot, bot);
|
|
1271
|
+
assert.deepStrictEqual(session.context, ctx);
|
|
1272
|
+
assert.deepStrictEqual(like.raw, rawLike);
|
|
1273
|
+
assert.deepStrictEqual(like.id, rawLike.id);
|
|
1274
|
+
assert.deepStrictEqual(like.actor.id, rawLike.actorId);
|
|
1275
|
+
assert.deepStrictEqual(like.message.id, rawLike.objectId);
|
|
1276
|
+
assert.deepStrictEqual(ctx.sentActivities, []);
|
|
1277
|
+
assert.deepStrictEqual(ctx.forwardedRecipients, []);
|
|
1278
|
+
likes.pop();
|
|
1279
|
+
const invalidUndo = undo.clone({ actor: new URL("https://example.com/ap/actor/another") });
|
|
1280
|
+
await bot.onUnliked(ctx, invalidUndo);
|
|
1281
|
+
assert.deepStrictEqual(likes, []);
|
|
1282
|
+
});
|
|
1283
|
+
test("BotImpl.onReacted()", async () => {
|
|
1284
|
+
const bot = new BotImpl({
|
|
1285
|
+
kv: new MemoryKvStore(),
|
|
1286
|
+
username: "bot"
|
|
1287
|
+
});
|
|
1288
|
+
const reactions = [];
|
|
1289
|
+
bot.onReact = (session$1, reaction$1) => void reactions.push([session$1, reaction$1]);
|
|
1290
|
+
const ctx = createMockInboxContext(bot, "https://example.com", "bot");
|
|
1291
|
+
const rawLike = new Like({
|
|
1292
|
+
id: new URL("https://example.com/ap/actor/bot/like/1"),
|
|
1293
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
1294
|
+
name: ":heart:",
|
|
1295
|
+
object: new Note({
|
|
1296
|
+
id: new URL("https://example.com/ap/actor/bot/note/1"),
|
|
1297
|
+
attribution: new URL("https://example.com/ap/actor/bot"),
|
|
1298
|
+
to: PUBLIC_COLLECTION,
|
|
1299
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
1300
|
+
content: "Hello, world!"
|
|
1301
|
+
}),
|
|
1302
|
+
tags: [new Emoji({
|
|
1303
|
+
id: new URL("https://example.com/ap/emoji/heart"),
|
|
1304
|
+
name: ":heart:",
|
|
1305
|
+
icon: new Image({
|
|
1306
|
+
mediaType: "image/png",
|
|
1307
|
+
url: new URL("https://example.com/emoji/heart.png")
|
|
1308
|
+
})
|
|
1309
|
+
})]
|
|
1310
|
+
});
|
|
1311
|
+
await bot.onReacted(ctx, rawLike);
|
|
1312
|
+
assert.deepStrictEqual(reactions.length, 1);
|
|
1313
|
+
const [session, reaction] = reactions[0];
|
|
1314
|
+
assert.deepStrictEqual(session.bot, bot);
|
|
1315
|
+
assert.deepStrictEqual(session.context, ctx);
|
|
1316
|
+
assert.deepStrictEqual(reaction.raw, rawLike);
|
|
1317
|
+
assert.deepStrictEqual(reaction.id, rawLike.id);
|
|
1318
|
+
assert.deepStrictEqual(reaction.actor.id, rawLike.actorId);
|
|
1319
|
+
assert.deepStrictEqual(reaction.message.id, rawLike.objectId);
|
|
1320
|
+
assert.ok(reaction.emoji instanceof Emoji);
|
|
1321
|
+
assert.deepStrictEqual(reaction.emoji.name, ":heart:");
|
|
1322
|
+
assert.deepStrictEqual(ctx.sentActivities, []);
|
|
1323
|
+
assert.deepStrictEqual(ctx.forwardedRecipients, []);
|
|
1324
|
+
reactions.pop();
|
|
1325
|
+
const emojiReact = new EmojiReact({
|
|
1326
|
+
id: new URL("https://example.com/ap/actor/bot/react/1"),
|
|
1327
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
1328
|
+
name: ":thumbsup:",
|
|
1329
|
+
object: new Note({
|
|
1330
|
+
id: new URL("https://example.com/ap/actor/bot/note/1"),
|
|
1331
|
+
attribution: new URL("https://example.com/ap/actor/bot"),
|
|
1332
|
+
to: PUBLIC_COLLECTION,
|
|
1333
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
1334
|
+
content: "Hello, world!"
|
|
1335
|
+
}),
|
|
1336
|
+
tags: [new Emoji({
|
|
1337
|
+
id: new URL("https://example.com/ap/emoji/thumbsup"),
|
|
1338
|
+
name: ":thumbsup:",
|
|
1339
|
+
icon: new Image({
|
|
1340
|
+
mediaType: "image/png",
|
|
1341
|
+
url: new URL("https://example.com/emoji/thumbsup.png")
|
|
1342
|
+
})
|
|
1343
|
+
})]
|
|
1344
|
+
});
|
|
1345
|
+
await bot.onReacted(ctx, emojiReact);
|
|
1346
|
+
assert.deepStrictEqual(reactions.length, 1);
|
|
1347
|
+
const [session2, reaction2] = reactions[0];
|
|
1348
|
+
assert.deepStrictEqual(session2.bot, bot);
|
|
1349
|
+
assert.deepStrictEqual(session2.context, ctx);
|
|
1350
|
+
assert.deepStrictEqual(reaction2.raw, emojiReact);
|
|
1351
|
+
assert.deepStrictEqual(reaction2.id, emojiReact.id);
|
|
1352
|
+
assert.deepStrictEqual(reaction2.actor.id, emojiReact.actorId);
|
|
1353
|
+
assert.deepStrictEqual(reaction2.message.id, emojiReact.objectId);
|
|
1354
|
+
assert.ok(reaction2.emoji instanceof Emoji);
|
|
1355
|
+
assert.deepStrictEqual(reaction2.emoji.name, ":thumbsup:");
|
|
1356
|
+
assert.deepStrictEqual(ctx.sentActivities, []);
|
|
1357
|
+
assert.deepStrictEqual(ctx.forwardedRecipients, []);
|
|
1358
|
+
});
|
|
1359
|
+
test("BotImpl.onUnreacted()", async () => {
|
|
1360
|
+
const bot = new BotImpl({
|
|
1361
|
+
kv: new MemoryKvStore(),
|
|
1362
|
+
username: "bot"
|
|
1363
|
+
});
|
|
1364
|
+
const reactions = [];
|
|
1365
|
+
bot.onUnreact = (session$1, reaction$1) => void reactions.push([session$1, reaction$1]);
|
|
1366
|
+
const ctx = createMockInboxContext(bot, "https://example.com", "bot");
|
|
1367
|
+
const rawLike = new Like({
|
|
1368
|
+
id: new URL("https://example.com/ap/actor/bot/like/1"),
|
|
1369
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
1370
|
+
name: ":heart:",
|
|
1371
|
+
object: new Note({
|
|
1372
|
+
id: new URL("https://example.com/ap/actor/bot/note/1"),
|
|
1373
|
+
attribution: new URL("https://example.com/ap/actor/bot"),
|
|
1374
|
+
to: PUBLIC_COLLECTION,
|
|
1375
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
1376
|
+
content: "Hello, world!"
|
|
1377
|
+
}),
|
|
1378
|
+
tags: [new Emoji({
|
|
1379
|
+
id: new URL("https://example.com/ap/emoji/heart"),
|
|
1380
|
+
name: ":heart:",
|
|
1381
|
+
icon: new Image({
|
|
1382
|
+
mediaType: "image/png",
|
|
1383
|
+
url: new URL("https://example.com/emoji/heart.png")
|
|
1384
|
+
})
|
|
1385
|
+
})]
|
|
1386
|
+
});
|
|
1387
|
+
const undo = new Undo({
|
|
1388
|
+
id: new URL("https://example.com/ap/actor/bot/unreact/1"),
|
|
1389
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
1390
|
+
object: rawLike
|
|
1391
|
+
});
|
|
1392
|
+
await bot.onUnreacted(ctx, undo);
|
|
1393
|
+
assert.deepStrictEqual(reactions.length, 1);
|
|
1394
|
+
const [session, reaction] = reactions[0];
|
|
1395
|
+
assert.deepStrictEqual(session.bot, bot);
|
|
1396
|
+
assert.deepStrictEqual(session.context, ctx);
|
|
1397
|
+
assert.deepStrictEqual(reaction.raw, rawLike);
|
|
1398
|
+
assert.deepStrictEqual(reaction.id, rawLike.id);
|
|
1399
|
+
assert.deepStrictEqual(reaction.actor.id, rawLike.actorId);
|
|
1400
|
+
assert.deepStrictEqual(reaction.message.id, rawLike.objectId);
|
|
1401
|
+
assert.ok(reaction.emoji instanceof Emoji);
|
|
1402
|
+
assert.deepStrictEqual(reaction.emoji.name, ":heart:");
|
|
1403
|
+
assert.deepStrictEqual(ctx.sentActivities, []);
|
|
1404
|
+
assert.deepStrictEqual(ctx.forwardedRecipients, []);
|
|
1405
|
+
reactions.pop();
|
|
1406
|
+
const emojiReact = new EmojiReact({
|
|
1407
|
+
id: new URL("https://example.com/ap/actor/bot/react/1"),
|
|
1408
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
1409
|
+
name: ":thumbsup:",
|
|
1410
|
+
object: new Note({
|
|
1411
|
+
id: new URL("https://example.com/ap/actor/bot/note/1"),
|
|
1412
|
+
attribution: new URL("https://example.com/ap/actor/bot"),
|
|
1413
|
+
to: PUBLIC_COLLECTION,
|
|
1414
|
+
cc: new URL("https://example.com/ap/actor/bot/followers"),
|
|
1415
|
+
content: "Hello, world!"
|
|
1416
|
+
}),
|
|
1417
|
+
tags: [new Emoji({
|
|
1418
|
+
id: new URL("https://example.com/ap/emoji/thumbsup"),
|
|
1419
|
+
name: ":thumbsup:",
|
|
1420
|
+
icon: new Image({
|
|
1421
|
+
mediaType: "image/png",
|
|
1422
|
+
url: new URL("https://example.com/emoji/thumbsup.png")
|
|
1423
|
+
})
|
|
1424
|
+
})]
|
|
1425
|
+
});
|
|
1426
|
+
const undoEmojiReact = new Undo({
|
|
1427
|
+
id: new URL("https://example.com/ap/actor/bot/unreact/2"),
|
|
1428
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
1429
|
+
object: emojiReact
|
|
1430
|
+
});
|
|
1431
|
+
await bot.onUnreacted(ctx, undoEmojiReact);
|
|
1432
|
+
assert.deepStrictEqual(reactions.length, 1);
|
|
1433
|
+
const [session2, reaction2] = reactions[0];
|
|
1434
|
+
assert.deepStrictEqual(session2.bot, bot);
|
|
1435
|
+
assert.deepStrictEqual(session2.context, ctx);
|
|
1436
|
+
assert.deepStrictEqual(reaction2.raw, emojiReact);
|
|
1437
|
+
assert.deepStrictEqual(reaction2.id, emojiReact.id);
|
|
1438
|
+
assert.deepStrictEqual(reaction2.actor.id, emojiReact.actorId);
|
|
1439
|
+
assert.deepStrictEqual(reaction2.message.id, emojiReact.objectId);
|
|
1440
|
+
assert.ok(reaction2.emoji instanceof Emoji);
|
|
1441
|
+
assert.deepStrictEqual(reaction2.emoji.name, ":thumbsup:");
|
|
1442
|
+
assert.deepStrictEqual(ctx.sentActivities, []);
|
|
1443
|
+
assert.deepStrictEqual(ctx.forwardedRecipients, []);
|
|
1444
|
+
reactions.pop();
|
|
1445
|
+
const invalidUndo = undoEmojiReact.clone({ actor: new URL("https://example.com/ap/actor/another") });
|
|
1446
|
+
await bot.onUnreacted(ctx, invalidUndo);
|
|
1447
|
+
assert.deepStrictEqual(reactions, []);
|
|
1448
|
+
reactions.pop();
|
|
1449
|
+
const nonReactionUndo = new Undo({
|
|
1450
|
+
id: new URL("https://example.com/ap/actor/bot/unreact/3"),
|
|
1451
|
+
actor: new URL("https://example.com/ap/actor/bot"),
|
|
1452
|
+
object: new Note({})
|
|
1453
|
+
});
|
|
1454
|
+
await bot.onUnreacted(ctx, nonReactionUndo);
|
|
1455
|
+
assert.deepStrictEqual(reactions, []);
|
|
1456
|
+
});
|
|
1457
|
+
test("BotImpl.dispatchNodeInfo()", () => {
|
|
1458
|
+
const bot = new BotImpl({
|
|
1459
|
+
kv: new MemoryKvStore(),
|
|
1460
|
+
username: "bot",
|
|
1461
|
+
software: {
|
|
1462
|
+
name: "test",
|
|
1463
|
+
version: parseSemVer("1.2.3"),
|
|
1464
|
+
homepage: new URL("https://example.com/"),
|
|
1465
|
+
repository: new URL("https://git.example.com/")
|
|
1466
|
+
}
|
|
1467
|
+
});
|
|
1468
|
+
const ctx = bot.federation.createContext(new URL("https://example.com"), void 0);
|
|
1469
|
+
assert.deepStrictEqual(bot.dispatchNodeInfo(ctx), {
|
|
1470
|
+
software: {
|
|
1471
|
+
name: "test",
|
|
1472
|
+
version: parseSemVer("1.2.3"),
|
|
1473
|
+
homepage: new URL("https://example.com/"),
|
|
1474
|
+
repository: new URL("https://git.example.com/")
|
|
1475
|
+
},
|
|
1476
|
+
protocols: ["activitypub"],
|
|
1477
|
+
services: { outbound: ["atom1.0"] },
|
|
1478
|
+
usage: {
|
|
1479
|
+
users: {
|
|
1480
|
+
total: 1,
|
|
1481
|
+
activeMonth: 1,
|
|
1482
|
+
activeHalfyear: 1
|
|
1483
|
+
},
|
|
1484
|
+
localPosts: 0,
|
|
1485
|
+
localComments: 0
|
|
1486
|
+
}
|
|
1487
|
+
});
|
|
1488
|
+
});
|
|
1489
|
+
test("BotImpl.fetch()", async () => {
|
|
1490
|
+
const bot = new BotImpl({
|
|
1491
|
+
kv: new MemoryKvStore(),
|
|
1492
|
+
username: "bot"
|
|
1493
|
+
});
|
|
1494
|
+
const request = new Request("http://localhost/.well-known/webfinger?resource=acct:bot@example.com", { headers: {
|
|
1495
|
+
"X-Forwarded-Host": "example.com",
|
|
1496
|
+
"X-Forwarded-Proto": "https"
|
|
1497
|
+
} });
|
|
1498
|
+
const response = await bot.fetch(request);
|
|
1499
|
+
assert.deepStrictEqual(response.status, 404);
|
|
1500
|
+
const botBehindProxy = new BotImpl({
|
|
1501
|
+
kv: new MemoryKvStore(),
|
|
1502
|
+
username: "bot",
|
|
1503
|
+
behindProxy: true
|
|
1504
|
+
});
|
|
1505
|
+
const response2 = await botBehindProxy.fetch(request);
|
|
1506
|
+
assert.deepStrictEqual(response2.status, 200);
|
|
1507
|
+
});
|
|
1508
|
+
describe("BotImpl.addCustomEmoji(), BotImpl.addCustomEmojis()", () => {
|
|
1509
|
+
const bot = new BotImpl({
|
|
1510
|
+
kv: new MemoryKvStore(),
|
|
1511
|
+
username: "bot"
|
|
1512
|
+
});
|
|
1513
|
+
test("addCustomEmoji()", () => {
|
|
1514
|
+
const emojiData = {
|
|
1515
|
+
type: "image/png",
|
|
1516
|
+
url: "https://example.com/emoji.png"
|
|
1517
|
+
};
|
|
1518
|
+
const deferredEmoji = bot.addCustomEmoji("testEmoji", emojiData);
|
|
1519
|
+
assert.deepStrictEqual(typeof deferredEmoji, "function");
|
|
1520
|
+
assert.deepStrictEqual(bot.customEmojis["testEmoji"], emojiData);
|
|
1521
|
+
assert.throws(() => bot.addCustomEmoji("invalid name", emojiData), TypeError, "Invalid custom emoji name");
|
|
1522
|
+
assert.throws(() => bot.addCustomEmoji("testEmoji", emojiData), TypeError, "Duplicate custom emoji name");
|
|
1523
|
+
assert.throws(() => bot.addCustomEmoji("invalidType", {
|
|
1524
|
+
type: "text/plain",
|
|
1525
|
+
url: "https://example.com/emoji.txt"
|
|
1526
|
+
}), TypeError, "Unsupported media type");
|
|
1527
|
+
});
|
|
1528
|
+
test("addCustomEmojis()", () => {
|
|
1529
|
+
const emojisData = {
|
|
1530
|
+
emoji1: {
|
|
1531
|
+
type: "image/png",
|
|
1532
|
+
url: "https://example.com/emoji1.png"
|
|
1533
|
+
},
|
|
1534
|
+
emoji2: {
|
|
1535
|
+
type: "image/gif",
|
|
1536
|
+
file: "/path/to/emoji2.gif"
|
|
1537
|
+
}
|
|
1538
|
+
};
|
|
1539
|
+
const deferredEmojis = bot.addCustomEmojis(emojisData);
|
|
1540
|
+
assert.deepStrictEqual(typeof deferredEmojis["emoji1"], "function");
|
|
1541
|
+
assert.deepStrictEqual(typeof deferredEmojis["emoji2"], "function");
|
|
1542
|
+
assert.deepStrictEqual(bot.customEmojis["emoji1"], emojisData.emoji1);
|
|
1543
|
+
assert.deepStrictEqual(bot.customEmojis["emoji2"], emojisData.emoji2);
|
|
1544
|
+
assert.throws(() => bot.addCustomEmojis({ emoji1: {
|
|
1545
|
+
type: "image/png",
|
|
1546
|
+
url: "https://example.com/dup1.png"
|
|
1547
|
+
} }), TypeError, "Duplicate custom emoji name: emoji1");
|
|
1548
|
+
});
|
|
1549
|
+
});
|
|
1550
|
+
test("BotImpl.getEmoji()", async () => {
|
|
1551
|
+
const bot = new BotImpl({
|
|
1552
|
+
kv: new MemoryKvStore(),
|
|
1553
|
+
username: "bot"
|
|
1554
|
+
});
|
|
1555
|
+
const ctx = bot.federation.createContext(new URL("https://example.com"), void 0);
|
|
1556
|
+
const remoteEmojiData = {
|
|
1557
|
+
type: "image/png",
|
|
1558
|
+
url: "https://remote.com/emoji.png"
|
|
1559
|
+
};
|
|
1560
|
+
bot.customEmojis["remoteEmoji"] = remoteEmojiData;
|
|
1561
|
+
const remoteEmoji = bot.getEmoji(ctx, "remoteEmoji", remoteEmojiData);
|
|
1562
|
+
assert.ok(remoteEmoji instanceof Emoji);
|
|
1563
|
+
assert.deepStrictEqual(remoteEmoji.id, new URL("https://example.com/ap/emoji/remoteEmoji"));
|
|
1564
|
+
assert.deepStrictEqual(remoteEmoji.name, ":remoteEmoji:");
|
|
1565
|
+
const icon = await remoteEmoji.getIcon();
|
|
1566
|
+
assert.ok(icon instanceof Image);
|
|
1567
|
+
assert.deepStrictEqual(icon.mediaType, "image/png");
|
|
1568
|
+
assert.deepStrictEqual(icon.url?.href, "https://remote.com/emoji.png");
|
|
1569
|
+
const localEmojiData = {
|
|
1570
|
+
type: "image/gif",
|
|
1571
|
+
file: "/path/to/local/emoji.gif"
|
|
1572
|
+
};
|
|
1573
|
+
bot.customEmojis["localEmoji"] = localEmojiData;
|
|
1574
|
+
const localEmoji = bot.getEmoji(ctx, "localEmoji", localEmojiData);
|
|
1575
|
+
assert.ok(localEmoji instanceof Emoji);
|
|
1576
|
+
assert.deepStrictEqual(localEmoji.id, new URL("https://example.com/ap/emoji/localEmoji"));
|
|
1577
|
+
assert.deepStrictEqual(localEmoji.name, ":localEmoji:");
|
|
1578
|
+
const icon2 = await localEmoji.getIcon();
|
|
1579
|
+
assert.ok(icon2 instanceof Image);
|
|
1580
|
+
assert.deepStrictEqual(icon2.mediaType, "image/gif");
|
|
1581
|
+
assert.deepStrictEqual(icon2.url?.href, "https://example.com/emojis/localEmoji.gif");
|
|
1582
|
+
const localEmojiDataNoExt = {
|
|
1583
|
+
type: "image/webp",
|
|
1584
|
+
file: "/path/to/local/emoji"
|
|
1585
|
+
};
|
|
1586
|
+
bot.customEmojis["localEmojiNoExt"] = localEmojiDataNoExt;
|
|
1587
|
+
const localEmojiNoExt = bot.getEmoji(ctx, "localEmojiNoExt", localEmojiDataNoExt);
|
|
1588
|
+
const icon3 = await localEmojiNoExt.getIcon();
|
|
1589
|
+
assert.ok(icon3 instanceof Image);
|
|
1590
|
+
assert.deepStrictEqual(icon3.mediaType, "image/webp");
|
|
1591
|
+
assert.deepStrictEqual(icon3.url?.href, "https://example.com/emojis/localEmojiNoExt.webp");
|
|
1592
|
+
});
|
|
1593
|
+
test("BotImpl.dispatchEmoji()", () => {
|
|
1594
|
+
const bot = new BotImpl({
|
|
1595
|
+
kv: new MemoryKvStore(),
|
|
1596
|
+
username: "bot"
|
|
1597
|
+
});
|
|
1598
|
+
const ctx = bot.federation.createContext(new URL("https://example.com"), void 0);
|
|
1599
|
+
const emojiData = {
|
|
1600
|
+
type: "image/png",
|
|
1601
|
+
url: "https://example.com/emoji.png"
|
|
1602
|
+
};
|
|
1603
|
+
bot.customEmojis["testEmoji"] = emojiData;
|
|
1604
|
+
const emoji = bot.dispatchEmoji(ctx, { name: "testEmoji" });
|
|
1605
|
+
assert.ok(emoji instanceof Emoji);
|
|
1606
|
+
assert.deepStrictEqual(emoji.id, new URL("https://example.com/ap/emoji/testEmoji"));
|
|
1607
|
+
assert.deepStrictEqual(emoji.name, ":testEmoji:");
|
|
1608
|
+
const nonExistent = bot.dispatchEmoji(ctx, { name: "nonExistent" });
|
|
1609
|
+
assert.deepStrictEqual(nonExistent, null);
|
|
1610
|
+
});
|
|
1611
|
+
test("BotImpl.getFollowersFirstCursor()", () => {
|
|
1612
|
+
const bot = new BotImpl({
|
|
1613
|
+
kv: new MemoryKvStore(),
|
|
1614
|
+
username: "bot"
|
|
1615
|
+
});
|
|
1616
|
+
const ctx = bot.federation.createContext(new URL("https://example.com"), void 0);
|
|
1617
|
+
assert.deepStrictEqual(bot.getFollowersFirstCursor(ctx, "non-existent"), null);
|
|
1618
|
+
assert.deepStrictEqual(bot.getFollowersFirstCursor(ctx, "bot"), "0");
|
|
1619
|
+
});
|
|
1620
|
+
function createMockInboxContext(bot, origin, recipient) {
|
|
1621
|
+
const ctx = bot.federation.createContext(new URL(origin), void 0);
|
|
1622
|
+
ctx.recipient = recipient ?? null;
|
|
1623
|
+
ctx.sentActivities = [];
|
|
1624
|
+
ctx.sendActivity = (_, recipients, activity) => {
|
|
1625
|
+
ctx.sentActivities.push({
|
|
1626
|
+
recipients: recipients === "followers" ? "followers" : Array.isArray(recipients) ? recipients : [recipients],
|
|
1627
|
+
activity
|
|
1628
|
+
});
|
|
1629
|
+
return Promise.resolve();
|
|
1630
|
+
};
|
|
1631
|
+
ctx.forwardedRecipients = [];
|
|
1632
|
+
ctx.forwardActivity = (_, recipients) => {
|
|
1633
|
+
if (recipients === "followers") ctx.forwardedRecipients.push("followers");
|
|
1634
|
+
else if (Array.isArray(recipients)) ctx.forwardedRecipients.push(...recipients);
|
|
1635
|
+
else ctx.forwardedRecipients.push(recipients);
|
|
1636
|
+
return Promise.resolve();
|
|
1637
|
+
};
|
|
1638
|
+
return ctx;
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
//#endregion
|
|
1642
|
+
//# sourceMappingURL=bot-impl.test.js.map
|