@evanp/activitypub-bot 0.12.1 → 0.13.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/lib/app.js +19 -1
- package/lib/index.js +2 -0
- package/lib/routes/collection.js +83 -67
- package/lib/routes/inbox.js +8 -0
- package/lib/routes/object.js +25 -5
- package/package.json +3 -3
- package/.github/dependabot.yml +0 -11
- package/.github/workflows/main.yml +0 -34
- package/.github/workflows/tag.yml +0 -106
- package/.nvmrc +0 -1
- package/Dockerfile +0 -17
- package/docs/activitypub.bot.drawio +0 -110
- package/tests/activitydistributor.test.js +0 -606
- package/tests/activityhandler.test.js +0 -2276
- package/tests/activitypubclient.test.js +0 -210
- package/tests/actorstorage.test.js +0 -283
- package/tests/app.test.js +0 -17
- package/tests/authorizer.test.js +0 -301
- package/tests/bot.donothing.test.js +0 -30
- package/tests/bot.ok.test.js +0 -101
- package/tests/botcontext.test.js +0 -720
- package/tests/botdatastorage.test.js +0 -88
- package/tests/botfactory.provincebotfactory.test.js +0 -430
- package/tests/digester.test.js +0 -56
- package/tests/fixtures/bots.js +0 -27
- package/tests/fixtures/eventloggingbot.js +0 -57
- package/tests/fixtures/provincebotfactory.js +0 -53
- package/tests/httpsignature.test.js +0 -199
- package/tests/httpsignatureauthenticator.test.js +0 -463
- package/tests/index.test.js +0 -10
- package/tests/keystorage.test.js +0 -124
- package/tests/microsyntax.test.js +0 -123
- package/tests/objectcache.test.js +0 -133
- package/tests/objectstorage.test.js +0 -149
- package/tests/remotekeystorage.test.js +0 -78
- package/tests/routes.actor.test.js +0 -214
- package/tests/routes.collection.test.js +0 -433
- package/tests/routes.health.test.js +0 -41
- package/tests/routes.inbox.test.js +0 -136
- package/tests/routes.object.test.js +0 -525
- package/tests/routes.server.test.js +0 -69
- package/tests/routes.sharedinbox.test.js +0 -473
- package/tests/routes.webfinger.test.js +0 -68
- package/tests/urlformatter.test.js +0 -164
- package/tests/utils/digest.js +0 -7
- package/tests/utils/nock.js +0 -499
package/lib/app.js
CHANGED
|
@@ -140,12 +140,30 @@ export async function makeApp (databaseUrl, origin, bots, logLevel = 'silent') {
|
|
|
140
140
|
app.use('/', serverRouter)
|
|
141
141
|
app.use('/', userRouter)
|
|
142
142
|
app.use('/', collectionRouter)
|
|
143
|
-
app.use('/', objectRouter)
|
|
144
143
|
app.use('/', inboxRouter)
|
|
144
|
+
app.use('/', objectRouter)
|
|
145
145
|
app.use('/', healthRouter)
|
|
146
146
|
app.use('/', webfingerRouter)
|
|
147
147
|
app.use('/', sharedInboxRouter)
|
|
148
148
|
|
|
149
|
+
app.use(async (req, res) => {
|
|
150
|
+
if (req.accepts('json')) {
|
|
151
|
+
const status = 404
|
|
152
|
+
const title = http.STATUS_CODES[status] || 'Not Found'
|
|
153
|
+
res.status(status)
|
|
154
|
+
res.type('application/problem+json')
|
|
155
|
+
res.json({
|
|
156
|
+
type: 'about:blank',
|
|
157
|
+
title,
|
|
158
|
+
status,
|
|
159
|
+
detail: 'Not found',
|
|
160
|
+
instance: req.originalUrl
|
|
161
|
+
})
|
|
162
|
+
} else {
|
|
163
|
+
res.status(404).send('Not found')
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
|
|
149
167
|
app.use((err, req, res, next) => {
|
|
150
168
|
const { logger } = req.app.locals
|
|
151
169
|
let status = 500
|
package/lib/index.js
CHANGED
package/lib/routes/collection.js
CHANGED
|
@@ -2,7 +2,9 @@ import express from 'express'
|
|
|
2
2
|
import as2 from '../activitystreams.js'
|
|
3
3
|
import createHttpError from 'http-errors'
|
|
4
4
|
import BotMaker from '../botmaker.js'
|
|
5
|
+
import assert from 'node:assert'
|
|
5
6
|
|
|
7
|
+
const collections = ['outbox', 'liked', 'followers', 'following']
|
|
6
8
|
const router = express.Router()
|
|
7
9
|
|
|
8
10
|
async function filterAsync (array, asyncPredicate) {
|
|
@@ -16,81 +18,95 @@ async function filterAsync (array, asyncPredicate) {
|
|
|
16
18
|
return array.filter((_, idx) => booleans[idx])
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
21
|
+
// This got tricky because Express 5 doesn't let us add regexes to our routes,
|
|
22
|
+
// so the page routes conflict with the object routes in ./object.js. This
|
|
23
|
+
// format lets us define fixed routes for the 4 user collections that support
|
|
24
|
+
// GET
|
|
25
|
+
|
|
26
|
+
function collectionHandler (collection) {
|
|
27
|
+
assert.ok(collections.includes(collection))
|
|
28
|
+
return async (req, res, next) => {
|
|
29
|
+
const { username } = req.params
|
|
30
|
+
const { actorStorage, bots } = req.app.locals
|
|
31
|
+
const bot = await BotMaker.makeBot(bots, username)
|
|
32
|
+
if (!bot) {
|
|
33
|
+
return next(createHttpError(404, `User ${username} not found`))
|
|
34
|
+
}
|
|
35
|
+
const coll = await actorStorage.getCollection(username, collection)
|
|
36
|
+
res.status(200)
|
|
37
|
+
res.type(as2.mediaType)
|
|
38
|
+
res.end(await coll.prettyWrite({ useOriginalContext: true }))
|
|
32
39
|
}
|
|
33
|
-
|
|
34
|
-
res.status(200)
|
|
35
|
-
res.type(as2.mediaType)
|
|
36
|
-
res.end(await coll.prettyWrite({ useOriginalContext: true }))
|
|
37
|
-
})
|
|
40
|
+
}
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
function collectionPageHandler (collection) {
|
|
43
|
+
assert.ok(['outbox', 'liked', 'followers', 'following'].includes(collection))
|
|
44
|
+
return async (req, res, next) => {
|
|
45
|
+
const { username, n } = req.params
|
|
46
|
+
let pageNo
|
|
47
|
+
try {
|
|
48
|
+
pageNo = parseInt(n)
|
|
49
|
+
} catch (err) {
|
|
50
|
+
return next(createHttpError(400, `Invalid page ${n}`))
|
|
51
|
+
}
|
|
52
|
+
const { actorStorage, bots, authorizer, objectStorage, formatter, client } = req.app.locals
|
|
53
|
+
const bot = await BotMaker.makeBot(bots, username)
|
|
43
54
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
55
|
+
if (!bot) {
|
|
56
|
+
return next(createHttpError(404, `User ${username} not found`))
|
|
57
|
+
}
|
|
47
58
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
if (!await actorStorage.hasPage(username, collection, parseInt(n))) {
|
|
56
|
-
return next(createHttpError(404, `No such page ${n} for collection ${collection} for user ${username}`))
|
|
57
|
-
}
|
|
59
|
+
if (collection === 'inbox') {
|
|
60
|
+
return next(createHttpError(403, `No access to ${collection} collection`))
|
|
61
|
+
}
|
|
62
|
+
if (!await actorStorage.hasPage(username, collection, parseInt(n))) {
|
|
63
|
+
return next(createHttpError(404, `No such page ${n} for collection ${collection} for user ${username}`))
|
|
64
|
+
}
|
|
58
65
|
|
|
59
|
-
|
|
66
|
+
let exported = null
|
|
60
67
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
try {
|
|
69
|
+
const id = req.auth?.subject
|
|
70
|
+
const remote = (id) ? await as2.import({ id }) : null
|
|
71
|
+
const page = await actorStorage.getCollectionPage(
|
|
72
|
+
username,
|
|
73
|
+
collection,
|
|
74
|
+
pageNo
|
|
75
|
+
)
|
|
76
|
+
exported = await page.export({ useOriginalContext: true })
|
|
70
77
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
if (['outbox', 'liked'].includes(collection)) {
|
|
79
|
+
exported.items = await filterAsync(exported.items, async (id) => {
|
|
80
|
+
const object = (formatter.isLocal(id))
|
|
81
|
+
? await objectStorage.read(id)
|
|
82
|
+
: await client.get(id)
|
|
83
|
+
if (!object) {
|
|
84
|
+
req.log.warn({ id }, 'could not load object')
|
|
85
|
+
return false
|
|
86
|
+
}
|
|
87
|
+
req.log.debug({ id, object }, 'loaded object')
|
|
88
|
+
return await authorizer.canRead(remote, object)
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
} catch (error) {
|
|
92
|
+
req.log.error(
|
|
93
|
+
{ err: error, username, collection, n },
|
|
94
|
+
'error loading collection page'
|
|
95
|
+
)
|
|
96
|
+
return next(createHttpError(500, 'Error loading collection page'))
|
|
83
97
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
'error loading collection page'
|
|
88
|
-
)
|
|
89
|
-
return next(createHttpError(500, 'Error loading collection page'))
|
|
98
|
+
res.status(200)
|
|
99
|
+
res.type(as2.mediaType)
|
|
100
|
+
res.end(JSON.stringify(exported))
|
|
90
101
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
for (const collection of collections) {
|
|
105
|
+
router.get(`/user/:username/${collection}`, collectionHandler(collection))
|
|
106
|
+
router.get(
|
|
107
|
+
`/user/:username/${collection}/:n`,
|
|
108
|
+
collectionPageHandler(collection)
|
|
109
|
+
)
|
|
110
|
+
}
|
|
95
111
|
|
|
96
112
|
export default router
|
package/lib/routes/inbox.js
CHANGED
|
@@ -67,4 +67,12 @@ router.post('/user/:username/inbox', async (req, res, next) => {
|
|
|
67
67
|
res.send('OK')
|
|
68
68
|
})
|
|
69
69
|
|
|
70
|
+
router.get('/user/:username/inbox', async (req, res, next) => {
|
|
71
|
+
return next(createHttpError(403, `No access to inbox collection`))
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
router.get('/user/:username/inbox/:n', async (req, res, next) => {
|
|
75
|
+
return next(createHttpError(403, `No access to inbox collection`))
|
|
76
|
+
})
|
|
77
|
+
|
|
70
78
|
export default router
|
package/lib/routes/object.js
CHANGED
|
@@ -6,8 +6,11 @@ const router = express.Router()
|
|
|
6
6
|
|
|
7
7
|
export default router
|
|
8
8
|
|
|
9
|
-
router.get('/user/:username/:type/:nanoid
|
|
9
|
+
router.get('/user/:username/:type/:nanoid', async (req, res, next) => {
|
|
10
10
|
const { username, type, nanoid } = req.params
|
|
11
|
+
if (!nanoid.match(/^[A-Za-z0-9_\\-]{21}$/)) {
|
|
12
|
+
return next(createHttpError(400, `Invalid nanoid ${nanoid}`))
|
|
13
|
+
}
|
|
11
14
|
const { objectStorage, formatter, authorizer } = req.app.locals
|
|
12
15
|
const id = formatter.format({ username, type, nanoid })
|
|
13
16
|
const object = await objectStorage.read(id)
|
|
@@ -23,12 +26,16 @@ router.get('/user/:username/:type/:nanoid([A-Za-z0-9_\\-]{21})', async (req, res
|
|
|
23
26
|
res.end(await object.prettyWrite())
|
|
24
27
|
})
|
|
25
28
|
|
|
26
|
-
router.get('/user/:username/:type/:nanoid
|
|
29
|
+
router.get('/user/:username/:type/:nanoid/:collection', async (req, res, next) => {
|
|
27
30
|
const { objectStorage, formatter, authorizer } = req.app.locals
|
|
28
31
|
if (!['replies', 'likes', 'shares', 'thread'].includes(req.params.collection)) {
|
|
29
32
|
return next(createHttpError(404, 'Not Found'))
|
|
30
33
|
}
|
|
31
|
-
const
|
|
34
|
+
const { username, type, nanoid } = req.params
|
|
35
|
+
if (!nanoid.match(/^[A-Za-z0-9_\\-]{21}$/)) {
|
|
36
|
+
return next(createHttpError(400, `Invalid nanoid ${nanoid}`))
|
|
37
|
+
}
|
|
38
|
+
const id = formatter.format({ username, type, nanoid })
|
|
32
39
|
const object = await objectStorage.read(id)
|
|
33
40
|
if (!object) {
|
|
34
41
|
return next(createHttpError(404, 'Not Found'))
|
|
@@ -43,8 +50,17 @@ router.get('/user/:username/:type/:nanoid([A-Za-z0-9_\\-]{21})/:collection', asy
|
|
|
43
50
|
res.end(await collection.prettyWrite({ useOriginalContext: true }))
|
|
44
51
|
})
|
|
45
52
|
|
|
46
|
-
router.get('/user/:username/:type/:nanoid
|
|
53
|
+
router.get('/user/:username/:type/:nanoid/:collection/:n', async (req, res, next) => {
|
|
47
54
|
const { username, type, nanoid, collection, n } = req.params
|
|
55
|
+
let pageNo
|
|
56
|
+
try {
|
|
57
|
+
pageNo = parseInt(n)
|
|
58
|
+
} catch (err) {
|
|
59
|
+
return next(createHttpError(400, `Invalid page ${n}`))
|
|
60
|
+
}
|
|
61
|
+
if (!nanoid.match(/^[A-Za-z0-9_\\-]{21}$/)) {
|
|
62
|
+
return next(createHttpError(400, `Invalid nanoid ${nanoid}`))
|
|
63
|
+
}
|
|
48
64
|
const { objectStorage, formatter, authorizer } = req.app.locals
|
|
49
65
|
if (!['replies', 'likes', 'shares', 'thread'].includes(req.params.collection)) {
|
|
50
66
|
return next(createHttpError(404, 'Not Found'))
|
|
@@ -58,7 +74,11 @@ router.get('/user/:username/:type/:nanoid([A-Za-z0-9_\\-]{21})/:collection/:n(\\
|
|
|
58
74
|
if (!await authorizer.canRead(remote, object)) {
|
|
59
75
|
return next(createHttpError(403, 'Forbidden'))
|
|
60
76
|
}
|
|
61
|
-
const collectionPage = await objectStorage.getCollectionPage(
|
|
77
|
+
const collectionPage = await objectStorage.getCollectionPage(
|
|
78
|
+
id,
|
|
79
|
+
collection,
|
|
80
|
+
pageNo
|
|
81
|
+
)
|
|
62
82
|
const exported = await collectionPage.export({ useOriginalContext: true })
|
|
63
83
|
if (!Array.isArray(exported.items)) {
|
|
64
84
|
exported.items = [exported.items]
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evanp/activitypub-bot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.1",
|
|
4
4
|
"description": "server-side ActivityPub bot framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"activitypub-bot": "
|
|
8
|
+
"activitypub-bot": "bin/activitypub-bot.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"test": "NODE_ENV=test node --test",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@isaacs/ttlcache": "^2.1.4",
|
|
32
32
|
"activitystrea.ms": "3.2",
|
|
33
|
-
"express": "^
|
|
33
|
+
"express": "^5.2.1",
|
|
34
34
|
"http-errors": "^2.0.0",
|
|
35
35
|
"humanhash": "^1.0.4",
|
|
36
36
|
"lru-cache": "^11.1.0",
|
package/.github/dependabot.yml
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
# To get started with Dependabot version updates, you'll need to specify which
|
|
2
|
-
# package ecosystems to update and where the package manifests are located.
|
|
3
|
-
# Please see the documentation for all configuration options:
|
|
4
|
-
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
|
5
|
-
|
|
6
|
-
version: 2
|
|
7
|
-
updates:
|
|
8
|
-
- package-ecosystem: "npm" # See documentation for possible values
|
|
9
|
-
directory: "/" # Location of package manifests
|
|
10
|
-
schedule:
|
|
11
|
-
interval: "weekly"
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
name: Run tests on main
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
workflow_dispatch:
|
|
5
|
-
push:
|
|
6
|
-
branches:
|
|
7
|
-
- main
|
|
8
|
-
|
|
9
|
-
permissions:
|
|
10
|
-
contents: read
|
|
11
|
-
packages: write
|
|
12
|
-
|
|
13
|
-
jobs:
|
|
14
|
-
|
|
15
|
-
runTests:
|
|
16
|
-
runs-on: ubuntu-latest
|
|
17
|
-
strategy:
|
|
18
|
-
matrix:
|
|
19
|
-
node-version: [20, 22, 24, 25]
|
|
20
|
-
steps:
|
|
21
|
-
|
|
22
|
-
- uses: actions/checkout@v4
|
|
23
|
-
|
|
24
|
-
- uses: actions/setup-node@v4
|
|
25
|
-
with:
|
|
26
|
-
node-version: ${{ matrix.node-version }}
|
|
27
|
-
cache: npm
|
|
28
|
-
cache-dependency-path: package-lock.json
|
|
29
|
-
|
|
30
|
-
- name: Install dependencies
|
|
31
|
-
run: npm ci
|
|
32
|
-
|
|
33
|
-
- name: Run tests
|
|
34
|
-
run: npm run test
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
name: Build and Push Docker Image
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
tags:
|
|
6
|
-
- 'v[0-9]+.[0-9]+.[0-9]+'
|
|
7
|
-
|
|
8
|
-
permissions:
|
|
9
|
-
contents: read
|
|
10
|
-
packages: write
|
|
11
|
-
|
|
12
|
-
jobs:
|
|
13
|
-
|
|
14
|
-
runTests:
|
|
15
|
-
runs-on: ubuntu-latest
|
|
16
|
-
strategy:
|
|
17
|
-
matrix:
|
|
18
|
-
node-version: [20, 22, 24, 25]
|
|
19
|
-
steps:
|
|
20
|
-
|
|
21
|
-
- uses: actions/checkout@v4
|
|
22
|
-
|
|
23
|
-
- uses: actions/setup-node@v4
|
|
24
|
-
with:
|
|
25
|
-
node-version: ${{ matrix.node-version }}
|
|
26
|
-
cache: npm
|
|
27
|
-
cache-dependency-path: package-lock.json
|
|
28
|
-
|
|
29
|
-
- name: Install dependencies
|
|
30
|
-
run: npm ci
|
|
31
|
-
|
|
32
|
-
- name: Run tests
|
|
33
|
-
run: npm run test
|
|
34
|
-
|
|
35
|
-
publish-npm:
|
|
36
|
-
needs: runTests
|
|
37
|
-
runs-on: ubuntu-latest
|
|
38
|
-
|
|
39
|
-
permissions:
|
|
40
|
-
contents: read
|
|
41
|
-
id-token: write
|
|
42
|
-
|
|
43
|
-
steps:
|
|
44
|
-
- name: Checkout code
|
|
45
|
-
uses: actions/checkout@v4
|
|
46
|
-
|
|
47
|
-
- name: Set up Node.js
|
|
48
|
-
uses: actions/setup-node@v4
|
|
49
|
-
with:
|
|
50
|
-
node-version: 25
|
|
51
|
-
registry-url: https://registry.npmjs.org
|
|
52
|
-
cache: npm
|
|
53
|
-
cache-dependency-path: package-lock.json
|
|
54
|
-
|
|
55
|
-
- name: Publish to NPM
|
|
56
|
-
run: npm publish --access public --provenance
|
|
57
|
-
|
|
58
|
-
build-and-push:
|
|
59
|
-
needs: publish-npm
|
|
60
|
-
runs-on: ubuntu-latest
|
|
61
|
-
|
|
62
|
-
permissions:
|
|
63
|
-
contents: read
|
|
64
|
-
packages: write
|
|
65
|
-
|
|
66
|
-
steps:
|
|
67
|
-
- name: Checkout code
|
|
68
|
-
uses: actions/checkout@v4
|
|
69
|
-
|
|
70
|
-
- name: Set up Docker Buildx
|
|
71
|
-
uses: docker/setup-buildx-action@v3
|
|
72
|
-
|
|
73
|
-
- name: Extract version components from tag
|
|
74
|
-
id: get_version
|
|
75
|
-
run: |
|
|
76
|
-
FULL_VERSION=${GITHUB_REF#refs/tags/v}
|
|
77
|
-
MAJOR=$(echo $FULL_VERSION | cut -d. -f1)
|
|
78
|
-
MINOR=$(echo $FULL_VERSION | cut -d. -f2)
|
|
79
|
-
PATCH=$(echo $FULL_VERSION | cut -d. -f3)
|
|
80
|
-
|
|
81
|
-
echo "FULL_VERSION=$FULL_VERSION" >> $GITHUB_OUTPUT
|
|
82
|
-
echo "MAJOR_VERSION=$MAJOR" >> $GITHUB_OUTPUT
|
|
83
|
-
echo "MAJOR_MINOR_VERSION=$MAJOR.$MINOR" >> $GITHUB_OUTPUT
|
|
84
|
-
|
|
85
|
-
- name: Login to GitHub Container Registry
|
|
86
|
-
uses: docker/login-action@v3
|
|
87
|
-
with:
|
|
88
|
-
registry: ghcr.io
|
|
89
|
-
username: ${{ github.actor }}
|
|
90
|
-
password: ${{ secrets.GITHUB_TOKEN }}
|
|
91
|
-
|
|
92
|
-
- name: Build and push Docker image
|
|
93
|
-
uses: docker/build-push-action@v5
|
|
94
|
-
with:
|
|
95
|
-
context: .
|
|
96
|
-
push: true
|
|
97
|
-
platforms: linux/amd64,linux/arm64
|
|
98
|
-
build-args: |
|
|
99
|
-
PACKAGE_VERSION=${{ steps.get_version.outputs.FULL_VERSION }}
|
|
100
|
-
tags: |
|
|
101
|
-
ghcr.io/${{ github.repository }}:${{ steps.get_version.outputs.FULL_VERSION }}
|
|
102
|
-
ghcr.io/${{ github.repository }}:${{ steps.get_version.outputs.MAJOR_MINOR_VERSION }}
|
|
103
|
-
ghcr.io/${{ github.repository }}:${{ steps.get_version.outputs.MAJOR_VERSION }}
|
|
104
|
-
ghcr.io/${{ github.repository }}:${{ github.sha }}
|
|
105
|
-
cache-from: type=gha
|
|
106
|
-
cache-to: type=gha,mode=max
|
package/.nvmrc
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
24
|
package/Dockerfile
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
FROM node:24-alpine
|
|
2
|
-
|
|
3
|
-
WORKDIR /app
|
|
4
|
-
|
|
5
|
-
RUN apk add --no-cache libstdc++ sqlite sqlite-libs
|
|
6
|
-
|
|
7
|
-
ARG PACKAGE_VERSION
|
|
8
|
-
|
|
9
|
-
ENV DATABASE_URL=
|
|
10
|
-
ENV ORIGIN=
|
|
11
|
-
ENV PORT=
|
|
12
|
-
ENV BOTS_CONFIG_FILE=
|
|
13
|
-
ENV LOG_LEVEL=
|
|
14
|
-
|
|
15
|
-
RUN npm install -g @evanp/activitypub-bot@${PACKAGE_VERSION:-latest}
|
|
16
|
-
|
|
17
|
-
CMD ["activitypub-bot"]
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
<mxfile host="app.diagrams.net" modified="2024-01-14T17:20:53.624Z" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:121.0) Gecko/20100101 Firefox/121.0" etag="UDjGppbSRivOII49-YrU" version="22.1.16" type="github">
|
|
2
|
-
<diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">
|
|
3
|
-
<mxGraphModel dx="2074" dy="1057" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
|
4
|
-
<root>
|
|
5
|
-
<mxCell id="WIyWlLk6GJQsqaUBKTNV-0" />
|
|
6
|
-
<mxCell id="WIyWlLk6GJQsqaUBKTNV-1" parent="WIyWlLk6GJQsqaUBKTNV-0" />
|
|
7
|
-
<mxCell id="zkfFHV4jXpPFQw0GAbJ--0" value="BotServer" style="swimlane;fontStyle=2;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
|
8
|
-
<mxGeometry x="220" y="120" width="160" height="138" as="geometry">
|
|
9
|
-
<mxRectangle x="230" y="140" width="160" height="26" as="alternateBounds" />
|
|
10
|
-
</mxGeometry>
|
|
11
|
-
</mxCell>
|
|
12
|
-
<mxCell id="zkfFHV4jXpPFQw0GAbJ--1" value="Name" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
|
|
13
|
-
<mxGeometry y="26" width="160" height="26" as="geometry" />
|
|
14
|
-
</mxCell>
|
|
15
|
-
<mxCell id="zkfFHV4jXpPFQw0GAbJ--2" value="Phone Number" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
|
|
16
|
-
<mxGeometry y="52" width="160" height="26" as="geometry" />
|
|
17
|
-
</mxCell>
|
|
18
|
-
<mxCell id="zkfFHV4jXpPFQw0GAbJ--3" value="Email Address" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
|
|
19
|
-
<mxGeometry y="78" width="160" height="26" as="geometry" />
|
|
20
|
-
</mxCell>
|
|
21
|
-
<mxCell id="zkfFHV4jXpPFQw0GAbJ--4" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
|
|
22
|
-
<mxGeometry y="104" width="160" height="8" as="geometry" />
|
|
23
|
-
</mxCell>
|
|
24
|
-
<mxCell id="zkfFHV4jXpPFQw0GAbJ--5" value="Purchase Parking Pass" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
|
|
25
|
-
<mxGeometry y="112" width="160" height="26" as="geometry" />
|
|
26
|
-
</mxCell>
|
|
27
|
-
<mxCell id="zkfFHV4jXpPFQw0GAbJ--6" value="Activity" style="swimlane;fontStyle=0;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
|
28
|
-
<mxGeometry x="120" y="360" width="160" height="138" as="geometry">
|
|
29
|
-
<mxRectangle x="130" y="380" width="160" height="26" as="alternateBounds" />
|
|
30
|
-
</mxGeometry>
|
|
31
|
-
</mxCell>
|
|
32
|
-
<mxCell id="zkfFHV4jXpPFQw0GAbJ--7" value="Student Number" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--6" vertex="1">
|
|
33
|
-
<mxGeometry y="26" width="160" height="26" as="geometry" />
|
|
34
|
-
</mxCell>
|
|
35
|
-
<mxCell id="zkfFHV4jXpPFQw0GAbJ--8" value="Average Mark" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--6" vertex="1">
|
|
36
|
-
<mxGeometry y="52" width="160" height="26" as="geometry" />
|
|
37
|
-
</mxCell>
|
|
38
|
-
<mxCell id="zkfFHV4jXpPFQw0GAbJ--9" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--6" vertex="1">
|
|
39
|
-
<mxGeometry y="78" width="160" height="8" as="geometry" />
|
|
40
|
-
</mxCell>
|
|
41
|
-
<mxCell id="zkfFHV4jXpPFQw0GAbJ--10" value="Is Eligible To Enroll" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontStyle=4" parent="zkfFHV4jXpPFQw0GAbJ--6" vertex="1">
|
|
42
|
-
<mxGeometry y="86" width="160" height="26" as="geometry" />
|
|
43
|
-
</mxCell>
|
|
44
|
-
<mxCell id="zkfFHV4jXpPFQw0GAbJ--11" value="Get Seminars Taken" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--6" vertex="1">
|
|
45
|
-
<mxGeometry y="112" width="160" height="26" as="geometry" />
|
|
46
|
-
</mxCell>
|
|
47
|
-
<mxCell id="zkfFHV4jXpPFQw0GAbJ--12" value="" style="endArrow=block;endSize=10;endFill=0;shadow=0;strokeWidth=1;rounded=0;edgeStyle=elbowEdgeStyle;elbow=vertical;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="zkfFHV4jXpPFQw0GAbJ--6" target="zkfFHV4jXpPFQw0GAbJ--0" edge="1">
|
|
48
|
-
<mxGeometry width="160" relative="1" as="geometry">
|
|
49
|
-
<mxPoint x="200" y="203" as="sourcePoint" />
|
|
50
|
-
<mxPoint x="200" y="203" as="targetPoint" />
|
|
51
|
-
</mxGeometry>
|
|
52
|
-
</mxCell>
|
|
53
|
-
<mxCell id="zkfFHV4jXpPFQw0GAbJ--13" value="Object" style="swimlane;fontStyle=0;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
|
54
|
-
<mxGeometry x="330" y="360" width="160" height="70" as="geometry">
|
|
55
|
-
<mxRectangle x="340" y="380" width="170" height="26" as="alternateBounds" />
|
|
56
|
-
</mxGeometry>
|
|
57
|
-
</mxCell>
|
|
58
|
-
<mxCell id="zkfFHV4jXpPFQw0GAbJ--14" value="Salary" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--13" vertex="1">
|
|
59
|
-
<mxGeometry y="26" width="160" height="26" as="geometry" />
|
|
60
|
-
</mxCell>
|
|
61
|
-
<mxCell id="zkfFHV4jXpPFQw0GAbJ--15" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--13" vertex="1">
|
|
62
|
-
<mxGeometry y="52" width="160" height="8" as="geometry" />
|
|
63
|
-
</mxCell>
|
|
64
|
-
<mxCell id="zkfFHV4jXpPFQw0GAbJ--16" value="" style="endArrow=block;endSize=10;endFill=0;shadow=0;strokeWidth=1;rounded=0;edgeStyle=elbowEdgeStyle;elbow=vertical;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="zkfFHV4jXpPFQw0GAbJ--13" target="zkfFHV4jXpPFQw0GAbJ--0" edge="1">
|
|
65
|
-
<mxGeometry width="160" relative="1" as="geometry">
|
|
66
|
-
<mxPoint x="210" y="373" as="sourcePoint" />
|
|
67
|
-
<mxPoint x="310" y="271" as="targetPoint" />
|
|
68
|
-
</mxGeometry>
|
|
69
|
-
</mxCell>
|
|
70
|
-
<mxCell id="zkfFHV4jXpPFQw0GAbJ--17" value="Bot" style="swimlane;fontStyle=0;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
|
71
|
-
<mxGeometry x="508" y="120" width="160" height="216" as="geometry">
|
|
72
|
-
<mxRectangle x="550" y="140" width="160" height="26" as="alternateBounds" />
|
|
73
|
-
</mxGeometry>
|
|
74
|
-
</mxCell>
|
|
75
|
-
<mxCell id="zkfFHV4jXpPFQw0GAbJ--23" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
|
|
76
|
-
<mxGeometry y="26" width="160" height="8" as="geometry" />
|
|
77
|
-
</mxCell>
|
|
78
|
-
<mxCell id="zkfFHV4jXpPFQw0GAbJ--24" value="+ powerUp(object)" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
|
|
79
|
-
<mxGeometry y="34" width="160" height="26" as="geometry" />
|
|
80
|
-
</mxCell>
|
|
81
|
-
<mxCell id="zkfFHV4jXpPFQw0GAbJ--25" value="+ powerDown(object)" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
|
|
82
|
-
<mxGeometry y="60" width="160" height="26" as="geometry" />
|
|
83
|
-
</mxCell>
|
|
84
|
-
<mxCell id="dP12apakX7S5GaSoqi7g-0" value="+ onReply(object)" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" vertex="1" parent="zkfFHV4jXpPFQw0GAbJ--17">
|
|
85
|
-
<mxGeometry y="86" width="160" height="26" as="geometry" />
|
|
86
|
-
</mxCell>
|
|
87
|
-
<mxCell id="dP12apakX7S5GaSoqi7g-1" value="+ onMention(object)" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" vertex="1" parent="zkfFHV4jXpPFQw0GAbJ--17">
|
|
88
|
-
<mxGeometry y="112" width="160" height="26" as="geometry" />
|
|
89
|
-
</mxCell>
|
|
90
|
-
<mxCell id="zkfFHV4jXpPFQw0GAbJ--26" value="" style="endArrow=open;shadow=0;strokeWidth=1;rounded=0;endFill=1;edgeStyle=elbowEdgeStyle;elbow=vertical;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="zkfFHV4jXpPFQw0GAbJ--0" target="zkfFHV4jXpPFQw0GAbJ--17" edge="1">
|
|
91
|
-
<mxGeometry x="0.5" y="41" relative="1" as="geometry">
|
|
92
|
-
<mxPoint x="380" y="192" as="sourcePoint" />
|
|
93
|
-
<mxPoint x="540" y="192" as="targetPoint" />
|
|
94
|
-
<mxPoint x="-40" y="32" as="offset" />
|
|
95
|
-
</mxGeometry>
|
|
96
|
-
</mxCell>
|
|
97
|
-
<mxCell id="zkfFHV4jXpPFQw0GAbJ--27" value="1" style="resizable=0;align=left;verticalAlign=bottom;labelBackgroundColor=none;fontSize=12;" parent="zkfFHV4jXpPFQw0GAbJ--26" connectable="0" vertex="1">
|
|
98
|
-
<mxGeometry x="-1" relative="1" as="geometry">
|
|
99
|
-
<mxPoint y="4" as="offset" />
|
|
100
|
-
</mxGeometry>
|
|
101
|
-
</mxCell>
|
|
102
|
-
<mxCell id="zkfFHV4jXpPFQw0GAbJ--28" value="0.." style="resizable=0;align=right;verticalAlign=bottom;labelBackgroundColor=none;fontSize=12;" parent="zkfFHV4jXpPFQw0GAbJ--26" connectable="0" vertex="1">
|
|
103
|
-
<mxGeometry x="1" relative="1" as="geometry">
|
|
104
|
-
<mxPoint x="-7" y="4" as="offset" />
|
|
105
|
-
</mxGeometry>
|
|
106
|
-
</mxCell>
|
|
107
|
-
</root>
|
|
108
|
-
</mxGraphModel>
|
|
109
|
-
</diagram>
|
|
110
|
-
</mxfile>
|