@fedify/botkit 0.5.0-dev.210 → 0.5.0-dev.225
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/dist/bot-group.test.d.ts +2 -0
- package/dist/bot-group.test.js +220 -0
- package/dist/bot-group.test.js.map +1 -0
- package/dist/bot-impl.d.ts +132 -13
- package/dist/bot-impl.d.ts.map +1 -1
- package/dist/bot-impl.js +400 -178
- package/dist/bot-impl.js.map +1 -1
- package/dist/bot-impl.test.js +214 -76
- package/dist/bot-impl.test.js.map +1 -1
- package/dist/bot.d.ts +94 -48
- package/dist/bot.d.ts.map +1 -1
- package/dist/bot.js +2 -104
- package/dist/bot.js.map +1 -1
- package/dist/bot.test.js +59 -0
- package/dist/bot.test.js.map +1 -1
- package/dist/components/FollowButton.d.ts +5 -3
- package/dist/components/FollowButton.d.ts.map +1 -1
- package/dist/components/FollowButton.js +2 -2
- package/dist/components/FollowButton.js.map +1 -1
- package/dist/components/Follower.d.ts +2 -2
- package/dist/components/Layout.js +1 -1
- package/dist/components/Layout.js.map +1 -1
- package/dist/components/Message.d.ts +2 -2
- package/dist/deno.js +2 -1
- package/dist/deno.js.map +1 -1
- package/dist/follow-impl.test.js +3 -3
- package/dist/follow-impl.test.js.map +1 -1
- package/dist/instance-impl.d.ts +158 -0
- package/dist/instance-impl.d.ts.map +1 -0
- package/dist/instance-impl.js +603 -0
- package/dist/instance-impl.js.map +1 -0
- package/dist/instance-impl.test.d.ts +2 -0
- package/dist/instance-impl.test.js +103 -0
- package/dist/instance-impl.test.js.map +1 -0
- package/dist/instance-multi.test.d.ts +2 -0
- package/dist/instance-multi.test.js +151 -0
- package/dist/instance-multi.test.js.map +1 -0
- package/dist/instance-routing.test.d.ts +2 -0
- package/dist/instance-routing.test.js +367 -0
- package/dist/instance-routing.test.js.map +1 -0
- package/dist/instance.d.ts +318 -0
- package/dist/instance.d.ts.map +1 -0
- package/dist/instance.js +51 -0
- package/dist/instance.js.map +1 -0
- package/dist/message-impl.d.ts.map +1 -1
- package/dist/message-impl.js +17 -10
- package/dist/message-impl.js.map +1 -1
- package/dist/message-impl.test.js +43 -9
- package/dist/message-impl.test.js.map +1 -1
- package/dist/mod.d.ts +5 -3
- package/dist/mod.js +4 -2
- package/dist/pages.d.ts +12 -3
- package/dist/pages.d.ts.map +1 -1
- package/dist/pages.js +112 -41
- package/dist/pages.js.map +1 -1
- package/dist/pages.test.d.ts +2 -0
- package/dist/pages.test.js +170 -0
- package/dist/pages.test.js.map +1 -0
- package/dist/repository.d.ts +385 -138
- package/dist/repository.d.ts.map +1 -1
- package/dist/repository.js +595 -223
- package/dist/repository.js.map +1 -1
- package/dist/repository.test.js +564 -136
- package/dist/repository.test.js.map +1 -1
- package/dist/session-impl.d.ts.map +1 -1
- package/dist/session-impl.js +13 -4
- package/dist/session-impl.js.map +1 -1
- package/dist/session-impl.test.d.ts.map +1 -1
- package/dist/session-impl.test.js +9 -9
- package/dist/session-impl.test.js.map +1 -1
- package/dist/session.d.ts +8 -3
- package/dist/session.d.ts.map +1 -1
- package/dist/text.d.ts.map +1 -1
- package/dist/text.js +27 -10
- package/dist/text.js.map +1 -1
- package/dist/text.test.js +37 -2
- package/dist/text.test.js.map +1 -1
- package/dist/uri.d.ts +46 -0
- package/dist/uri.d.ts.map +1 -0
- package/dist/uri.js +64 -0
- package/dist/uri.js.map +1 -0
- package/dist/uri.test.d.ts +2 -0
- package/dist/uri.test.js +93 -0
- package/dist/uri.test.js.map +1 -0
- package/package.json +5 -1
package/dist/bot-impl.js
CHANGED
|
@@ -2,21 +2,37 @@
|
|
|
2
2
|
import { Temporal, toTemporalInstant } from "@js-temporal/polyfill";
|
|
3
3
|
Date.prototype.toTemporalInstant = toTemporalInstant;
|
|
4
4
|
|
|
5
|
-
import deno_default from "./deno.js";
|
|
6
5
|
import { isEmoji } from "./emoji.js";
|
|
7
6
|
import { FollowRequestImpl } from "./follow-impl.js";
|
|
7
|
+
import { parseLocalUri } from "./uri.js";
|
|
8
8
|
import { createMessage, getMessageVisibility, isMessageObject, isQuoteLink, messageClasses } from "./message-impl.js";
|
|
9
|
-
import {
|
|
10
|
-
import { KvRepository } from "./repository.js";
|
|
9
|
+
import { ActorScopedRepository, KvRepository } from "./repository.js";
|
|
11
10
|
import { SessionImpl } from "./session-impl.js";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import mimeDb from "mime-db";
|
|
16
|
-
import fs from "node:fs/promises";
|
|
17
|
-
import { getXForwardedRequest } from "x-forwarded-fetch";
|
|
11
|
+
import { InstanceImpl } from "./instance-impl.js";
|
|
12
|
+
import { Announce, Article, ChatMessage, Create, Emoji, EmojiReact, Endpoints, Follow, Image, Like, Link, Mention, Note, Object as Object$1, PUBLIC_COLLECTION, PropertyValue, Question, Service, Update, isActor } from "@fedify/vocab";
|
|
13
|
+
import { generateCryptoKeyPair } from "@fedify/fedify";
|
|
18
14
|
|
|
19
15
|
//#region src/bot-impl.ts
|
|
16
|
+
/**
|
|
17
|
+
* The names of the event handler properties of {@link BotEventHandlers}.
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
20
|
+
const botEventHandlerNames = [
|
|
21
|
+
"onFollow",
|
|
22
|
+
"onUnfollow",
|
|
23
|
+
"onAcceptFollow",
|
|
24
|
+
"onRejectFollow",
|
|
25
|
+
"onMention",
|
|
26
|
+
"onReply",
|
|
27
|
+
"onQuote",
|
|
28
|
+
"onMessage",
|
|
29
|
+
"onSharedMessage",
|
|
30
|
+
"onLike",
|
|
31
|
+
"onUnlike",
|
|
32
|
+
"onReact",
|
|
33
|
+
"onUnreact",
|
|
34
|
+
"onVote"
|
|
35
|
+
];
|
|
20
36
|
var BotImpl = class {
|
|
21
37
|
identifier;
|
|
22
38
|
class;
|
|
@@ -29,13 +45,40 @@ var BotImpl = class {
|
|
|
29
45
|
properties;
|
|
30
46
|
#properties;
|
|
31
47
|
followerPolicy;
|
|
32
|
-
customEmojis;
|
|
33
48
|
repository;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
49
|
+
/**
|
|
50
|
+
* The instance hosting the bot. It owns the shared infrastructure:
|
|
51
|
+
* the Fedify federation, the key–value store, the message queue,
|
|
52
|
+
* the root repository, and HTTP handling.
|
|
53
|
+
*/
|
|
54
|
+
instance;
|
|
55
|
+
get customEmojis() {
|
|
56
|
+
return this.instance.customEmojis;
|
|
57
|
+
}
|
|
58
|
+
get software() {
|
|
59
|
+
return this.instance.software;
|
|
60
|
+
}
|
|
61
|
+
get behindProxy() {
|
|
62
|
+
return this.instance.behindProxy;
|
|
63
|
+
}
|
|
64
|
+
get pages() {
|
|
65
|
+
return this.instance.pages;
|
|
66
|
+
}
|
|
67
|
+
get collectionWindow() {
|
|
68
|
+
return this.instance.collectionWindow;
|
|
69
|
+
}
|
|
70
|
+
get federation() {
|
|
71
|
+
return this.instance.federation;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The identifier of the bot actor that owns local objects whose URIs are
|
|
75
|
+
* in the legacy (pre-0.5) format, which did not carry the identifier.
|
|
76
|
+
* Legacy URIs can only occur in deployments that hosted a single bot
|
|
77
|
+
* before the upgrade, so they are attributed to that bot.
|
|
78
|
+
*/
|
|
79
|
+
get legacyObjectUrisIdentifier() {
|
|
80
|
+
return this.instance.legacyObjectUrisIdentifier;
|
|
81
|
+
}
|
|
39
82
|
onFollow;
|
|
40
83
|
onUnfollow;
|
|
41
84
|
onAcceptFollow;
|
|
@@ -62,52 +105,19 @@ var BotImpl = class {
|
|
|
62
105
|
this.properties = options.properties ?? {};
|
|
63
106
|
this.#properties = null;
|
|
64
107
|
this.followerPolicy = options.followerPolicy ?? "accept";
|
|
65
|
-
this.
|
|
66
|
-
this.repository = options.repository ?? new KvRepository(options.kv);
|
|
67
|
-
this.software = options.software;
|
|
68
|
-
this.pages = {
|
|
69
|
-
color: "green",
|
|
70
|
-
css: "",
|
|
71
|
-
...options.pages ?? {}
|
|
72
|
-
};
|
|
73
|
-
this.federation = createFederation({
|
|
108
|
+
this.instance = options.instance ?? new InstanceImpl({
|
|
74
109
|
kv: options.kv,
|
|
110
|
+
repository: new MigrationGatedRepository(options.repository ?? new KvRepository(options.kv), this.identifier),
|
|
75
111
|
queue: options.queue,
|
|
76
|
-
|
|
112
|
+
software: options.software,
|
|
113
|
+
behindProxy: options.behindProxy,
|
|
114
|
+
pages: options.pages,
|
|
115
|
+
collectionWindow: options.collectionWindow,
|
|
116
|
+
legacyObjectUris: { identifier: this.identifier },
|
|
117
|
+
compatMode: true
|
|
77
118
|
});
|
|
78
|
-
this.
|
|
79
|
-
|
|
80
|
-
this.initialize();
|
|
81
|
-
}
|
|
82
|
-
initialize() {
|
|
83
|
-
this.federation.setActorDispatcher("/ap/actor/{identifier}", this.dispatchActor.bind(this)).mapHandle(this.mapHandle.bind(this)).setKeyPairsDispatcher(this.dispatchActorKeyPairs.bind(this));
|
|
84
|
-
this.federation.setFollowersDispatcher("/ap/actor/{identifier}/followers", this.dispatchFollowers.bind(this)).setFirstCursor(this.getFollowersFirstCursor.bind(this)).setCounter(this.countFollowers.bind(this));
|
|
85
|
-
this.federation.setOutboxDispatcher("/ap/actor/{identifier}/outbox", this.dispatchOutbox.bind(this)).setFirstCursor(this.getOutboxFirstCursor.bind(this)).setCounter(this.countOutbox.bind(this));
|
|
86
|
-
this.federation.setObjectDispatcher(Follow, "/ap/follow/{id}", this.dispatchFollow.bind(this)).authorize(this.authorizeFollow.bind(this));
|
|
87
|
-
this.federation.setObjectDispatcher(Create, "/ap/create/{id}", this.dispatchCreate.bind(this));
|
|
88
|
-
this.federation.setObjectDispatcher(Article, "/ap/article/{id}", (ctx, values) => this.dispatchMessage(Article, ctx, values.id));
|
|
89
|
-
this.federation.setObjectDispatcher(ChatMessage, "/ap/chat-message/{id}", (ctx, values) => this.dispatchMessage(ChatMessage, ctx, values.id));
|
|
90
|
-
this.federation.setObjectDispatcher(Note, "/ap/note/{id}", (ctx, values) => this.dispatchMessage(Note, ctx, values.id));
|
|
91
|
-
this.federation.setObjectDispatcher(Question, "/ap/question/{id}", (ctx, values) => this.dispatchMessage(Question, ctx, values.id));
|
|
92
|
-
this.federation.setObjectDispatcher(Announce, "/ap/announce/{id}", this.dispatchAnnounce.bind(this));
|
|
93
|
-
this.federation.setObjectDispatcher(Emoji, "/ap/emoji/{name}", this.dispatchEmoji.bind(this));
|
|
94
|
-
this.federation.setInboxListeners("/ap/actor/{identifier}/inbox", "/ap/inbox").onUnverifiedActivity(this.onUnverifiedActivity.bind(this)).on(Follow, this.onFollowed.bind(this)).on(Undo, async (ctx, undo) => {
|
|
95
|
-
const object = await undo.getObject(ctx);
|
|
96
|
-
if (object instanceof Follow) await this.onUnfollowed(ctx, undo);
|
|
97
|
-
else if (object instanceof Like) await this.onUnliked(ctx, undo);
|
|
98
|
-
else {
|
|
99
|
-
const logger = getLogger([
|
|
100
|
-
"botkit",
|
|
101
|
-
"bot",
|
|
102
|
-
"inbox"
|
|
103
|
-
]);
|
|
104
|
-
logger.warn("The Undo object {undoId} is not about Follow or Like: {object}.", {
|
|
105
|
-
undoId: undo.id?.href,
|
|
106
|
-
object
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
}).on(Accept, this.onFollowAccepted.bind(this)).on(Reject, this.onFollowRejected.bind(this)).on(Create, this.onCreated.bind(this)).on(Announce, this.onAnnounced.bind(this)).on(Like, this.onLiked.bind(this)).setSharedKeyDispatcher(this.dispatchSharedKey.bind(this));
|
|
110
|
-
if (this.software != null) this.federation.setNodeInfoDispatcher("/nodeinfo/2.1", this.dispatchNodeInfo.bind(this));
|
|
119
|
+
this.repository = this.instance.repository.forIdentifier(this.identifier);
|
|
120
|
+
if (!options.transient) this.instance.addBot(this);
|
|
111
121
|
}
|
|
112
122
|
async getActorSummary(session) {
|
|
113
123
|
if (this.summary == null) return null;
|
|
@@ -163,7 +173,7 @@ var BotImpl = class {
|
|
|
163
173
|
outbox: ctx.getOutboxUri(identifier),
|
|
164
174
|
publicKey: keyPairs[0].cryptographicKey,
|
|
165
175
|
assertionMethods: keyPairs.map((pair) => pair.multikey),
|
|
166
|
-
url:
|
|
176
|
+
url: this.instance.getBotWebUrl(this, ctx.origin)
|
|
167
177
|
});
|
|
168
178
|
}
|
|
169
179
|
mapHandle(_ctx, username) {
|
|
@@ -266,11 +276,13 @@ var BotImpl = class {
|
|
|
266
276
|
return await this.repository.countMessages();
|
|
267
277
|
}
|
|
268
278
|
async dispatchFollow(_ctx, values) {
|
|
279
|
+
if (values.identifier !== this.identifier) return null;
|
|
269
280
|
const id = values.id;
|
|
270
281
|
const follow = await this.repository.getSentFollow(id);
|
|
271
282
|
return follow ?? null;
|
|
272
283
|
}
|
|
273
284
|
async authorizeFollow(ctx, values) {
|
|
285
|
+
if (values.identifier !== this.identifier) return false;
|
|
274
286
|
const signedKeyOwner = await ctx.getSignedKeyOwner();
|
|
275
287
|
if (signedKeyOwner == null || signedKeyOwner.id == null) return false;
|
|
276
288
|
const id = values.id;
|
|
@@ -279,6 +291,7 @@ var BotImpl = class {
|
|
|
279
291
|
return signedKeyOwner.id.href === follow.objectId?.href || signedKeyOwner.id.href === follow.actorId?.href;
|
|
280
292
|
}
|
|
281
293
|
async dispatchCreate(ctx, values) {
|
|
294
|
+
if (values.identifier !== this.identifier) return null;
|
|
282
295
|
const activity = await this.repository.getMessage(values.id);
|
|
283
296
|
if (!(activity instanceof Create)) return null;
|
|
284
297
|
const isVisible = await this.getPermissionChecker(ctx);
|
|
@@ -296,21 +309,20 @@ var BotImpl = class {
|
|
|
296
309
|
return object;
|
|
297
310
|
}
|
|
298
311
|
async dispatchAnnounce(ctx, values) {
|
|
312
|
+
if (values.identifier !== this.identifier) return null;
|
|
299
313
|
const activity = await this.repository.getMessage(values.id);
|
|
300
314
|
if (!(activity instanceof Announce)) return null;
|
|
301
315
|
const isVisible = await this.getPermissionChecker(ctx);
|
|
302
316
|
return isVisible(activity) ? activity : null;
|
|
303
317
|
}
|
|
304
318
|
dispatchEmoji(ctx, values) {
|
|
305
|
-
|
|
306
|
-
if (customEmoji == null) return null;
|
|
307
|
-
return this.getEmoji(ctx, values.name, customEmoji);
|
|
319
|
+
return this.instance.dispatchEmoji(ctx, values);
|
|
308
320
|
}
|
|
309
|
-
dispatchSharedKey(
|
|
310
|
-
return
|
|
321
|
+
dispatchSharedKey(ctx) {
|
|
322
|
+
return this.instance.dispatchSharedKey(ctx);
|
|
311
323
|
}
|
|
312
|
-
onUnverifiedActivity(
|
|
313
|
-
|
|
324
|
+
onUnverifiedActivity(ctx, activity, reason) {
|
|
325
|
+
return this.instance.onUnverifiedActivity(ctx, activity, reason);
|
|
314
326
|
}
|
|
315
327
|
async onFollowed(ctx, follow) {
|
|
316
328
|
const botUri = ctx.getActorUri(this.identifier);
|
|
@@ -339,8 +351,8 @@ var BotImpl = class {
|
|
|
339
351
|
}
|
|
340
352
|
}
|
|
341
353
|
async onFollowAccepted(ctx, accept) {
|
|
342
|
-
const parsedObj = ctx
|
|
343
|
-
if (parsedObj?.type !== "object" || parsedObj.class !== Follow) return;
|
|
354
|
+
const parsedObj = parseLocalUri(ctx, accept.objectId, this.legacyObjectUrisIdentifier);
|
|
355
|
+
if (parsedObj?.type !== "object" || parsedObj.class !== Follow || parsedObj.values.identifier !== this.identifier) return;
|
|
344
356
|
const follow = await this.repository.getSentFollow(parsedObj.values.id);
|
|
345
357
|
if (follow == null) return;
|
|
346
358
|
const followee = await follow.getObject(ctx);
|
|
@@ -352,8 +364,8 @@ var BotImpl = class {
|
|
|
352
364
|
}
|
|
353
365
|
}
|
|
354
366
|
async onFollowRejected(ctx, reject) {
|
|
355
|
-
const parsedObj = ctx
|
|
356
|
-
if (parsedObj?.type !== "object" || parsedObj.class !== Follow) return;
|
|
367
|
+
const parsedObj = parseLocalUri(ctx, reject.objectId, this.legacyObjectUrisIdentifier);
|
|
368
|
+
if (parsedObj?.type !== "object" || parsedObj.class !== Follow || parsedObj.values.identifier !== this.identifier) return;
|
|
357
369
|
const id = parsedObj.values.id;
|
|
358
370
|
const follow = await this.repository.getSentFollow(id);
|
|
359
371
|
if (follow == null) return;
|
|
@@ -374,8 +386,8 @@ var BotImpl = class {
|
|
|
374
386
|
if (messageCache != null) return messageCache;
|
|
375
387
|
return messageCache = await createMessage(object, session, {});
|
|
376
388
|
};
|
|
377
|
-
const replyTarget = ctx
|
|
378
|
-
if (this.onVote != null && object instanceof Note && replyTarget?.type === "object" && messageClasses.includes(replyTarget.class) && object.name != null) {
|
|
389
|
+
const replyTarget = parseLocalUri(ctx, object.replyTargetId, this.legacyObjectUrisIdentifier);
|
|
390
|
+
if (this.onVote != null && object instanceof Note && replyTarget?.type === "object" && messageClasses.includes(replyTarget.class) && replyTarget.values.identifier === this.identifier && object.name != null) {
|
|
379
391
|
if (create.actorId == null || create.actorId.href === session.actorId.href) return;
|
|
380
392
|
const actorId = create.actorId;
|
|
381
393
|
const actor = await create.getActor(ctx);
|
|
@@ -460,7 +472,7 @@ var BotImpl = class {
|
|
|
460
472
|
}
|
|
461
473
|
return;
|
|
462
474
|
}
|
|
463
|
-
if (this.onReply != null && replyTarget?.type === "object" && messageClasses.includes(replyTarget.class)) {
|
|
475
|
+
if (this.onReply != null && replyTarget?.type === "object" && messageClasses.includes(replyTarget.class) && replyTarget.values.identifier === this.identifier) {
|
|
464
476
|
const message = await getMessage();
|
|
465
477
|
if (message.visibility === "public" || message.visibility === "unlisted") await ctx.forwardActivity(this, "followers", {
|
|
466
478
|
skipIfUnsigned: true,
|
|
@@ -475,8 +487,8 @@ var BotImpl = class {
|
|
|
475
487
|
break;
|
|
476
488
|
}
|
|
477
489
|
if (quoteUrl == null) quoteUrl = object.quoteUrl;
|
|
478
|
-
const quoteTarget = ctx.
|
|
479
|
-
if (this.onQuote != null && quoteTarget?.type === "object" && messageClasses.includes(quoteTarget.class)) {
|
|
490
|
+
const quoteTarget = parseLocalUri(ctx, quoteUrl, this.legacyObjectUrisIdentifier);
|
|
491
|
+
if (this.onQuote != null && quoteTarget?.type === "object" && messageClasses.includes(quoteTarget.class) && quoteTarget.values.identifier === this.identifier) {
|
|
480
492
|
const message = await getMessage();
|
|
481
493
|
if (message.visibility === "public" || message.visibility === "unlisted") await ctx.forwardActivity(this, "followers", {
|
|
482
494
|
skipIfUnsigned: true,
|
|
@@ -496,9 +508,9 @@ var BotImpl = class {
|
|
|
496
508
|
}
|
|
497
509
|
async onAnnounced(ctx, announce) {
|
|
498
510
|
if (this.onSharedMessage == null || announce.id == null || announce.actorId == null) return;
|
|
499
|
-
const objectUri = ctx
|
|
511
|
+
const objectUri = parseLocalUri(ctx, announce.objectId, this.legacyObjectUrisIdentifier);
|
|
500
512
|
let object = null;
|
|
501
|
-
if (objectUri?.type === "object" && messageClasses.includes(objectUri.class)) {
|
|
513
|
+
if (objectUri?.type === "object" && messageClasses.includes(objectUri.class) && objectUri.values.identifier === this.identifier) {
|
|
502
514
|
const msg = await this.repository.getMessage(objectUri.values.id);
|
|
503
515
|
if (msg instanceof Create) object = await msg.getObject(ctx);
|
|
504
516
|
} else object = await announce.getObject(ctx);
|
|
@@ -518,9 +530,10 @@ var BotImpl = class {
|
|
|
518
530
|
}
|
|
519
531
|
async #parseLike(ctx, like) {
|
|
520
532
|
if (like.id == null || like.actorId == null) return void 0;
|
|
521
|
-
const objectUri = ctx
|
|
533
|
+
const objectUri = parseLocalUri(ctx, like.objectId, this.legacyObjectUrisIdentifier);
|
|
522
534
|
let object = null;
|
|
523
535
|
if (objectUri?.type === "object" && messageClasses.includes(objectUri.class)) {
|
|
536
|
+
if (objectUri.values.identifier !== this.identifier) return void 0;
|
|
524
537
|
const msg = await this.repository.getMessage(objectUri.values.id);
|
|
525
538
|
if (msg instanceof Create) object = await msg.getObject(ctx);
|
|
526
539
|
} else object = await like.getObject(ctx);
|
|
@@ -569,9 +582,10 @@ var BotImpl = class {
|
|
|
569
582
|
}
|
|
570
583
|
}
|
|
571
584
|
if (emoji == null) return void 0;
|
|
572
|
-
const objectUri = ctx
|
|
585
|
+
const objectUri = parseLocalUri(ctx, react.objectId, this.legacyObjectUrisIdentifier);
|
|
573
586
|
let object = null;
|
|
574
587
|
if (objectUri?.type === "object" && messageClasses.includes(objectUri.class)) {
|
|
588
|
+
if (objectUri.values.identifier !== this.identifier) return void 0;
|
|
575
589
|
const msg = await this.repository.getMessage(objectUri.values.id);
|
|
576
590
|
if (msg instanceof Create) object = await msg.getObject(ctx);
|
|
577
591
|
} else object = await react.getObject(ctx);
|
|
@@ -608,119 +622,327 @@ var BotImpl = class {
|
|
|
608
622
|
const { session, reaction } = sessionAndReaction;
|
|
609
623
|
await this.onUnreact(session, reaction);
|
|
610
624
|
}
|
|
611
|
-
dispatchNodeInfo(
|
|
612
|
-
return
|
|
613
|
-
software: this.software,
|
|
614
|
-
protocols: ["activitypub"],
|
|
615
|
-
services: { outbound: ["atom1.0"] },
|
|
616
|
-
usage: {
|
|
617
|
-
users: {
|
|
618
|
-
total: 1,
|
|
619
|
-
activeMonth: 1,
|
|
620
|
-
activeHalfyear: 1
|
|
621
|
-
},
|
|
622
|
-
localPosts: 0,
|
|
623
|
-
localComments: 0
|
|
624
|
-
}
|
|
625
|
-
};
|
|
625
|
+
dispatchNodeInfo(ctx) {
|
|
626
|
+
return this.instance.dispatchNodeInfo(ctx);
|
|
626
627
|
}
|
|
627
628
|
getSession(origin, contextData) {
|
|
628
629
|
const ctx = typeof origin === "string" || origin instanceof URL ? this.federation.createContext(new URL(origin), contextData) : origin;
|
|
629
630
|
return new SessionImpl(this, ctx);
|
|
630
631
|
}
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
const ctx = this.federation.createContext(request, contextData);
|
|
634
|
-
const parsed = ctx.parseUri(new URL(request.url));
|
|
635
|
-
if (parsed == null || parsed.type !== "outbox" && parsed.type !== "followers" || parsed.identifier == null) return response;
|
|
636
|
-
const contentType = response.headers.get("Content-Type");
|
|
637
|
-
if (contentType == null || !contentType.startsWith("application/activity+json") && !contentType.startsWith("application/ld+json")) return response;
|
|
638
|
-
const body = await response.json();
|
|
639
|
-
if (typeof body !== "object" || body == null || Array.isArray(body)) return new Response(JSON.stringify(body), {
|
|
640
|
-
headers: response.headers,
|
|
641
|
-
status: response.status,
|
|
642
|
-
statusText: response.statusText
|
|
643
|
-
});
|
|
644
|
-
const property = parsed.type === "outbox" ? "outboxOf" : "followersOf";
|
|
645
|
-
const actorUri = ctx.getActorUri(parsed.identifier).href;
|
|
646
|
-
if (body[property] === actorUri) return new Response(JSON.stringify(body), {
|
|
647
|
-
headers: response.headers,
|
|
648
|
-
status: response.status,
|
|
649
|
-
statusText: response.statusText
|
|
650
|
-
});
|
|
651
|
-
const headers = new Headers(response.headers);
|
|
652
|
-
headers.delete("Content-Length");
|
|
653
|
-
return new Response(JSON.stringify({
|
|
654
|
-
...body,
|
|
655
|
-
[property]: actorUri
|
|
656
|
-
}), {
|
|
657
|
-
headers,
|
|
658
|
-
status: response.status,
|
|
659
|
-
statusText: response.statusText
|
|
660
|
-
});
|
|
632
|
+
addCollectionInverseProperty(request, contextData, response) {
|
|
633
|
+
return this.instance.addCollectionInverseProperty(request, contextData, response);
|
|
661
634
|
}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
const url = new URL(request.url);
|
|
665
|
-
if (url.pathname.startsWith("/.well-known/") || url.pathname.startsWith("/ap/") || url.pathname.startsWith("/nodeinfo/")) {
|
|
666
|
-
const response = await this.federation.fetch(request, { contextData });
|
|
667
|
-
return await this.addCollectionInverseProperty(request, contextData, response);
|
|
668
|
-
}
|
|
669
|
-
const match = /^\/emojis\/([a-z0-9-_]+)(?:$|\.)/.exec(url.pathname);
|
|
670
|
-
if (match != null) {
|
|
671
|
-
const customEmoji = this.customEmojis[match[1]];
|
|
672
|
-
if (customEmoji == null || !("file" in customEmoji)) return new Response("Not Found", { status: 404 });
|
|
673
|
-
let file;
|
|
674
|
-
try {
|
|
675
|
-
file = await fs.open(customEmoji.file, "r");
|
|
676
|
-
} catch (error) {
|
|
677
|
-
if (typeof error === "object" && error != null && "code" in error && error.code === "ENOENT") return new Response("Not Found", { status: 404 });
|
|
678
|
-
throw error;
|
|
679
|
-
}
|
|
680
|
-
const fileInfo = await file.stat();
|
|
681
|
-
return new Response(file.readableWebStream(), { headers: {
|
|
682
|
-
"Content-Type": customEmoji.type,
|
|
683
|
-
"Content-Length": fileInfo.size.toString(),
|
|
684
|
-
"Cache-Control": "public, max-age=31536000, immutable",
|
|
685
|
-
"Last-Modified": (fileInfo.mtime ?? /* @__PURE__ */ new Date()).toUTCString(),
|
|
686
|
-
"ETag": `"${fileInfo.mtime?.getTime().toString(36)}${fileInfo.size.toString(36)}"`
|
|
687
|
-
} });
|
|
688
|
-
}
|
|
689
|
-
return await app.fetch(request, {
|
|
690
|
-
bot: this,
|
|
691
|
-
contextData
|
|
692
|
-
});
|
|
635
|
+
fetch(request, contextData) {
|
|
636
|
+
return this.instance.fetch(request, contextData);
|
|
693
637
|
}
|
|
694
638
|
getEmoji(ctx, name, data) {
|
|
695
|
-
|
|
696
|
-
if ("url" in data) url = new URL(data.url);
|
|
697
|
-
else {
|
|
698
|
-
const t = mimeDb[data.type];
|
|
699
|
-
url = new URL(`/emojis/${name}${t == null || t.extensions == null || t.extensions.length < 1 ? "" : `.${t.extensions[0]}`}`, ctx.origin);
|
|
700
|
-
}
|
|
701
|
-
return new Emoji({
|
|
702
|
-
id: ctx.getObjectUri(Emoji, { name }),
|
|
703
|
-
name: `:${name}:`,
|
|
704
|
-
icon: new Image({
|
|
705
|
-
mediaType: data.type,
|
|
706
|
-
url
|
|
707
|
-
})
|
|
708
|
-
});
|
|
639
|
+
return this.instance.getEmoji(ctx, name, data);
|
|
709
640
|
}
|
|
710
641
|
addCustomEmoji(name, data) {
|
|
711
|
-
|
|
712
|
-
else if (name in this.customEmojis) throw new TypeError(`Duplicate custom emoji name: ${name}`);
|
|
713
|
-
else if (!data.type.startsWith("image/")) throw new TypeError(`Unsupported media type: ${data.type}`);
|
|
714
|
-
this.customEmojis[name] = data;
|
|
715
|
-
return (session) => this.getEmoji(session.context, name, data);
|
|
642
|
+
return this.instance.addCustomEmoji(name, data);
|
|
716
643
|
}
|
|
717
644
|
addCustomEmojis(emojis) {
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
645
|
+
return this.instance.addCustomEmojis(emojis);
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
/**
|
|
649
|
+
* Wraps a {@link BotImpl} instance with a plain object implementing
|
|
650
|
+
* the {@link Bot} interface. Since `deno serve` does not recognize a class
|
|
651
|
+
* instance having fetch(), we wrap a BotImpl instance with a plain object.
|
|
652
|
+
* See also https://github.com/denoland/deno/issues/24062
|
|
653
|
+
* @param bot The bot implementation to wrap.
|
|
654
|
+
* @returns The wrapped bot.
|
|
655
|
+
* @internal
|
|
656
|
+
*/
|
|
657
|
+
function wrapBotImpl(bot) {
|
|
658
|
+
const wrapper = {
|
|
659
|
+
impl: bot,
|
|
660
|
+
get federation() {
|
|
661
|
+
return bot.federation;
|
|
662
|
+
},
|
|
663
|
+
get identifier() {
|
|
664
|
+
return bot.identifier;
|
|
665
|
+
},
|
|
666
|
+
getSession(a, b) {
|
|
667
|
+
return bot.getSession(a, b);
|
|
668
|
+
},
|
|
669
|
+
fetch(request, contextData) {
|
|
670
|
+
return bot.fetch(request, contextData);
|
|
671
|
+
},
|
|
672
|
+
addCustomEmojis(emojis) {
|
|
673
|
+
return bot.addCustomEmojis(emojis);
|
|
674
|
+
},
|
|
675
|
+
get onFollow() {
|
|
676
|
+
return bot.onFollow;
|
|
677
|
+
},
|
|
678
|
+
set onFollow(value) {
|
|
679
|
+
bot.onFollow = value;
|
|
680
|
+
},
|
|
681
|
+
get onUnfollow() {
|
|
682
|
+
return bot.onUnfollow;
|
|
683
|
+
},
|
|
684
|
+
set onUnfollow(value) {
|
|
685
|
+
bot.onUnfollow = value;
|
|
686
|
+
},
|
|
687
|
+
get onAcceptFollow() {
|
|
688
|
+
return bot.onAcceptFollow;
|
|
689
|
+
},
|
|
690
|
+
set onAcceptFollow(value) {
|
|
691
|
+
bot.onAcceptFollow = value;
|
|
692
|
+
},
|
|
693
|
+
get onRejectFollow() {
|
|
694
|
+
return bot.onRejectFollow;
|
|
695
|
+
},
|
|
696
|
+
set onRejectFollow(value) {
|
|
697
|
+
bot.onRejectFollow = value;
|
|
698
|
+
},
|
|
699
|
+
get onMention() {
|
|
700
|
+
return bot.onMention;
|
|
701
|
+
},
|
|
702
|
+
set onMention(value) {
|
|
703
|
+
bot.onMention = value;
|
|
704
|
+
},
|
|
705
|
+
get onReply() {
|
|
706
|
+
return bot.onReply;
|
|
707
|
+
},
|
|
708
|
+
set onReply(value) {
|
|
709
|
+
bot.onReply = value;
|
|
710
|
+
},
|
|
711
|
+
get onQuote() {
|
|
712
|
+
return bot.onQuote;
|
|
713
|
+
},
|
|
714
|
+
set onQuote(value) {
|
|
715
|
+
bot.onQuote = value;
|
|
716
|
+
},
|
|
717
|
+
get onMessage() {
|
|
718
|
+
return bot.onMessage;
|
|
719
|
+
},
|
|
720
|
+
set onMessage(value) {
|
|
721
|
+
bot.onMessage = value;
|
|
722
|
+
},
|
|
723
|
+
get onSharedMessage() {
|
|
724
|
+
return bot.onSharedMessage;
|
|
725
|
+
},
|
|
726
|
+
set onSharedMessage(value) {
|
|
727
|
+
bot.onSharedMessage = value;
|
|
728
|
+
},
|
|
729
|
+
get onLike() {
|
|
730
|
+
return bot.onLike;
|
|
731
|
+
},
|
|
732
|
+
set onLike(value) {
|
|
733
|
+
bot.onLike = value;
|
|
734
|
+
},
|
|
735
|
+
get onUnlike() {
|
|
736
|
+
return bot.onUnlike;
|
|
737
|
+
},
|
|
738
|
+
set onUnlike(value) {
|
|
739
|
+
bot.onUnlike = value;
|
|
740
|
+
},
|
|
741
|
+
get onReact() {
|
|
742
|
+
return bot.onReact;
|
|
743
|
+
},
|
|
744
|
+
set onReact(value) {
|
|
745
|
+
bot.onReact = value;
|
|
746
|
+
},
|
|
747
|
+
get onUnreact() {
|
|
748
|
+
return bot.onUnreact;
|
|
749
|
+
},
|
|
750
|
+
set onUnreact(value) {
|
|
751
|
+
bot.onUnreact = value;
|
|
752
|
+
},
|
|
753
|
+
get onVote() {
|
|
754
|
+
return bot.onVote;
|
|
755
|
+
},
|
|
756
|
+
set onVote(value) {
|
|
757
|
+
bot.onVote = value;
|
|
758
|
+
}
|
|
759
|
+
};
|
|
760
|
+
return wrapper;
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* A repository decorator that adopts legacy (pre-0.5) data for a bot actor
|
|
764
|
+
* before the first repository operation. The migration is kicked off at
|
|
765
|
+
* construction time and every operation awaits its completion, so data
|
|
766
|
+
* stored by BotKit 0.4 or earlier is visible from the start.
|
|
767
|
+
* @internal
|
|
768
|
+
*/
|
|
769
|
+
var MigrationGatedRepository = class {
|
|
770
|
+
#repository;
|
|
771
|
+
#migration;
|
|
772
|
+
constructor(repository, identifier) {
|
|
773
|
+
this.#repository = repository;
|
|
774
|
+
this.#migration = repository.migrate?.(identifier) ?? Promise.resolve();
|
|
775
|
+
this.#migration.catch(() => {});
|
|
776
|
+
}
|
|
777
|
+
async setKeyPairs(identifier, keyPairs) {
|
|
778
|
+
await this.#migration;
|
|
779
|
+
return await this.#repository.setKeyPairs(identifier, keyPairs);
|
|
780
|
+
}
|
|
781
|
+
async getKeyPairs(identifier) {
|
|
782
|
+
await this.#migration;
|
|
783
|
+
return await this.#repository.getKeyPairs(identifier);
|
|
784
|
+
}
|
|
785
|
+
async addMessage(identifier, id, activity) {
|
|
786
|
+
await this.#migration;
|
|
787
|
+
return await this.#repository.addMessage(identifier, id, activity);
|
|
788
|
+
}
|
|
789
|
+
async updateMessage(identifier, id, updater) {
|
|
790
|
+
await this.#migration;
|
|
791
|
+
return await this.#repository.updateMessage(identifier, id, updater);
|
|
792
|
+
}
|
|
793
|
+
async removeMessage(identifier, id) {
|
|
794
|
+
await this.#migration;
|
|
795
|
+
return await this.#repository.removeMessage(identifier, id);
|
|
796
|
+
}
|
|
797
|
+
async *getMessages(identifier, options) {
|
|
798
|
+
await this.#migration;
|
|
799
|
+
yield* this.#repository.getMessages(identifier, options);
|
|
800
|
+
}
|
|
801
|
+
async getMessage(identifier, id) {
|
|
802
|
+
await this.#migration;
|
|
803
|
+
return await this.#repository.getMessage(identifier, id);
|
|
804
|
+
}
|
|
805
|
+
async countMessages(identifier) {
|
|
806
|
+
await this.#migration;
|
|
807
|
+
return await this.#repository.countMessages(identifier);
|
|
808
|
+
}
|
|
809
|
+
async addFollower(identifier, followId, follower) {
|
|
810
|
+
await this.#migration;
|
|
811
|
+
return await this.#repository.addFollower(identifier, followId, follower);
|
|
812
|
+
}
|
|
813
|
+
async removeFollower(identifier, followId, followerId) {
|
|
814
|
+
await this.#migration;
|
|
815
|
+
return await this.#repository.removeFollower(identifier, followId, followerId);
|
|
816
|
+
}
|
|
817
|
+
async hasFollower(identifier, followerId) {
|
|
818
|
+
await this.#migration;
|
|
819
|
+
return await this.#repository.hasFollower(identifier, followerId);
|
|
820
|
+
}
|
|
821
|
+
async *getFollowers(identifier, options) {
|
|
822
|
+
await this.#migration;
|
|
823
|
+
yield* this.#repository.getFollowers(identifier, options);
|
|
824
|
+
}
|
|
825
|
+
async countFollowers(identifier) {
|
|
826
|
+
await this.#migration;
|
|
827
|
+
return await this.#repository.countFollowers(identifier);
|
|
828
|
+
}
|
|
829
|
+
async addSentFollow(identifier, id, follow) {
|
|
830
|
+
await this.#migration;
|
|
831
|
+
return await this.#repository.addSentFollow(identifier, id, follow);
|
|
832
|
+
}
|
|
833
|
+
async removeSentFollow(identifier, id) {
|
|
834
|
+
await this.#migration;
|
|
835
|
+
return await this.#repository.removeSentFollow(identifier, id);
|
|
836
|
+
}
|
|
837
|
+
async getSentFollow(identifier, id) {
|
|
838
|
+
await this.#migration;
|
|
839
|
+
return await this.#repository.getSentFollow(identifier, id);
|
|
840
|
+
}
|
|
841
|
+
async addFollowee(identifier, followeeId, follow) {
|
|
842
|
+
await this.#migration;
|
|
843
|
+
return await this.#repository.addFollowee(identifier, followeeId, follow);
|
|
844
|
+
}
|
|
845
|
+
async removeFollowee(identifier, followeeId) {
|
|
846
|
+
await this.#migration;
|
|
847
|
+
return await this.#repository.removeFollowee(identifier, followeeId);
|
|
848
|
+
}
|
|
849
|
+
async getFollowee(identifier, followeeId) {
|
|
850
|
+
await this.#migration;
|
|
851
|
+
return await this.#repository.getFollowee(identifier, followeeId);
|
|
852
|
+
}
|
|
853
|
+
async *findFollowedBots(followeeId) {
|
|
854
|
+
await this.#migration;
|
|
855
|
+
yield* this.#repository.findFollowedBots(followeeId);
|
|
856
|
+
}
|
|
857
|
+
async vote(identifier, messageId, voterId, option) {
|
|
858
|
+
await this.#migration;
|
|
859
|
+
return await this.#repository.vote(identifier, messageId, voterId, option);
|
|
860
|
+
}
|
|
861
|
+
async countVoters(identifier, messageId) {
|
|
862
|
+
await this.#migration;
|
|
863
|
+
return await this.#repository.countVoters(identifier, messageId);
|
|
864
|
+
}
|
|
865
|
+
async countVotes(identifier, messageId) {
|
|
866
|
+
await this.#migration;
|
|
867
|
+
return await this.#repository.countVotes(identifier, messageId);
|
|
868
|
+
}
|
|
869
|
+
forIdentifier(identifier) {
|
|
870
|
+
return new ActorScopedRepository(this, identifier);
|
|
871
|
+
}
|
|
872
|
+
async migrate(identifier) {
|
|
873
|
+
await this.#migration;
|
|
874
|
+
await this.#repository.migrate?.(identifier);
|
|
875
|
+
}
|
|
876
|
+
};
|
|
877
|
+
/**
|
|
878
|
+
* The internal implementation of a {@link BotGroup}: a registry of event
|
|
879
|
+
* handlers shared by every bot its dispatcher resolves.
|
|
880
|
+
* @internal
|
|
881
|
+
*/
|
|
882
|
+
var BotGroupImpl = class {
|
|
883
|
+
instance;
|
|
884
|
+
dispatcher;
|
|
885
|
+
mapUsername;
|
|
886
|
+
onFollow;
|
|
887
|
+
onUnfollow;
|
|
888
|
+
onAcceptFollow;
|
|
889
|
+
onRejectFollow;
|
|
890
|
+
onMention;
|
|
891
|
+
onReply;
|
|
892
|
+
onQuote;
|
|
893
|
+
onMessage;
|
|
894
|
+
onSharedMessage;
|
|
895
|
+
onLike;
|
|
896
|
+
onUnlike;
|
|
897
|
+
onReact;
|
|
898
|
+
onUnreact;
|
|
899
|
+
onVote;
|
|
900
|
+
constructor(instance, dispatcher, options = {}) {
|
|
901
|
+
this.instance = instance;
|
|
902
|
+
this.dispatcher = dispatcher;
|
|
903
|
+
this.mapUsername = options.mapUsername;
|
|
904
|
+
}
|
|
905
|
+
async getSession(origin, identifier, contextData) {
|
|
906
|
+
const ctx = this.instance.federation.createContext(new URL(origin), contextData);
|
|
907
|
+
const bot = await this.instance.resolveBot(ctx, identifier);
|
|
908
|
+
if (bot == null || !(bot instanceof GroupBotImpl) || bot.group !== this) throw new TypeError(`The group's dispatcher does not resolve the identifier: ${identifier}`);
|
|
909
|
+
return bot.getSession(ctx);
|
|
910
|
+
}
|
|
911
|
+
};
|
|
912
|
+
/**
|
|
913
|
+
* A transient per-bot view of a dynamically resolved bot. It behaves like
|
|
914
|
+
* a regular {@link BotImpl}, except that its event handlers are read live
|
|
915
|
+
* from the owning {@link BotGroupImpl} at dispatch time, so handlers
|
|
916
|
+
* registered on the group after a bot was resolved still fire. Views are
|
|
917
|
+
* not registered on the instance and live only as long as the resolution
|
|
918
|
+
* cache of the request that produced them.
|
|
919
|
+
* @internal
|
|
920
|
+
*/
|
|
921
|
+
var GroupBotImpl = class extends BotImpl {
|
|
922
|
+
group;
|
|
923
|
+
constructor(group, identifier, profile) {
|
|
924
|
+
super({
|
|
925
|
+
instance: group.instance,
|
|
926
|
+
transient: true,
|
|
927
|
+
identifier,
|
|
928
|
+
kv: group.instance.kv,
|
|
929
|
+
class: profile.class,
|
|
930
|
+
username: profile.username,
|
|
931
|
+
name: profile.name,
|
|
932
|
+
summary: profile.summary,
|
|
933
|
+
icon: profile.icon,
|
|
934
|
+
image: profile.image,
|
|
935
|
+
properties: profile.properties,
|
|
936
|
+
followerPolicy: profile.followerPolicy
|
|
937
|
+
});
|
|
938
|
+
this.group = group;
|
|
939
|
+
for (const name of botEventHandlerNames) globalThis.Object.defineProperty(this, name, {
|
|
940
|
+
get: () => group[name],
|
|
941
|
+
configurable: true
|
|
942
|
+
});
|
|
721
943
|
}
|
|
722
944
|
};
|
|
723
945
|
|
|
724
946
|
//#endregion
|
|
725
|
-
export { BotImpl };
|
|
947
|
+
export { BotGroupImpl, BotImpl, GroupBotImpl, MigrationGatedRepository, botEventHandlerNames, wrapBotImpl };
|
|
726
948
|
//# sourceMappingURL=bot-impl.js.map
|