@evanp/activitypub-bot 0.33.0 → 0.34.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.
@@ -160,6 +160,20 @@ export class ActivityDeliverer {
160
160
  }
161
161
  }
162
162
  }
163
+
164
+ switch (activity.type) {
165
+ case `${NS}Follow`:
166
+ await this.#deliverFollowToAll(activity, bots, deliveredTo)
167
+ break
168
+ }
169
+ }
170
+
171
+ async #deliverFollowToAll (activity, bots, deliveredTo) {
172
+ const object = activity.object?.first
173
+ if (object?.id && this.#isLocal(object) && !deliveredTo.has(this.#formatter.unformat(object.id).username)) {
174
+ this.#logger.debug({ object: object.id, activity: activity.id }, 'Follow not yet delivered to object, delivering now')
175
+ await this.#deliverLocalActor(activity, object, bots, deliveredTo)
176
+ }
163
177
  }
164
178
 
165
179
  async #isRemoteFollowersCollection (actor, object) {
@@ -634,16 +634,15 @@ export class ActivityHandler {
634
634
  'following',
635
635
  actor
636
636
  )
637
- await this.#actorStorage.removeFromCollection(
638
- bot.username,
639
- 'pendingFollowing',
640
- actor
641
- )
642
- await this.#actorStorage.removeFromCollection(
643
- bot.username,
644
- 'pendingFollowers',
645
- actor
646
- )
637
+ const followId = await this.#actorStorage.getLastActivity(bot.username, 'Follow', actor)
638
+ if (followId) {
639
+ const followActivity = await this.#objectStorage.read(followId)
640
+ await this.#actorStorage.removeFromCollection(
641
+ bot.username,
642
+ 'pendingFollowing',
643
+ followActivity
644
+ )
645
+ }
647
646
  }
648
647
  }
649
648
 
@@ -829,11 +828,9 @@ export class ActivityHandler {
829
828
  })
830
829
  return
831
830
  }
