@evanp/activitypub-bot 0.16.7 → 0.18.0
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/.eslintrc +7 -0
- package/README.md +17 -0
- package/lib/activitydeliverer.js +2 -6
- package/lib/activitydistributor.js +14 -14
- package/lib/activityhandler.js +24 -2
- package/lib/actorstorage.js +2 -1
- package/lib/app.js +2 -2
- package/lib/authorizer.js +1 -1
- package/lib/bot.js +8 -0
- package/lib/botcontext.js +8 -2
- package/lib/bots/ok.js +1 -1
- package/lib/bots/relayclient.js +38 -0
- package/lib/bots/relayserver.js +61 -0
- package/lib/httpsignature.js +6 -2
- package/lib/routes/collection.js +1 -1
- package/lib/routes/inbox.js +4 -4
- package/lib/routes/sharedinbox.js +15 -2
- package/lib/urlformatter.js +2 -2
- package/package.json +10 -5
package/.eslintrc
ADDED
package/README.md
CHANGED
|
@@ -198,6 +198,18 @@ Called when the server receives a public activity to its shared inbox. This can
|
|
|
198
198
|
|
|
199
199
|
Called when one of the bot's objects is shared by another actor. The first argument is the object, and the second is the `Announce` activity itself. This method is called after the activity has been added to the `shares` collection.
|
|
200
200
|
|
|
201
|
+
#### async actorOK (actor, activity)
|
|
202
|
+
|
|
203
|
+
Lets the bot override the default check for matching the actor who sent an activity with the
|
|
204
|
+
actor who did the activity. Usually, these need to be the same, but for some sub-protocols, like
|
|
205
|
+
relays, it can be different. Returns a boolean saying whether the actor is OK.
|
|
206
|
+
|
|
207
|
+
#### async handleActivity (activity)
|
|
208
|
+
|
|
209
|
+
Runs *before* default handling for an activity. This is a chance to do non-standard handling
|
|
210
|
+
for activities. Boolean return; truthy return means that the bot has already handled the
|
|
211
|
+
activity and standard handling should be skipped. The activity will still be delivered to inboxes. Use with caution!
|
|
212
|
+
|
|
201
213
|
### BotFactory
|
|
202
214
|
|
|
203
215
|
The BotFactory interface lets you have a lot of bots that act in a similar way without declaring them explicitly in the bots config file.
|
|
@@ -297,6 +309,11 @@ Sends a `Block` activity for the passed-in actor in [activitystrea.ms](#activity
|
|
|
297
309
|
|
|
298
310
|
Sends an `Undo`/`Block` activity for the passed-in actor in [activitystrea.ms](#activitystreams) form.
|
|
299
311
|
|
|
312
|
+
#### async doActivity (data, distribute = true)
|
|
313
|
+
|
|
314
|
+
Sends an arbitrary activity object out into the network. This requires some
|
|
315
|
+
care! `id`, `actor`, and timestamps will be added automatically. `distribute` flag is for sensitive activities that shouldn't be distributed to anyone.
|
|
316
|
+
|
|
300
317
|
#### async toActorId (webfinger)
|
|
301
318
|
|
|
302
319
|
Gets the `id` of the [ActivityPub Actor](https://www.w3.org/TR/activitypub/#actors) with the given [WebFinger](https://en.wikipedia.org/wiki/WebFinger) identity.
|
package/lib/activitydeliverer.js
CHANGED
|
@@ -38,10 +38,6 @@ export class ActivityDeliverer {
|
|
|
38
38
|
this.#deliverBotQueue = new PQueue({ max: ActivityDeliverer.#MAX_BOT_QUEUE })
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
isActivity (object) {
|
|
42
|
-
return true
|
|
43
|
-
}
|
|
44
|
-
|
|
45
41
|
getActor (activity) {
|
|
46
42
|
return activity.actor?.first
|
|
47
43
|
}
|
|
@@ -64,10 +60,10 @@ export class ActivityDeliverer {
|
|
|
64
60
|
}
|
|
65
61
|
|
|
66
62
|
async onIdle () {
|
|
67
|
-
this.#logger.debug(
|
|
63
|
+
this.#logger.debug('Awaiting delivery queues')
|
|
68
64
|
await this.#deliverAllQueue.onIdle()
|
|
69
65
|
await this.#deliverBotQueue.onIdle()
|
|
70
|
-
this.#logger.debug(
|
|
66
|
+
this.#logger.debug('Done awaiting delivery queues')
|
|
71
67
|
}
|
|
72
68
|
|
|
73
69
|
async #deliverTo (activity, bot) {
|
|
@@ -95,11 +95,11 @@ export class ActivityDistributor {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
async * #public (activity, username) {
|
|
98
|
-
yield* this.#recipients(activity, username, ['to', 'cc', 'audience'])
|
|
98
|
+
yield * this.#recipients(activity, username, ['to', 'cc', 'audience'])
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
async * #private (activity, username) {
|
|
102
|
-
yield* this.#recipients(activity, username, ['bto', 'bcc'])
|
|
102
|
+
yield * this.#recipients(activity, username, ['bto', 'bcc'])
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
async * #recipients (activity, username, props) {
|
|
@@ -108,52 +108,52 @@ export class ActivityDistributor {
|
|
|
108
108
|
if (p) {
|
|
109
109
|
for (const value of p) {
|
|
110
110
|
const id = value.id
|
|
111
|
-
this.#logger.debug({id}, 'Checking recipient')
|
|
111
|
+
this.#logger.debug({ id }, 'Checking recipient')
|
|
112
112
|
if (ActivityDistributor.#PUBLIC.includes(id)) {
|
|
113
113
|
this.#logger.debug(
|
|
114
114
|
{ activity: activity.id },
|
|
115
115
|
'Skipping public delivery'
|
|
116
116
|
)
|
|
117
117
|
} else if (this.#formatter.isLocal(id)) {
|
|
118
|
-
this.#logger.debug({id}, 'Unformatting local recipient')
|
|
118
|
+
this.#logger.debug({ id }, 'Unformatting local recipient')
|
|
119
119
|
const parts = this.#formatter.unformat(id)
|
|
120
120
|
this.#logger.debug(parts, 'Local recipient')
|
|
121
121
|
if (this.#isLocalActor(parts)) {
|
|
122
|
-
this.#logger.debug({id}, 'Local actor')
|
|
122
|
+
this.#logger.debug({ id }, 'Local actor')
|
|
123
123
|
yield id
|
|
124
124
|
} else if (this.#isLocalCollection(parts)) {
|
|
125
|
-
this.#logger.debug({id}, 'Local collection')
|
|
125
|
+
this.#logger.debug({ id }, 'Local collection')
|
|
126
126
|
for await (const item of this.#actorStorage.items(parts.username, parts.collection)) {
|
|
127
|
-
this.#logger.debug({id: item.id}, 'Local collection member')
|
|
127
|
+
this.#logger.debug({ id: item.id }, 'Local collection member')
|
|
128
128
|
yield item.id
|
|
129
129
|
}
|
|
130
130
|
} else {
|
|
131
|
-
this.#logger.warn({id}, 'Local non-actor non-collection')
|
|
131
|
+
this.#logger.warn({ id }, 'Local non-actor non-collection')
|
|
132
132
|
}
|
|
133
133
|
} else {
|
|
134
134
|
let obj
|
|
135
135
|
try {
|
|
136
136
|
obj = await this.#client.get(id, username)
|
|
137
137
|
} catch (err) {
|
|
138
|
-
this.#logger.warn({id, err}, 'Cannot get recipient, skipping')
|
|
138
|
+
this.#logger.warn({ id, err }, 'Cannot get recipient, skipping')
|
|
139
139
|
continue
|
|
140
140
|
}
|
|
141
141
|
if (this.#isRemoteActor(obj)) {
|
|
142
|
-
this.#logger.debug({id}, 'Remote actor')
|
|
142
|
+
this.#logger.debug({ id }, 'Remote actor')
|
|
143
143
|
yield id
|
|
144
144
|
} else if (this.#isRemoteCollection(obj)) {
|
|
145
|
-
this.#logger.debug({id}, 'Remote collection')
|
|
145
|
+
this.#logger.debug({ id }, 'Remote collection')
|
|
146
146
|
try {
|
|
147
147
|
for await (const item of this.#client.items(obj.id, username)) {
|
|
148
|
-
this.#logger.debug({id: item.id}, 'Remote collection member')
|
|
148
|
+
this.#logger.debug({ id: item.id }, 'Remote collection member')
|
|
149
149
|
yield item.id
|
|
150
150
|
}
|
|
151
151
|
} catch (err) {
|
|
152
|
-
this.#logger.warn({id, err}, 'Cannot iterate, skipping')
|
|
152
|
+
this.#logger.warn({ id, err }, 'Cannot iterate, skipping')
|
|
153
153
|
continue
|
|
154
154
|
}
|
|
155
155
|
} else {
|
|
156
|
-
this.#logger.warn({id}, 'Remote non-actor non-collection')
|
|
156
|
+
this.#logger.warn({ id }, 'Remote non-actor non-collection')
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
}
|
package/lib/activityhandler.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import as2 from './activitystreams.js'
|
|
2
2
|
import { nanoid } from 'nanoid'
|
|
3
|
+
import assert from 'node:assert'
|
|
3
4
|
|
|
4
5
|
const AS2 = 'https://www.w3.org/ns/activitystreams#'
|
|
5
6
|
|
|
@@ -35,6 +36,27 @@ export class ActivityHandler {
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
async handleActivity (bot, activity) {
|
|
39
|
+
assert.ok(bot, 'bot parameter is required')
|
|
40
|
+
assert.strictEqual(typeof bot, 'object', 'bot parameter must be an object')
|
|
41
|
+
assert.ok(activity, 'activity parameter is required')
|
|
42
|
+
assert.strictEqual(
|
|
43
|
+
typeof activity,
|
|
44
|
+
'object',
|
|
45
|
+
'activity parameter must be an object'
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
let handled = false
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
handled = await bot.handleActivity(activity)
|
|
52
|
+
} catch (err) {
|
|
53
|
+
this.#logger.warn({ err }, `Bot ${bot.username} errored on activity`)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (handled) {
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
|
|
38
60
|
switch (activity.type) {
|
|
39
61
|
case AS2 + 'Create': await this.#handleCreate(bot, activity); break
|
|
40
62
|
case AS2 + 'Update': await this.#handleUpdate(bot, activity); break
|
|
@@ -80,7 +102,7 @@ export class ActivityHandler {
|
|
|
80
102
|
await this.#handleCreateThread(bot, activity, actor, object)
|
|
81
103
|
if (this.#isMention(bot, object)) {
|
|
82
104
|
this.#logger.debug(
|
|
83
|
-
|
|
105
|
+
{ username: bot.username, object: object.id },
|
|
84
106
|
'bot mentioned'
|
|
85
107
|
)
|
|
86
108
|
await bot.onMention(object, activity)
|
|
@@ -875,7 +897,7 @@ export class ActivityHandler {
|
|
|
875
897
|
if (!type || typeof type !== 'string') {
|
|
876
898
|
return 'activity'
|
|
877
899
|
}
|
|
878
|
-
const parts = type.split(/[
|
|
900
|
+
const parts = type.split(/[/#]/)
|
|
879
901
|
return parts[parts.length - 1].toLowerCase()
|
|
880
902
|
}
|
|
881
903
|
|
package/lib/actorstorage.js
CHANGED
package/lib/app.js
CHANGED
|
@@ -188,10 +188,10 @@ export async function makeApp (databaseUrl, origin, bots, logLevel = 'silent') {
|
|
|
188
188
|
})
|
|
189
189
|
|
|
190
190
|
app.onIdle = async () => {
|
|
191
|
-
logger.debug(
|
|
191
|
+
logger.debug('Awaiting components')
|
|
192
192
|
await distributor.onIdle()
|
|
193
193
|
await deliverer.onIdle()
|
|
194
|
-
logger.debug(
|
|
194
|
+
logger.debug('Done awaiting components')
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
app.cleanup = async () => {
|
package/lib/authorizer.js
CHANGED
|
@@ -101,7 +101,7 @@ export class Authorizer {
|
|
|
101
101
|
} else if (this.#formatter.isLocal(object.id)) {
|
|
102
102
|
const parts = this.#formatter.unformat(object.id)
|
|
103
103
|
return as2.import({
|
|
104
|
-
id: this.#formatter.format({username: parts.username})
|
|
104
|
+
id: this.#formatter.format({ username: parts.username })
|
|
105
105
|
})
|
|
106
106
|
} else {
|
|
107
107
|
return null
|
package/lib/bot.js
CHANGED
package/lib/botcontext.js
CHANGED
|
@@ -143,7 +143,7 @@ export class BotContext {
|
|
|
143
143
|
attributedTo: this.#formatter.format({ username: this.#botId })
|
|
144
144
|
})
|
|
145
145
|
await this.#objectStorage.create(note)
|
|
146
|
-
|
|
146
|
+
await this.#doActivity({
|
|
147
147
|
'@context': [
|
|
148
148
|
'https://www.w3.org/ns/activitystreams',
|
|
149
149
|
'https://purl.archive.org/socialweb/thread/1.0',
|
|
@@ -394,6 +394,12 @@ export class BotContext {
|
|
|
394
394
|
return await this.#undoActivity('Announce', obj)
|
|
395
395
|
}
|
|
396
396
|
|
|
397
|
+
async doActivity (data, distribute = true) {
|
|
398
|
+
assert.ok(data)
|
|
399
|
+
assert.equal(typeof data, 'object')
|
|
400
|
+
return await this.#doActivity(data, distribute)
|
|
401
|
+
}
|
|
402
|
+
|
|
397
403
|
async #findInOutbox (type, obj) {
|
|
398
404
|
const full = `https://www.w3.org/ns/activitystreams#${type}`
|
|
399
405
|
let found = null
|
|
@@ -442,7 +448,7 @@ export class BotContext {
|
|
|
442
448
|
type,
|
|
443
449
|
id: this.#formatter.format({
|
|
444
450
|
username: this.#botId,
|
|
445
|
-
type
|
|
451
|
+
type,
|
|
446
452
|
nanoid: nanoid()
|
|
447
453
|
}),
|
|
448
454
|
actor: {
|
package/lib/bots/ok.js
CHANGED
|
@@ -23,7 +23,7 @@ export default class OKBot extends Bot {
|
|
|
23
23
|
object.attributedTo?.first.id ||
|
|
24
24
|
activity.actor?.first.id
|
|
25
25
|
this._context.logger.debug(
|
|
26
|
-
{ object: object.id, attributedTo
|
|
26
|
+
{ object: object.id, attributedTo },
|
|
27
27
|
'attributed to'
|
|
28
28
|
)
|
|
29
29
|
const wf = await this._context.toWebfinger(attributedTo)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import Bot from '../bot.js'
|
|
2
|
+
|
|
3
|
+
export default class RelayClientBot extends Bot {
|
|
4
|
+
#relay
|
|
5
|
+
|
|
6
|
+
constructor (username, relay) {
|
|
7
|
+
super(username)
|
|
8
|
+
this.#relay = relay
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
get fullname () {
|
|
12
|
+
return 'Relay Client Bot'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
get description () {
|
|
16
|
+
return 'A bot for subscribing to relays'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async initialize (context) {
|
|
20
|
+
super.initialize(context)
|
|
21
|
+
if (!(await this._context.hasData(`follow:${this.#relay}`))) {
|
|
22
|
+
await this.#followRelay()
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async #followRelay () {
|
|
27
|
+
const activity = await this._context.doActivity({
|
|
28
|
+
to: this.#relay,
|
|
29
|
+
type: 'Follow',
|
|
30
|
+
object: 'https://www.w3.org/ns/activitystreams#Public'
|
|
31
|
+
})
|
|
32
|
+
this._context.setData(`follow:${this.#relay}`, activity.id)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async actorOK (actorId, activity) {
|
|
36
|
+
return (actorId === this.#relay)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import assert from 'node:assert'
|
|
2
|
+
|
|
3
|
+
import Bot from '../bot.js'
|
|
4
|
+
|
|
5
|
+
const NS = 'https://www.w3.org/ns/activitystreams#'
|
|
6
|
+
const FOLLOW = `${NS}Follow`
|
|
7
|
+
const PUBLICS = [
|
|
8
|
+
'Public',
|
|
9
|
+
'as:Public',
|
|
10
|
+
`${NS}Public`
|
|
11
|
+
]
|
|
12
|
+
const CLIENTS = 'relay-clients'
|
|
13
|
+
|
|
14
|
+
export default class RelayServerBot extends Bot {
|
|
15
|
+
get fullname () {
|
|
16
|
+
return 'Relay Server Bot'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get description () {
|
|
20
|
+
return 'A bot for subscribing to relays'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async handleActivity (activity) {
|
|
24
|
+
if (activity.type === FOLLOW &&
|
|
25
|
+
PUBLICS.includes(activity.object?.first?.id)) {
|
|
26
|
+
this._context.logger.debug(
|
|
27
|
+
{ activity: activity.id },
|
|
28
|
+
'Non-default handling for relay follow'
|
|
29
|
+
)
|
|
30
|
+
const actorId = activity.actor?.first?.id
|
|
31
|
+
if (actorId) {
|
|
32
|
+
this._context.logger.info(
|
|
33
|
+
{ actor: actorId },
|
|
34
|
+
'Adding actor as relay follower'
|
|
35
|
+
)
|
|
36
|
+
// FIXME: will this work at scale?
|
|
37
|
+
const clients = (await this._context.hasData(CLIENTS))
|
|
38
|
+
? await this._context.getData(CLIENTS)
|
|
39
|
+
: []
|
|
40
|
+
assert.ok(Array.isArray(clients))
|
|
41
|
+
const clientSet = new Set(clients)
|
|
42
|
+
if (!clientSet.has(actorId)) {
|
|
43
|
+
clientSet.add(actorId)
|
|
44
|
+
await this._context.setData(CLIENTS, Array.from(clientSet))
|
|
45
|
+
}
|
|
46
|
+
this._context.logger.debug(
|
|
47
|
+
{ activity: activity.id },
|
|
48
|
+
'Accepting follow activity'
|
|
49
|
+
)
|
|
50
|
+
await this._context.doActivity({
|
|
51
|
+
type: 'Accept',
|
|
52
|
+
object: activity.id,
|
|
53
|
+
to: actorId
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
return true
|
|
57
|
+
} else {
|
|
58
|
+
return false
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
package/lib/httpsignature.js
CHANGED
|
@@ -43,8 +43,8 @@ export class HTTPSignature {
|
|
|
43
43
|
throw createHttpError(401, 'No algorithm provided')
|
|
44
44
|
}
|
|
45
45
|
this.#logger.debug({ algorithm }, 'validating signature')
|
|
46
|
-
if (algorithm
|
|
47
|
-
throw createHttpError(401, 'Only rsa-sha256
|
|
46
|
+
if (!(algorithm === 'rsa-sha256' || (algorithm === 'hs2019' && this.#isRSAKey(publicKeyPem)))) {
|
|
47
|
+
throw createHttpError(401, 'Only rsa-sha256 or hs2019 with RSA supported')
|
|
48
48
|
}
|
|
49
49
|
if (!params.headers) {
|
|
50
50
|
throw createHttpError(401, 'No headers provided')
|
|
@@ -192,4 +192,8 @@ export class HTTPSignature {
|
|
|
192
192
|
verifier.end()
|
|
193
193
|
return isValid
|
|
194
194
|
}
|
|
195
|
+
|
|
196
|
+
#isRSAKey (publicKeyPem) {
|
|
197
|
+
return crypto.createPublicKey(publicKeyPem).asymmetricKeyType === 'rsa'
|
|
198
|
+
}
|
|
195
199
|
}
|
package/lib/routes/collection.js
CHANGED
package/lib/routes/inbox.js
CHANGED
|
@@ -35,7 +35,7 @@ router.post('/user/:username/inbox', async (req, res, next) => {
|
|
|
35
35
|
return next(createHttpError(400, 'Invalid request body'))
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
if (!
|
|
38
|
+
if (!activity.isActivity()) {
|
|
39
39
|
return next(createHttpError(400, 'Request body is not an activity'))
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -49,7 +49,7 @@ router.post('/user/:username/inbox', async (req, res, next) => {
|
|
|
49
49
|
return next(createHttpError(400, 'No actor id found in activity'))
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
if (actor.id !== subject) {
|
|
52
|
+
if (actor.id !== subject && !(await bot.actorOK(subject, activity))) {
|
|
53
53
|
return next(createHttpError(403, `${subject} is not the actor ${actor.id}`))
|
|
54
54
|
}
|
|
55
55
|
|
|
@@ -71,11 +71,11 @@ router.post('/user/:username/inbox', async (req, res, next) => {
|
|
|
71
71
|
})
|
|
72
72
|
|
|
73
73
|
router.get('/user/:username/inbox', async (req, res, next) => {
|
|
74
|
-
return next(createHttpError(403,
|
|
74
|
+
return next(createHttpError(403, 'No access to inbox collection'))
|
|
75
75
|
})
|
|
76
76
|
|
|
77
77
|
router.get('/user/:username/inbox/:n', async (req, res, next) => {
|
|
78
|
-
return next(createHttpError(403,
|
|
78
|
+
return next(createHttpError(403, 'No access to inbox collection'))
|
|
79
79
|
})
|
|
80
80
|
|
|
81
81
|
export default router
|
|
@@ -5,6 +5,19 @@ import http from 'node:http'
|
|
|
5
5
|
|
|
6
6
|
const router = express.Router()
|
|
7
7
|
|
|
8
|
+
async function asyncSome (array, asyncPredicate) {
|
|
9
|
+
for (let i = 0; i < array.length; i++) {
|
|
10
|
+
if (await asyncPredicate(array[i], i, array)) {
|
|
11
|
+
return true
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return false
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function actorOK (subject, activity, bots) {
|
|
18
|
+
await asyncSome(Object.values(bots), bot => bot.actorOK(subject, activity))
|
|
19
|
+
}
|
|
20
|
+
|
|
8
21
|
router.post('/shared/inbox', async (req, res, next) => {
|
|
9
22
|
const { bots, deliverer, logger } = req.app.locals
|
|
10
23
|
const { subject } = req.auth
|
|
@@ -27,7 +40,7 @@ router.post('/shared/inbox', async (req, res, next) => {
|
|
|
27
40
|
return next(createHttpError(400, 'Invalid request body'))
|
|
28
41
|
}
|
|
29
42
|
|
|
30
|
-
if (!
|
|
43
|
+
if (!activity.isActivity()) {
|
|
31
44
|
return next(createHttpError(400, 'Request body is not an activity'))
|
|
32
45
|
}
|
|
33
46
|
|
|
@@ -41,7 +54,7 @@ router.post('/shared/inbox', async (req, res, next) => {
|
|
|
41
54
|
return next(createHttpError(400, 'No actor id found in activity'))
|
|
42
55
|
}
|
|
43
56
|
|
|
44
|
-
if (actor.id !== subject) {
|
|
57
|
+
if (actor.id !== subject && !(await actorOK(subject, activity, bots))) {
|
|
45
58
|
return next(createHttpError(403, `${subject} is not the actor ${actor.id}`))
|
|
46
59
|
}
|
|
47
60
|
|
package/lib/urlformatter.js
CHANGED
|
@@ -20,9 +20,9 @@ export class UrlFormatter {
|
|
|
20
20
|
let major = null
|
|
21
21
|
if (type) {
|
|
22
22
|
if (nanoid) {
|
|
23
|
-
major = `${base}/${type}/${nanoid}`
|
|
23
|
+
major = `${base}/${type.toLowerCase()}/${nanoid}`
|
|
24
24
|
} else if (type === 'publickey') {
|
|
25
|
-
major = `${base}/${type}`
|
|
25
|
+
major = `${base}/${type.toLowerCase()}`
|
|
26
26
|
} else {
|
|
27
27
|
throw new Error('Cannot format URL without nanoid')
|
|
28
28
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evanp/activitypub-bot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "server-side ActivityPub bot framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"test": "NODE_ENV=test node --test",
|
|
12
|
-
"start": "npx activitypub-bot"
|
|
12
|
+
"start": "npx activitypub-bot",
|
|
13
|
+
"lint": "eslint ."
|
|
13
14
|
},
|
|
14
15
|
"repository": {
|
|
15
16
|
"type": "git",
|
|
@@ -29,7 +30,7 @@
|
|
|
29
30
|
"homepage": "https://github.com/evanp/activitypub-bot#readme",
|
|
30
31
|
"dependencies": {
|
|
31
32
|
"@isaacs/ttlcache": "^2.1.4",
|
|
32
|
-
"activitystrea.ms": "3.
|
|
33
|
+
"activitystrea.ms": "^3.3.0",
|
|
33
34
|
"express": "^5.2.1",
|
|
34
35
|
"http-errors": "^2.0.0",
|
|
35
36
|
"humanhash": "^1.0.4",
|
|
@@ -42,9 +43,13 @@
|
|
|
42
43
|
"sequelize": "^6.37.7"
|
|
43
44
|
},
|
|
44
45
|
"devDependencies": {
|
|
45
|
-
"@evanp/activitypub-nock": "^0.
|
|
46
|
+
"@evanp/activitypub-nock": "^0.5.0",
|
|
47
|
+
"eslint": "^8.57.1",
|
|
48
|
+
"eslint-config-standard": "^17.1.0",
|
|
49
|
+
"eslint-plugin-import": "^2.29.1",
|
|
50
|
+
"eslint-plugin-n": "^16.6.2",
|
|
51
|
+
"eslint-plugin-promise": "^6.6.0",
|
|
46
52
|
"nock": "^14.0.5",
|
|
47
|
-
"standard": "^17.1.2",
|
|
48
53
|
"supertest": "^7.1.4"
|
|
49
54
|
},
|
|
50
55
|
"optionalDependencies": {
|