@evanp/activitypub-bot 0.14.2 → 0.15.4
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/activitydistributor.js +76 -30
- package/lib/routes/collection.js +9 -1
- package/package.json +2 -2
|
@@ -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
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
for (const prop of
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
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/lib/routes/collection.js
CHANGED
|
@@ -7,7 +7,15 @@ import assert from 'node:assert'
|
|
|
7
7
|
const collections = ['outbox', 'liked', 'followers', 'following']
|
|
8
8
|
const router = express.Router()
|
|
9
9
|
|
|
10
|
+
function toArray (value) {
|
|
11
|
+
return (Array.isArray(value))
|
|
12
|
+
? value
|
|
13
|
+
: [value]
|
|
14
|
+
}
|
|
15
|
+
|
|
10
16
|
async function filterAsync (array, asyncPredicate) {
|
|
17
|
+
assert.ok(array)
|
|
18
|
+
assert.ok(Array.isArray(array))
|
|
11
19
|
// 1. Kick off all predicate calls in parallel:
|
|
12
20
|
const checks = array.map(item => asyncPredicate(item))
|
|
13
21
|
|
|
@@ -76,7 +84,7 @@ function collectionPageHandler (collection) {
|
|
|
76
84
|
exported = await page.export({ useOriginalContext: true })
|
|
77
85
|
|
|
78
86
|
if (['outbox', 'liked'].includes(collection)) {
|
|
79
|
-
exported.items = await filterAsync(exported.items, async (id) => {
|
|
87
|
+
exported.items = await filterAsync(toArray(exported.items), async (id) => {
|
|
80
88
|
const object = (formatter.isLocal(id))
|
|
81
89
|
? await objectStorage.read(id)
|
|
82
90
|
: await client.get(id)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evanp/activitypub-bot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.4",
|
|
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.
|
|
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"
|