@evanp/activitypub-bot 0.21.2 → 0.23.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/README.md CHANGED
@@ -97,6 +97,24 @@ The path to the [config file](#config-file) for this server, which defines the u
97
97
 
98
98
  Falls back to `BOTS_CONFIG_FILE` environment variable. The default is to use the shipped default bot config file, which defines an [OKBot](#okbot) named `ok` and a [DoNothingBot](#donothingbot) named `null`.
99
99
 
100
+ #### --delivery
101
+
102
+ The number of background delivery workers to run for this server. These workers accept and process remote activities that arrive at the server. If the server has a lot of traffic, and it's taking a while for bots to do their thing, increase this number.
103
+
104
+ Falls back to the `DELIVERY` environment variable. Default is 2.
105
+
106
+ #### --distribution
107
+
108
+ The number of background distribution workers to run for this server. These workers distribute bot activities to followers and other recipients on remote servers. If it's taking a while for bot activities to get to their recipients, increase this number.
109
+
110
+ Falls back to the `DISTRIBUTION` environment variable. Default is 8.
111
+
112
+ #### --index-file
113
+
114
+ Path to the HTML file to show for the home page of your server. The activitypub.bot server doesn't support any other files, so any images or CSS stylesheets or JavaScript in this page have to be hosted elsewhere. Or just skip them!
115
+
116
+ Falls back to the 'INDEX_FILE' environment variable. The default index file is in `web/index.html` and just says that this is an activitypub.bot server with a link to the GitHub repo.
117
+
100
118
  ### Config file
101
119
 
102
120
  The config file defines the bots provided by this server.
@@ -12,6 +12,9 @@ const { values } = parseArgs({
12
12
  port: { type: 'string' },
13
13
  'bots-config-file': { type: 'string' },
14
14
  'log-level': { type: 'string' },
15
+ delivery: { type: 'string' },
16
+ distribution: { type: 'string' },
17
+ 'index-file': { type: 'string' },
15
18
  help: { type: 'boolean', short: 'h' }
16
19
  },
17
20
  allowPositionals: false
@@ -26,13 +29,16 @@ Options:
26
29
  --port <number> Port to listen on
27
30
  --bots-config-file <path> Path to bots config module
28
31
  --log-level <level> Log level (e.g., info, debug)
32
+ --delivery <number> Number of background delivery workers
33
+ --distribution <number> Number of background distribution workers
34
+ --index-file <path> HTML page to show at root path
29
35
  -h, --help Show this help
30
36
  `)
31
37
  process.exit(0)
32
38
  }
33
39
 
34
40
  const normalize = (value) => (value === '' ? undefined : value)
35
- const parsePort = (value) => {
41
+ const parseNumber = (value) => {
36
42
  if (!value) return undefined
37
43
  const parsed = Number.parseInt(value, 10)
38
44
  return Number.isNaN(parsed) ? undefined : parsed
@@ -40,20 +46,32 @@ const parsePort = (value) => {
40
46
 
41
47
  const baseDir = dirname(fileURLToPath(import.meta.url))
42
48
  const DEFAULT_BOTS_CONFIG_FILE = resolve(baseDir, '..', 'bots', 'index.js')
49
+ const DEFAULT_INDEX_FILE = resolve(baseDir, '..', 'web', 'index.html')
43
50
 
44
51
  const DATABASE_URL = normalize(values['database-url']) || process.env.DATABASE_URL || 'sqlite::memory:'
45
52
  const ORIGIN = normalize(values.origin) || process.env.ORIGIN || 'https://activitypubbot.test'
46
- const PORT = parsePort(normalize(values.port)) || parsePort(process.env.PORT) || 9000 // HAL
53
+ const PORT = parseNumber(normalize(values.port)) || parseNumber(process.env.PORT) || 9000 // HAL
47
54
  const BOTS_CONFIG_FILE =
48
55
  normalize(values['bots-config-file']) || process.env.BOTS_CONFIG_FILE || DEFAULT_BOTS_CONFIG_FILE
49
56
  const LOG_LEVEL =
50
57
  normalize(values['log-level']) ||
51
58
  process.env.LOG_LEVEL ||
52
59
  (process.env.NODE_ENV === 'test' ? 'silent' : 'info')
60
+ const DELIVERY = parseNumber(values.delivery) || parseNumber(process.env.DELIVERY) || 2
61
+ const DISTRIBUTION = parseNumber(values.distribution) || parseNumber(process.env.DISTRIBUTION) || 8
62
+ const INDEX_FILE = values['index-file'] || process.env.INDEX_FILE || DEFAULT_INDEX_FILE
53
63
 
54
64
  const bots = (await import(BOTS_CONFIG_FILE)).default
55
65
 
56
- const app = await makeApp(DATABASE_URL, ORIGIN, bots, LOG_LEVEL)
66
+ const app = await makeApp({
67
+ databaseUrl: DATABASE_URL,
68
+ origin: ORIGIN,
69
+ bots,
70
+ logLevel: LOG_LEVEL,
71
+ deliveryWorkerCount: DELIVERY,
72
+ distributionWorkerCount: DISTRIBUTION,
73
+ indexFileName: INDEX_FILE
74
+ })
57
75
 
58
76
  const server = app.listen(parseInt(PORT), () => {
59
77
  app.locals.logger.info(`Listening on port ${PORT}`)
package/lib/app.js CHANGED
@@ -1,8 +1,12 @@
1
+ import http from 'node:http'
2
+ import { resolve, dirname } from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
4
+
1
5
  import { Sequelize } from 'sequelize'
2
6
  import express from 'express'
3
7
  import Logger from 'pino'
4
8
  import HTTPLogger from 'pino-http'
5
- import http from 'node:http'
9
+
6
10
  import { ActivityDistributor } from './activitydistributor.js'
7
11
  import { ActivityPubClient } from './activitypubclient.js'
8
12
  import { ActorStorage } from './actorstorage.js'
@@ -34,7 +38,10 @@ import { JobReaper } from './jobreaper.js'
34
38
  import { DeliveryWorker } from './deliveryworker.js'
35
39
  import { DistributionWorker } from './distributionworker.js'
36
40
 
37
- export async function makeApp (databaseUrl, origin, bots, logLevel = 'silent', deliveryWorkerCount = 2, distributionWorkerCount = 8) {
41
+ const currentDir = dirname(fileURLToPath(import.meta.url))
42
+ const DEFAULT_INDEX_FILENAME = resolve(currentDir, '..', 'web', 'index.html')
43
+
44
+ export async function makeApp ({ databaseUrl, origin, bots, logLevel = 'silent', deliveryWorkerCount = 2, distributionWorkerCount = 8, indexFileName = DEFAULT_INDEX_FILENAME }) {
38
45
  const logger = Logger({
39
46
  level: logLevel
40
47
  })
@@ -159,7 +166,8 @@ export async function makeApp (databaseUrl, origin, bots, logLevel = 'silent', d
159
166
  activityHandler,
160
167
  origin,
161
168
  deliverer,
162
- deliveryWorkers
169
+ deliveryWorkers,
170
+ indexFileName
163
171
  }
164
172
 
165
173
  app.use(HTTPLogger({
package/lib/jobreaper.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { setTimeout as sleep } from 'node:timers/promises'
2
2
 
3
- const DEFAULT_TIMEOUT = 5 * 60 * 1000 // 5 minutes
4
- const DEFAULT_INTERVAL = 60 * 1000 // 1 minute
3
+ const DEFAULT_TIMEOUT = 5 * 60 * 1000 // 5 minutes
4
+ const DEFAULT_INTERVAL = 60 * 1000 // 1 minute
5
5
 
6
6
  export class JobReaper {
7
7
  #jobQueue
@@ -4,22 +4,34 @@ import as2 from '../activitystreams.js'
4
4
  const router = express.Router()
5
5
 
6
6
  router.get('/', async (req, res) => {
7
- const { formatter } = req.app.locals
8
- const server = await as2.import({
9
- '@context': [
10
- 'https://www.w3.org/ns/activitystreams',
11
- 'https://w3id.org/security/v1'
12
- ],
13
- id: formatter.format({ server: true }),
14
- type: 'Service',
15
- publicKey: formatter.format({ server: true, type: 'publickey' })
7
+ const fullUrl = `${req.protocol}://${req.get('host')}${req.originalUrl}`
8
+ res.format({
9
+ html: async () => {
10
+ const { indexFileName } = req.app.locals
11
+ res.sendFile(indexFileName)
12
+ },
13
+ json: async () => {
14
+ const { formatter } = req.app.locals
15
+ const server = await as2.import({
16
+ '@context': [
17
+ 'https://www.w3.org/ns/activitystreams',
18
+ 'https://w3id.org/security/v1'
19
+ ],
20
+ id: formatter.format({ server: true }),
21
+ type: 'Service',
22
+ publicKey: formatter.format({ server: true, type: 'publickey' }),
23
+ url: {
24
+ type: 'Link',
25
+ mediaType: 'text/html',
26
+ href: fullUrl
27
+ }
28
+ })
29
+ const body = await server.export({ useOriginalContext: true })
30
+ res.status(200)
31
+ res.type(as2.mediaType)
32
+ res.json(body)
33
+ }
16
34
  })
17
- res.status(200)
18
- res.type(as2.mediaType)
19
- const body = await server.prettyWrite(
20
- { additional_context: 'https://w3id.org/security/v1' }
21
- )
22
- res.end(body)
23
35
  })
24
36
 
25
37
  router.get('/publickey', async (req, res) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evanp/activitypub-bot",
3
- "version": "0.21.2",
3
+ "version": "0.23.0",
4
4
  "description": "server-side ActivityPub bot framework",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
package/web/index.html ADDED
@@ -0,0 +1,17 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>activitypub.bot</title>
7
+ </head>
8
+ <body>
9
+ <h1>activitypub.bot</h1>
10
+ <p>
11
+ This is an activitypub.bot server. It hosts automated &quot;bot&quot; accounts on the <a href="https://en.wikipedia.org/wiki/Fediverse">Fediverse</a>.
12
+ </p>
13
+ <p>
14
+ <a href="https://github.com/evanp/activitypub-bot">Source</a>
15
+ </p>
16
+ </body>
17
+ </html>