832
- if (
833
- !(await this.#actorStorage.isInCollection(bot.username, 'followers', actor)) &&
834
- !(await this.#actorStorage.isInCollection(bot.username, 'pendingFollowers', actor))) {
831
+ if (!(await this.#actorStorage.isInCollection(bot.username, 'followers', actor))) {
835
832
  this.#logger.warn({
836
- msg: 'Undo follow activity from actor not in followers or pendingFollowers',
833
+ msg: 'Undo follow activity from actor not in followers',
837
834
  activity: undoActivity.id,
838
835
  followActivity: followActivity.id,
839
836
  actor: actor.id
@@ -841,7 +838,6 @@ export class ActivityHandler {
841
838
  return
842
839
  }
843
840
  await this.#actorStorage.removeFromCollection(bot.username, 'followers', actor)
844
- await this.#actorStorage.removeFromCollection(bot.username, 'pendingFollowers', actor)
845
841
  this.#logger.debug(
846
842
  { bot: this.#botId(bot), activity: undoActivity.id },
847
843
  'Notifying bot of undo follow activity'
package/lib/botcontext.js CHANGED
@@ -227,8 +227,7 @@ export class BotContext {
227
227
  assert.ok(actor)
228
228
  assert.equal(typeof actor, 'object')
229
229
  let activity
230
- if (await this.#actorStorage.isInCollection(this.#botId, 'following', actor) ||
231
- await this.#actorStorage.isInCollection(this.#botId, 'pendingFollowing', actor)) {
230
+ if (await this.#actorStorage.isInCollection(this.#botId, 'following', actor)) {
232
231
  const originalId = await this.#actorStorage.getLastActivity(
233
232
  this.#botId,
234
233
  'Follow',
@@ -239,17 +238,24 @@ export class BotContext {
239
238
  }
240
239
  activity = await this.#objectStorage.read(originalId)
241
240
  } else {
242
- await this.#actorStorage.addToCollection(
243
- this.#botId,
244
- 'pendingFollowing',
245
- actor
246
- )
241
+ const pendingId = await this.#actorStorage.getLastActivity(this.#botId, 'Follow', actor)
242
+ if (pendingId) {
243
+ const pendingActivity = await this.#objectStorage.read(pendingId)
244
+ if (await this.#actorStorage.isInCollection(this.#botId, 'pendingFollowing', pendingActivity)) {
245
+ return pendingActivity
246
+ }
247
+ }
247
248
  activity = await this.#doActivity({
248
249
  type: 'Follow',
249
250
  object: actor.id,
250
251
  to: actor.id
251
252
  })
252
253
  await this.#actorStorage.setLastActivity(this.#botId, activity)
254
+ await this.#actorStorage.addToCollection(
255
+ this.#botId,
256
+ 'pendingFollowing',
257
+ activity
258
+ )
253
259
  }
254
260
 
255
261
  return activity
@@ -258,11 +264,15 @@ export class BotContext {
258
264
  async unfollowActor (actor) {
259
265
  assert.ok(actor)
260
266
  assert.equal(typeof actor, 'object')
261
- await this.#actorStorage.removeFromCollection(
262
- this.#botId,
263
- 'pendingFollowing',
264
- actor
265
- )
267
+ const followId = await this.#actorStorage.getLastActivity(this.#botId, 'Follow', actor)
268
+ if (followId) {
269
+ const followActivity = await this.#objectStorage.read(followId)
270
+ await this.#actorStorage.removeFromCollection(
271
+ this.#botId,
272
+ 'pendingFollowing',
273
+ followActivity
274
+ )
275
+ }
266
276
  await this.#actorStorage.removeFromCollection(
267
277
  this.#botId,
268
278
  'following',
@@ -275,11 +285,14 @@ export class BotContext {
275
285
  assert.ok(actor)
276
286
  assert.equal(typeof actor, 'object')
277
287
  await this.#actorStorage.addToCollection(this.#botId, 'blocked', actor)
288
+ const followId = await this.#actorStorage.getLastActivity(this.#botId, 'Follow', actor)
289
+ if (followId) {
290
+ const followActivity = await this.#objectStorage.read(followId)
291
+ await this.#actorStorage.removeFromCollection(this.#botId, 'pendingFollowing', followActivity)
292
+ }
278
293
  for (const coll of [
279
294
  'following',
280
- 'followers',
281
- 'pendingFollowing',
282
- 'pendingFollowers'
295
+ 'followers'
283
296
  ]) {
284
297
  await this.#actorStorage.removeFromCollection(this.#botId, coll, actor)
285
298
  }
@@ -55,7 +55,38 @@ export class RateLimiter {
55
55
  updated_at = CURRENT_TIMESTAMP`,
56
56
  { replacements: [host, remaining, reset] }
57
57
  )
58
+ } else if (headers.get('server') === 'Mastodon') {
59
+ const limits = await this.peek(host)
60
+ if (!limits || limits.length === 0 || limits[0].reset < (new Date())) {
61
+ const remaining = 299 // 300 - 1 for the current request
62
+ const reset = new Date(Math.ceil(Date.now() / 300000) * 300000)
63
+ this.#logger.debug({ reset, remaining, host }, 'updating')
64
+ await this.#connection.query(
65
+ `INSERT INTO rate_limit (host, remaining, reset)
66
+ VALUES (?, ?, ?)
67
+ ON CONFLICT (host) DO UPDATE
68
+ SET remaining = EXCLUDED.remaining,
69
+ reset = EXCLUDED.reset,
70
+ updated_at = CURRENT_TIMESTAMP`,
71
+ { replacements: [host, remaining, reset] }
72
+ )
73
+ }
74
+ }
75
+ }
76
+
77
+ async peek (host) {
78
+ const [result] = await this.#connection.query(
79
+ 'SELECT remaining, reset FROM rate_limit WHERE host = ?',
80
+ { replacements: [host] }
81
+ )
82
+
83
+ if (result.length === 0) {
84
+ return []
58
85
  }
86
+
87
+ const { remaining, reset } = result[0]
88
+
89
+ return [{ policy: 'default', remaining, reset: new Date(reset) }]
59
90
  }
60
91
 
61
92
  async #getWaitTime (host, maxWaitTime) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evanp/activitypub-bot",
3
- "version": "0.33.0",
3
+ "version": "0.34.1",
4
4
  "description": "server-side ActivityPub bot framework",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",