@evanp/activitypub-bot 0.51.4 → 0.52.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.
- package/CHANGELOG.md +28 -1
- package/lib/app.js +2 -0
- package/lib/botcontext.js +5 -32
- package/lib/microsyntax.js +5 -21
- package/lib/routes/static.js +12 -0
- package/package.json +3 -1
- package/web/profile.html +1 -2
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,31 @@ and this project adheres to
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [0.52.1] - 2026-10-05
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- Updated transitive dependencies: brace-expansion and http-cache-semantics.
|
|
17
|
+
|
|
18
|
+
## [0.52.0] - 2026-10-05
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- `GET /js/ap-components.min.js` to serve the installed ap-components bundle
|
|
23
|
+
locally, with cache revalidation.
|
|
24
|
+
- Regression tests for local component serving, injected fetch usage, reverse
|
|
25
|
+
WebFinger metadata, and discovery failures.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- Serve ap-components from the installed npm dependency at
|
|
30
|
+
`/js/ap-components.min.js` instead of loading it from a CDN on profile pages.
|
|
31
|
+
- Use activitypub-webfinger for actor ID discovery, mention resolution, and
|
|
32
|
+
reverse WebFinger lookup, with SafeFetcher.fetch bound once per instance.
|
|
33
|
+
- Reverse WebFinger lookups now fetch directly through SafeFetcher instead of
|
|
34
|
+
the cached, signed ActivityPub client.
|
|
35
|
+
- Return `null` on failed WebFinger discovery in `BotContext.toActorId()`.
|
|
36
|
+
|
|
12
37
|
## [0.51.4] 2026-09-29
|
|
13
38
|
|
|
14
39
|
### Updated
|
|
@@ -1663,7 +1688,9 @@ Hot patch on the `stable` branch.
|
|
|
1663
1688
|
- Docker multi-platform build workflow.
|
|
1664
1689
|
- Dependabot configuration.
|
|
1665
1690
|
|
|
1666
|
-
[Unreleased]: https://github.com/evanp/activitypub-bot/compare/v0.
|
|
1691
|
+
[Unreleased]: https://github.com/evanp/activitypub-bot/compare/v0.52.1...HEAD
|
|
1692
|
+
[0.52.1]: https://github.com/evanp/activitypub-bot/compare/v0.52.0...v0.52.1
|
|
1693
|
+
[0.52.0]: https://github.com/evanp/activitypub-bot/compare/v0.51.4...v0.52.0
|
|
1667
1694
|
[0.51.4]: https://github.com/evanp/activitypub-bot/compare/v0.51.3...v0.51.4
|
|
1668
1695
|
[0.51.3]: https://github.com/evanp/activitypub-bot/compare/v0.51.2...v0.51.3
|
|
1669
1696
|
[0.51.2]: https://github.com/evanp/activitypub-bot/compare/v0.51.1...v0.51.2
|
package/lib/app.js
CHANGED
|
@@ -37,6 +37,7 @@ import proxyRouter from './routes/proxy.js'
|
|
|
37
37
|
import nodeinfoRouter from './routes/nodeinfo.js'
|
|
38
38
|
import robotsTxtRouter from './routes/robotstxt.js'
|
|
39
39
|
import faviconRouter from './routes/favicon.js'
|
|
40
|
+
import staticRouter from './routes/static.js'
|
|
40
41
|
import { BotContext } from './botcontext.js'
|
|
41
42
|
import { Transformer } from './microsyntax.js'
|
|
42
43
|
import { HTTPSignatureAuthenticator } from './httpsignatureauthenticator.js'
|
|
@@ -331,6 +332,7 @@ export async function makeApp ({ databaseUrl, origin, bots, logLevel = 'silent',
|
|
|
331
332
|
|
|
332
333
|
// These should not check HTTP Signature even if provided
|
|
333
334
|
|
|
335
|
+
app.use('/', staticRouter)
|
|
334
336
|
app.use('/', healthRouter)
|
|
335
337
|
app.use('/', serverRouter)
|
|
336
338
|
app.use('/', profileRouter)
|
package/lib/botcontext.js
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
import assert from 'node:assert'
|
|
2
2
|
|
|
3
3
|
import { nanoid } from 'nanoid'
|
|
4
|
+
import { actorIdOf, webfingerOf } from 'activitypub-webfinger'
|
|
4
5
|
|
|
5
6
|
import as2 from './activitystreams.js'
|
|
6
7
|
|
|
7
|
-
const AS2_TYPES = [
|
|
8
|
-
'application/activity+json',
|
|
9
|
-
'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'
|
|
10
|
-
]
|
|
11
|
-
|
|
12
8
|
const AS2_NS = 'https://www.w3.org/ns/activitystreams#'
|
|
13
9
|
|
|
14
|
-
const WF_NS = 'https://purl.archive.org/socialweb/webfinger#'
|
|
15
|
-
|
|
16
10
|
const THREAD_PROP = 'https://purl.archive.org/socialweb/thread#thread'
|
|
17
11
|
const CONVERSATION_PROP = 'http://ostatus.org/schema/1.0/conversation'
|
|
18
12
|
|
|
@@ -28,6 +22,7 @@ export class BotContext {
|
|
|
28
22
|
#logger = null
|
|
29
23
|
#bots
|
|
30
24
|
#fetcher
|
|
25
|
+
#fetch
|
|
31
26
|
|
|
32
27
|
constructor (
|
|
33
28
|
botId,
|
|
@@ -64,6 +59,7 @@ export class BotContext {
|
|
|
64
59
|
this.#bots = bots
|
|
65
60
|
this.#logger = logger.child({ class: 'BotContext', botId })
|
|
66
61
|
this.#fetcher = fetcher
|
|
62
|
+
this.#fetch = fetcher.fetch.bind(fetcher)
|
|
67
63
|
}
|
|
68
64
|
|
|
69
65
|
get botId () {
|
|
@@ -400,34 +396,11 @@ export class BotContext {
|
|
|
400
396
|
if (domain.toLowerCase() === this.#formatter.hostname) {
|
|
401
397
|
return this.#formatter.format({ username })
|
|
402
398
|
}
|
|
403
|
-
|
|
404
|
-
`?resource=acct:${webfinger}`
|
|
405
|
-
const res = await this.#fetcher.fetch(url)
|
|
406
|
-
if (res.status !== 200) {
|
|
407
|
-
throw new Error(`Status ${res.status} fetching ${url}`)
|
|
408
|
-
}
|
|
409
|
-
const json = await res.json()
|
|
410
|
-
const link = json?.links?.find(
|
|
411
|
-
(l) => l.rel === 'self' && AS2_TYPES.includes(l.type)
|
|
412
|
-
)
|
|
413
|
-
return link ? link.href : null
|
|
399
|
+
return await actorIdOf(webfinger, { fetch: this.#fetch })
|
|
414
400
|
}
|
|
415
401
|
|
|
416
402
|
async toWebfinger (actorId) {
|
|
417
|
-
|
|
418
|
-
if (!actor) {
|
|
419
|
-
return null
|
|
420
|
-
}
|
|
421
|
-
const wf = actor.get(WF_NS + 'webfinger')
|
|
422
|
-
if (wf) {
|
|
423
|
-
return wf.value()
|
|
424
|
-
}
|
|
425
|
-
const username = actor.get('preferredUsername')?.first
|
|
426
|
-
if (!username) {
|
|
427
|
-
return null
|
|
428
|
-
}
|
|
429
|
-
const actorUrl = new URL(actor.id)
|
|
430
|
-
return `${username}@${actorUrl.hostname}`
|
|
403
|
+
return await webfingerOf(actorId, { fetch: this.#fetch })
|
|
431
404
|
}
|
|
432
405
|
|
|
433
406
|
async announceObject (obj, actors = null) {
|
package/lib/microsyntax.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import assert from 'node:assert'
|
|
2
|
+
import { actorIdOf } from 'activitypub-webfinger'
|
|
2
3
|
|
|
3
4
|
const AS2 = 'https://www.w3.org/ns/activitystreams#'
|
|
4
5
|
|
|
5
6
|
export class Transformer {
|
|
6
7
|
#tagNamespace = null
|
|
7
8
|
#client = null
|
|
8
|
-
#
|
|
9
|
+
#fetch = null
|
|
9
10
|
#formatter = null
|
|
10
11
|
|
|
11
12
|
constructor (tagNamespace, client, fetcher, formatter) {
|
|
@@ -16,12 +17,12 @@ export class Transformer {
|
|
|
16
17
|
|
|
17
18
|
this.#tagNamespace = tagNamespace
|
|
18
19
|
this.#client = client
|
|
19
|
-
this.#
|
|
20
|
+
this.#fetch = fetcher.fetch.bind(fetcher)
|
|
20
21
|
this.#formatter = formatter
|
|
21
22
|
|
|
22
23
|
assert.ok(this.#tagNamespace)
|
|
23
24
|
assert.ok(this.#client)
|
|
24
|
-
assert.ok(this.#
|
|
25
|
+
assert.ok(this.#fetch)
|
|
25
26
|
assert.ok(this.#formatter)
|
|
26
27
|
}
|
|
27
28
|
|
|
@@ -99,24 +100,7 @@ export class Transformer {
|
|
|
99
100
|
if (domain.toLowerCase() === this.#formatter.hostname) {
|
|
100
101
|
return this.#formatter.format({ username, type: 'profile' })
|
|
101
102
|
}
|
|
102
|
-
const
|
|
103
|
-
`resource=acct:${username}@${domain}`
|
|
104
|
-
let json = null
|
|
105
|
-
try {
|
|
106
|
-
const response = await this.#fetcher.fetch(url,
|
|
107
|
-
{ headers: { Accept: 'application/jrd+json' } })
|
|
108
|
-
if (response.status !== 200) return null
|
|
109
|
-
json = await response.json()
|
|
110
|
-
} catch (error) {
|
|
111
|
-
return null
|
|
112
|
-
}
|
|
113
|
-
if (!json.links) return null
|
|
114
|
-
const link = json.links.find(
|
|
115
|
-
link => link.rel === 'self' &&
|
|
116
|
-
(link.type === 'application/activity+json' ||
|
|
117
|
-
link.type === 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'))
|
|
118
|
-
if (!link) return null
|
|
119
|
-
const actorId = link.href
|
|
103
|
+
const actorId = await actorIdOf(webfinger, { fetch: this.#fetch })
|
|
120
104
|
if (!actorId) return null
|
|
121
105
|
let actor = null
|
|
122
106
|
try {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Router } from 'express'
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
|
+
|
|
4
|
+
const router = Router()
|
|
5
|
+
|
|
6
|
+
const COMPONENT_BUNDLE_FILENAME = fileURLToPath(import.meta.resolve('@socialwebfoundation/ap-components/dist/ap-components.min.js'))
|
|
7
|
+
|
|
8
|
+
router.get('/js/ap-components.min.js', (req, res) => {
|
|
9
|
+
res.sendFile(COMPONENT_BUNDLE_FILENAME, { maxAge: 0 })
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
export default router
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evanp/activitypub-bot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.52.1",
|
|
4
4
|
"description": "server-side ActivityPub bot framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -31,6 +31,8 @@
|
|
|
31
31
|
"homepage": "https://github.com/evanp/activitypub-bot#readme",
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@isaacs/ttlcache": "^2.1.4",
|
|
34
|
+
"@socialwebfoundation/ap-components": "^1.4.2",
|
|
35
|
+
"activitypub-webfinger": "^0.2.1",
|
|
34
36
|
"activitystrea.ms": "^4.0.1",
|
|
35
37
|
"csv-parse": "^7.0.0",
|
|
36
38
|
"express": "^5.2.1",
|
package/web/profile.html
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
7
|
<meta name="robots" content="noindex,nofollow">
|
|
8
8
|
<title>activitypub.bot</title>
|
|
9
|
-
<script type="module" src="https://unpkg.com/@socialwebfoundation/ap-components"></script>
|
|
10
9
|
<style>
|
|
11
10
|
body { max-width: 60ch; margin: 0 auto; padding: 1rem; }
|
|
12
11
|
</style>
|
|
@@ -19,7 +18,7 @@
|
|
|
19
18
|
</main>
|
|
20
19
|
|
|
21
20
|
<script type="module">
|
|
22
|
-
import { ActivityPubElement } from "
|
|
21
|
+
import { ActivityPubElement } from "/js/ap-components.min.js";
|
|
23
22
|
|
|
24
23
|
const cache = new Map();
|
|
25
24
|
|