@evanp/activitypub-bot 0.14.2 → 0.15.3

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.
@@ -4,6 +4,13 @@ import { LRUCache } from 'lru-cache'
4
4
  import PQueue from 'p-queue'
5
5
  import { setTimeout } from 'node:timers/promises'
6
6
 
7
+ const NS = 'https://www.w3.org/ns/activitystreams#'
8
+
9
+ const COLLECTION_TYPES = [
10
+ `${NS}Collection`,
11
+ `${NS}OrderedCollection`
12
+ ]
13
+
7
14
  export class ActivityDistributor {
8
15
  static #MAX_CACHE_SIZE = 1000000
9
16
  static #CONCURRENCY = 32
@@ -88,45 +95,66 @@ export class ActivityDistributor {
88
95
  }
89
96
 
90
97
  async * #public (activity, username) {
91
- const followers = this.#formatter.format({
92
- username,
93
- collection: 'followers'
94
- })
95
- for (const prop of ['to', 'cc', 'audience']) {
96
- const p = activity.get(prop)
97
- if (p) {
98
- for (const value of p) {
99
- const id = value.id
100
- if (id === followers ||
101
- ActivityDistributor.#PUBLIC.includes(id)) {
102
- for await (const follower of this.#actorStorage.items(username, 'followers')) {
103
- yield follower.id
104
- }
105
- } else {
106
- yield id
107
- }
108
- }
109
- }
110
- }
98
+ yield* this.#recipients(activity, username, ['to', 'cc', 'audience'])
111
99
  }
112
100
 
113
101
  async * #private (activity, username) {
114
- const followers = this.#formatter.format({
115
- username,
116
- collection: 'followers'
117
- })
118
- for (const prop of ['bto', 'bcc']) {
102
+ yield* this.#recipients(activity, username, ['bto', 'bcc'])
103
+ }
104
+
105
+ async * #recipients (activity, username, props) {
106
+ for (const prop of props) {
119
107
  const p = activity.get(prop)
120
108
  if (p) {
121
109
  for (const value of p) {
122
110
  const id = value.id
123
- if (id === followers ||
124
- ActivityDistributor.#PUBLIC.includes(id)) {
125
- for await (const follower of this.#actorStorage.items(username, 'followers')) {
126
- yield follower.id
111
+ this.#logger.debug({id}, 'Checking recipient')
112
+ if (ActivityDistributor.#PUBLIC.includes(id)) {
113
+ this.#logger.debug(
114
+ { activity: activity.id },
115
+ 'Skipping public delivery'
116
+ )
117
+ } else if (this.#formatter.isLocal(id)) {
118
+ this.#logger.debug({id}, 'Unformatting local recipient')
119
+ const parts = this.#formatter.unformat(id)
120
+ this.#logger.debug(parts, 'Local recipient')
121
+ if (this.#isLocalActor(parts)) {
122
+ this.#logger.debug({id}, 'Local actor')
123
+ yield id
124
+ } else if (this.#isLocalCollection(parts)) {
125
+ this.#logger.debug({id}, 'Local collection')
126
+ for await (const item of this.#actorStorage.items(parts.username, parts.collection)) {
127
+ this.#logger.debug({id: item.id}, 'Local collection member')
128
+ yield item.id
129
+ }
130
+ } else {
131
+ this.#logger.warn({id}, 'Local non-actor non-collection')
127
132
  }
128
133
  } else {
129
- yield id
134
+ let obj
135
+ try {
136
+ obj = await this.#client.get(id, username)
137
+ } catch (err) {
138
+ this.#logger.warn({id, err}, 'Cannot get recipient, skipping')
139
+ continue
140
+ }
141
+ if (this.#isRemoteActor(obj)) {
142
+ this.#logger.debug({id}, 'Remote actor')
143
+ yield id
144
+ } else if (this.#isRemoteCollection(obj)) {
145
+ this.#logger.debug({id}, 'Remote collection')
146
+ try {
147
+ for await (const item of this.#client.items(obj.id, username)) {
148
+ this.#logger.debug({id: item.id}, 'Remote collection member')
149
+ yield item.id
150
+ }
151
+ } catch (err) {
152
+ this.#logger.warn({id, err}, 'Cannot iterate, skipping')
153
+ continue
154
+ }
155
+ } else {
156
+ this.#logger.warn({id}, 'Remote non-actor non-collection')
157
+ }
130
158
  }
131
159
  }
132
160
  }
@@ -260,4 +288,22 @@ export class ActivityDistributor {
260
288
  await this.#actorStorage.addToCollection(username, 'inbox', activity)
261
289
  }
262
290
  }
291
+
292
+ #isLocalActor (parts) {
293
+ return parts.username && !parts.type && !parts.collection
294
+ }
295
+
296
+ #isLocalCollection (parts) {
297
+ return parts.username && !parts.type && parts.collection && !parts.page
298
+ }
299
+
300
+ #isRemoteActor (obj) {
301
+ return !!obj.inbox
302
+ }
303
+
304
+ #isRemoteCollection (obj) {
305
+ return (Array.isArray(obj.type))
306
+ ? obj.type.some(t => COLLECTION_TYPES.includes(t))
307
+ : COLLECTION_TYPES.includes(obj.type)
308
+ }
263
309
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evanp/activitypub-bot",
3
- "version": "0.14.2",
3
+ "version": "0.15.3",
4
4
  "description": "server-side ActivityPub bot framework",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -42,7 +42,7 @@
42
42
  "sequelize": "^6.37.7"
43
43
  },
44
44
  "devDependencies": {
45
- "@evanp/activitypub-nock": "^0.2.1",
45
+ "@evanp/activitypub-nock": "^0.4.4",
46
46
  "nock": "^14.0.5",
47
47
  "standard": "^17.1.2",
48
48
  "supertest": "^7.1.4"