@evanp/activitypub-bot 0.29.0 → 0.30.1
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/lib/activityhandler.js +40 -16
- package/lib/activitypubclient.js +11 -0
- package/lib/botcontext.js +10 -2
- package/lib/keystorage.js +6 -2
- package/lib/ratelimiter.js +11 -6
- package/package.json +1 -1
package/lib/activityhandler.js
CHANGED
|
@@ -151,10 +151,16 @@ export class ActivityHandler {
|
|
|
151
151
|
)
|
|
152
152
|
const recipients = this.#getRecipients(original)
|
|
153
153
|
this.#addRecipient(recipients, actor, 'to')
|
|
154
|
+
this.#removeRecipient(recipients, await this.#botActor(bot))
|
|
155
|
+
const parts = await this.#formatter.unformat(original.id)
|
|
156
|
+
const repliesId = await this.#formatter.format({
|
|
157
|
+
collection: 'replies',
|
|
158
|
+
...parts
|
|
159
|
+
})
|
|
154
160
|
await this.#doActivity(bot, {
|
|
155
161
|
type: 'Add',
|
|
156
|
-
object,
|
|
157
|
-
target:
|
|
162
|
+
object: object.id,
|
|
163
|
+
target: repliesId,
|
|
158
164
|
...recipients
|
|
159
165
|
})
|
|
160
166
|
}
|
|
@@ -205,10 +211,16 @@ export class ActivityHandler {
|
|
|
205
211
|
)
|
|
206
212
|
const recipients = this.#getRecipients(root)
|
|
207
213
|
this.#addRecipient(recipients, actor, 'to')
|
|
214
|
+
this.#removeRecipient(recipients, await this.#botActor(bot))
|
|
215
|
+
const parts = await this.#formatter.unformat(root.id)
|
|
216
|
+
const threadId = await this.#formatter.format({
|
|
217
|
+
collection: 'thread',
|
|
218
|
+
...parts
|
|
219
|
+
})
|
|
208
220
|
await this.#doActivity(bot, {
|
|
209
221
|
type: 'Add',
|
|
210
222
|
object,
|
|
211
|
-
target:
|
|
223
|
+
target: threadId,
|
|
212
224
|
...recipients
|
|
213
225
|
})
|
|
214
226
|
}
|
|
@@ -301,20 +313,26 @@ export class ActivityHandler {
|
|
|
301
313
|
'Sending accept',
|
|
302
314
|
{ actor: actor.id }
|
|
303
315
|
)
|
|
304
|
-
|
|
316
|
+
const followersId = this.#formatter.format({
|
|
317
|
+
username: bot.username,
|
|
318
|
+
collection: 'followers'
|
|
319
|
+
})
|
|
320
|
+
const add = await this.#doActivity(bot, {
|
|
305
321
|
type: 'Add',
|
|
306
|
-
object: actor,
|
|
307
|
-
target:
|
|
308
|
-
|
|
309
|
-
collection: 'followers'
|
|
310
|
-
}),
|
|
311
|
-
to: ['as:Public', actor.id]
|
|
322
|
+
object: actor.id,
|
|
323
|
+
target: followersId,
|
|
324
|
+
to: ['as:Public', actor.id, followersId]
|
|
312
325
|
})
|
|
313
|
-
|
|
326
|
+
this.#logger.debug({ add: await add.export() }, 'added follower')
|
|
327
|
+
const recipients = await this.#getRecipients(activity)
|
|
328
|
+
this.#addRecipient(recipients, actor, 'to')
|
|
329
|
+
this.#removeRecipient(recipients, await this.#botActor(bot))
|
|
330
|
+
const accept = await this.#doActivity(bot, {
|
|
314
331
|
type: 'Accept',
|
|
315
|
-
object: activity,
|
|
316
|
-
|
|
332
|
+
object: activity.id,
|
|
333
|
+
...recipients
|
|
317
334
|
})
|
|
335
|
+
this.#logger.debug({ accept: await accept.export() }, 'accepted follow')
|
|
318
336
|
this.#logger.debug({
|
|
319
337
|
msg: 'Notifying bot of new follow',
|
|
320
338
|
actor: actor.id
|
|
@@ -378,7 +396,7 @@ export class ActivityHandler {
|
|
|
378
396
|
)
|
|
379
397
|
await this.#doActivity(bot, {
|
|
380
398
|
type: 'Add',
|
|
381
|
-
object: actor,
|
|
399
|
+
object: actor.id,
|
|
382
400
|
target: this.#formatter.format({
|
|
383
401
|
username: bot.username,
|
|
384
402
|
collection: 'following'
|
|
@@ -510,7 +528,7 @@ export class ActivityHandler {
|
|
|
510
528
|
})
|
|
511
529
|
await this.#doActivity(bot, {
|
|
512
530
|
type: 'Add',
|
|
513
|
-
object: activity,
|
|
531
|
+
object: activity.id,
|
|
514
532
|
target: likesCollectionId,
|
|
515
533
|
...recipients
|
|
516
534
|
})
|
|
@@ -585,7 +603,7 @@ export class ActivityHandler {
|
|
|
585
603
|
})
|
|
586
604
|
await this.#doActivity(bot, {
|
|
587
605
|
type: 'Add',
|
|
588
|
-
object: activity,
|
|
606
|
+
object: activity.id,
|
|
589
607
|
target: sharesCollectionId,
|
|
590
608
|
...recipients
|
|
591
609
|
})
|
|
@@ -852,6 +870,8 @@ export class ActivityHandler {
|
|
|
852
870
|
}
|
|
853
871
|
|
|
854
872
|
#removeRecipient (recipients, actor) {
|
|
873
|
+
assert.ok(typeof recipients, 'object')
|
|
874
|
+
assert.ok(typeof actor, 'object')
|
|
855
875
|
const remove = (list) => {
|
|
856
876
|
if (!list) {
|
|
857
877
|
return
|
|
@@ -869,6 +889,9 @@ export class ActivityHandler {
|
|
|
869
889
|
}
|
|
870
890
|
|
|
871
891
|
#addRecipient (recipients, actor, key = 'to') {
|
|
892
|
+
assert.ok(typeof recipients, 'object')
|
|
893
|
+
assert.ok(typeof actor, 'object')
|
|
894
|
+
assert.ok(typeof key, 'string')
|
|
872
895
|
if (!actor.id) {
|
|
873
896
|
return
|
|
874
897
|
}
|
|
@@ -897,6 +920,7 @@ export class ActivityHandler {
|
|
|
897
920
|
await this.#actorStorage.addToCollection(bot.username, 'outbox', activity)
|
|
898
921
|
await this.#actorStorage.addToCollection(bot.username, 'inbox', activity)
|
|
899
922
|
await this.#distributor.distribute(activity, bot.username)
|
|
923
|
+
return activity
|
|
900
924
|
}
|
|
901
925
|
|
|
902
926
|
#formatActivityId (bot, type) {
|
package/lib/activitypubclient.js
CHANGED
|
@@ -53,6 +53,7 @@ export class ActivityPubClient {
|
|
|
53
53
|
async get (url, username = null) {
|
|
54
54
|
assert.ok(url)
|
|
55
55
|
assert.equal(typeof url, 'string')
|
|
56
|
+
assert.ok(username === null || username !== '*')
|
|
56
57
|
const res = await this.#getRes(url, username, true)
|
|
57
58
|
return await this.#handleRes(res, url)
|
|
58
59
|
}
|
|
@@ -101,6 +102,8 @@ export class ActivityPubClient {
|
|
|
101
102
|
|
|
102
103
|
async #handleRes (res, url) {
|
|
103
104
|
if (res.status < 200 || res.status > 299) {
|
|
105
|
+
const body = await res.text()
|
|
106
|
+
this.#logger.warn({ status: res.status, body, url }, 'Could not fetch url')
|
|
104
107
|
throw createHttpError(res.status, `Could not fetch ${url}`)
|
|
105
108
|
}
|
|
106
109
|
const contentType = res.headers.get('content-type')
|
|
@@ -136,6 +139,7 @@ export class ActivityPubClient {
|
|
|
136
139
|
assert.equal(typeof obj, 'object')
|
|
137
140
|
assert.ok(username)
|
|
138
141
|
assert.equal(typeof username, 'string')
|
|
142
|
+
assert.ok(username !== '*')
|
|
139
143
|
const json = await obj.export()
|
|
140
144
|
this.#fixupJson(json)
|
|
141
145
|
const body = JSON.stringify(json)
|
|
@@ -186,6 +190,13 @@ export class ActivityPubClient {
|
|
|
186
190
|
}
|
|
187
191
|
|
|
188
192
|
async * items (id, username = null) {
|
|
193
|
+
assert.ok(id)
|
|
194
|
+
assert.equal(typeof id, 'string')
|
|
195
|
+
assert.ok(
|
|
196
|
+
username === null ||
|
|
197
|
+
(typeof username === 'string' && username !== '*')
|
|
198
|
+
)
|
|
199
|
+
|
|
189
200
|
const coll = await this.get(id, username)
|
|
190
201
|
|
|
191
202
|
this.#logger.debug(`Got object ${id}`)
|
package/lib/botcontext.js
CHANGED
|
@@ -91,7 +91,7 @@ export class BotContext {
|
|
|
91
91
|
if (this.#formatter.isLocal(id)) {
|
|
92
92
|
return await this.#objectStorage.read(id)
|
|
93
93
|
} else {
|
|
94
|
-
return await this.#
|
|
94
|
+
return await this.#getRemoteObject(id)
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -362,7 +362,7 @@ export class BotContext {
|
|
|
362
362
|
}
|
|
363
363
|
|
|
364
364
|
async toWebfinger (actorId) {
|
|
365
|
-
const actor = await this.#
|
|
365
|
+
const actor = await this.#getRemoteObject(actorId)
|
|
366
366
|
if (!actor) {
|
|
367
367
|
return null
|
|
368
368
|
}
|
|
@@ -526,4 +526,12 @@ export class BotContext {
|
|
|
526
526
|
async onIdle () {
|
|
527
527
|
await this.#distributor.onIdle()
|
|
528
528
|
}
|
|
529
|
+
|
|
530
|
+
async #getRemoteObject (id) {
|
|
531
|
+
if (this.#botId === '*') {
|
|
532
|
+
return await this.#client.get(id)
|
|
533
|
+
} else {
|
|
534
|
+
return await this.#client.get(id, this.#botId)
|
|
535
|
+
}
|
|
536
|
+
}
|
|
529
537
|
}
|
package/lib/keystorage.js
CHANGED
|
@@ -17,7 +17,9 @@ export class KeyStorage {
|
|
|
17
17
|
this.#hasher = new HumanHasher()
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
async getPublicKey (username) {
|
|
20
|
+
async getPublicKey (username = null) {
|
|
21
|
+
assert.ok(username === null || typeof username === 'string')
|
|
22
|
+
assert.ok(username !== '*')
|
|
21
23
|
this.#logger.debug(
|
|
22
24
|
{ username, method: 'KeyStorage.getPublicKey' },
|
|
23
25
|
'getting public key for bot')
|
|
@@ -25,7 +27,9 @@ export class KeyStorage {
|
|
|
25
27
|
return publicKey
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
async getPrivateKey (username) {
|
|
30
|
+
async getPrivateKey (username = null) {
|
|
31
|
+
assert.ok(username === null || typeof username === 'string')
|
|
32
|
+
assert.ok(username !== '*')
|
|
29
33
|
this.#logger.debug(
|
|
30
34
|
{ username, method: 'KeyStorage.getPrivateKey' },
|
|
31
35
|
'getting private key for bot')
|
package/lib/ratelimiter.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { setTimeout as sleep } from 'node:timers/promises'
|
|
2
2
|
import assert from 'node:assert'
|
|
3
3
|
|
|
4
|
+
const BETA = 0.75
|
|
5
|
+
|
|
4
6
|
export class RateLimiter {
|
|
5
7
|
#connection
|
|
6
8
|
#logger
|
|
@@ -15,7 +17,7 @@ export class RateLimiter {
|
|
|
15
17
|
async limit (host, maxWaitTime = 30000) {
|
|
16
18
|
assert.strictEqual(typeof host, 'string')
|
|
17
19
|
assert.strictEqual(typeof maxWaitTime, 'number')
|
|
18
|
-
const waitTime = await this.#getWaitTime(host)
|
|
20
|
+
const waitTime = await this.#getWaitTime(host, maxWaitTime)
|
|
19
21
|
if (waitTime > maxWaitTime) {
|
|
20
22
|
throw new Error(`Wait time is too long; ${waitTime} > ${maxWaitTime}`)
|
|
21
23
|
}
|
|
@@ -49,8 +51,9 @@ export class RateLimiter {
|
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
async #getWaitTime (host) {
|
|
54
|
+
async #getWaitTime (host, maxWaitTime) {
|
|
53
55
|
assert.strictEqual(typeof host, 'string')
|
|
56
|
+
assert.ok(maxWaitTime > 0)
|
|
54
57
|
|
|
55
58
|
const [result] = await this.#connection.query(
|
|
56
59
|
'SELECT remaining, reset FROM rate_limit WHERE host = ?',
|
|
@@ -64,6 +67,7 @@ export class RateLimiter {
|
|
|
64
67
|
const { remaining, reset } = result[0]
|
|
65
68
|
const resetDate = new Date(reset)
|
|
66
69
|
const now = new Date()
|
|
70
|
+
const window = Math.round(resetDate - now)
|
|
67
71
|
|
|
68
72
|
if (now > resetDate) {
|
|
69
73
|
this.#logger.debug(
|
|
@@ -78,19 +82,20 @@ export class RateLimiter {
|
|
|
78
82
|
{ remaining, resetDate, host },
|
|
79
83
|
'no more requests remaining'
|
|
80
84
|
)
|
|
81
|
-
return
|
|
85
|
+
return window
|
|
82
86
|
}
|
|
83
87
|
|
|
84
88
|
this.#logger.debug(
|
|
85
89
|
{ remaining, resetDate, host },
|
|
86
|
-
'
|
|
90
|
+
'requests remain'
|
|
87
91
|
)
|
|
88
92
|
|
|
89
|
-
const
|
|
93
|
+
const space = (window / remaining)
|
|
94
|
+
const waitTime = (space) * Math.pow(space / maxWaitTime, BETA)
|
|
90
95
|
|
|
91
96
|
this.#logger.debug(
|
|
92
97
|
{ remaining, resetDate, host, waitTime },
|
|
93
|
-
'
|
|
98
|
+
'wait time calculated'
|
|
94
99
|
)
|
|
95
100
|
return waitTime
|
|
96
101
|
}
|