@evanp/activitypub-bot 0.29.0 → 0.30.2

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.
@@ -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: original.replies,
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: root.thread,
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
- await this.#doActivity(bot, {
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: this.#formatter.format({
308
- username: bot.username,
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
- await this.#doActivity(bot, {
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
- to: actor
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) {
@@ -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.#client.get(id, this.#botId)
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.#client.get(actorId)
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')
@@ -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 Math.round(resetDate - now)
85
+ return window
82
86
  }
83
87
 
84
88
  this.#logger.debug(
85
89
  { remaining, resetDate, host },
86
- 'no more requests remaining'
90
+ 'requests remain'
87
91
  )
88
92
 
89
- const waitTime = Math.round((resetDate - now) / (remaining * 1.0))
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
- 'no more requests remaining'
98
+ 'wait time calculated'
94
99
  )
95
100
  return waitTime
96
101
  }
@@ -15,6 +15,7 @@ router.get('/actor', async (req, res) => {
15
15
  const publicKeyPem = await keyStorage.getPublicKey(null)
16
16
  const acct = formatter.acct()
17
17
  const webfinger = acct.slice(5)
18
+ const [username] = webfinger.split('@', 1)
18
19
  const server = await as2.import({
19
20
  '@context': [
20
21
  'https://www.w3.org/ns/activitystreams',
@@ -24,6 +25,7 @@ router.get('/actor', async (req, res) => {
24
25
  id: formatter.format({ server: true }),
25
26
  type: 'Service',
26
27
  to: 'as:Public',
28
+ preferredUsername: username,
27
29
  alsoKnownAs: acct,
28
30
  webfinger,
29
31
  publicKey: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evanp/activitypub-bot",
3
- "version": "0.29.0",
3
+ "version": "0.30.2",
4
4
  "description": "server-side ActivityPub bot framework",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",