@evanp/activitypub-bot 0.53.1 → 0.54.2

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 CHANGED
@@ -9,6 +9,45 @@ and this project adheres to
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [0.54.2] - 2026-10-09
13
+
14
+ ### Fixed
15
+
16
+ - Authenticate Docker Hub pulls for PostgreSQL and Redis service containers
17
+ in the release integration tests using repository secrets, avoiding the
18
+ unauthenticated pull rate limit.
19
+
20
+ ## [0.54.1] - 2026-10-09
21
+
22
+ ### Fixed
23
+
24
+ - Restore "OK" as OKBot's default reply content and update the default
25
+ description to match. Custom response content remains configurable.
26
+ - Update the actor route test to expect OKBot's fullname default of the
27
+ username followed by " bot".
28
+
29
+ ## [0.54.0] - 2026-10-09
30
+
31
+ ### Added
32
+
33
+ - Configure OKBot with a fixed response or an array of randomly selected
34
+ responses, a content language, and custom fullname and description.
35
+ - Support an optional `language` option in `BotContext.sendNote()` and a third
36
+ language argument in `sendReply()`. Notes always include `content` and also
37
+ include `contentMap` when a language is supplied.
38
+
39
+ ### Changed
40
+
41
+ - Default OKBot replies to its username and fullname to its username followed
42
+ by " bot". The shipped `ok` bot now replies with "ok" instead of "OK" and
43
+ uses "ok bot" instead of "OK Bot" as its fullname. Default descriptions
44
+ reflect the configured response content.
45
+
46
+ ### Fixed
47
+
48
+ - Keep single-language `contentMap` entries synchronized with `content` when
49
+ updating a Note through `BotContext.updateNote()`.
50
+
12
51
  ## [0.53.1] - 2026-10-09
13
52
 
14
53
  ### Security
@@ -1720,7 +1759,10 @@ Hot patch on the `stable` branch.
1720
1759
  - Docker multi-platform build workflow.
1721
1760
  - Dependabot configuration.
1722
1761
 
1723
- [Unreleased]: https://github.com/evanp/activitypub-bot/compare/v0.53.1...HEAD
1762
+ [Unreleased]: https://github.com/evanp/activitypub-bot/compare/v0.54.2...HEAD
1763
+ [0.54.2]: https://github.com/evanp/activitypub-bot/compare/v0.54.1...v0.54.2
1764
+ [0.54.1]: https://github.com/evanp/activitypub-bot/compare/v0.54.0...v0.54.1
1765
+ [0.54.0]: https://github.com/evanp/activitypub-bot/compare/v0.53.1...v0.54.0
1724
1766
  [0.53.1]: https://github.com/evanp/activitypub-bot/compare/v0.53.0...v0.53.1
1725
1767
  [0.53.0]: https://github.com/evanp/activitypub-bot/compare/v0.52.2...v0.53.0
1726
1768
  [0.52.2]: https://github.com/evanp/activitypub-bot/compare/v0.52.1...v0.52.2
package/README.md CHANGED
@@ -198,7 +198,36 @@ The following bot classes are pre-installed with the server.
198
198
 
199
199
  #### OKBot
200
200
 
201
- An *OKBot* instance will reply to any message that it's mentioned in with the constant string 'OK'.
201
+ An *OKBot* instance replies when mentioned, using "OK" as the default
202
+ response. The shipped `ok` bot replies with "OK" and has the fullname "ok bot".
203
+ Each message receives at most one reply. When the author's WebFinger handle is
204
+ available, the reply starts with a mention of that author.
205
+
206
+ The constructor accepts a username and an optional options object:
207
+
208
+ | Option | Behavior | Default |
209
+ | --- | --- | --- |
210
+ | `content` | A response string or a nonempty array of response strings. An array selects one response at random for each new mention; a string behaves like a one-element array. | `"OK"`. |
211
+ | `language` | A BCP 47 language tag, such as `fr` or `fr-CA`, for the outgoing Note's content. | Untagged. |
212
+ | `fullname` | The bot's display name. | The username followed by " bot". |
213
+ | `description` | The bot's profile description. | See below. |
214
+
215
+ With default content or a configured string, the default description is
216
+ `A bot that says "<response>" when mentioned.` With a configured array,
217
+ including a one-element array, it is
218
+ `A bot that says random things when mentioned.` An explicit description
219
+ overrides either default. With no configured content, the description is
220
+ `A bot that says "OK" when mentioned.`
221
+
222
+ Outgoing Notes always include rendered HTML in `content`. When `language` is
223
+ supplied, they also include the same HTML in `contentMap` under that language
224
+ tag. Language keys are normalized to lowercase, so `fr-CA` is published as
225
+ `fr-ca`.
226
+
227
+ For a Magic 8-Ball-style bot, configure `content` with an array of answers such
228
+ as "Yes", "No", and "Ask again later", and set `fullname` to "Magic 8-Ball".
229
+ Other options supported by [Bot](#bot), such as `icon` and `image`, can also be
230
+ supplied.
202
231
 
203
232
  #### DoNothingBot
204
233
 
package/lib/botcontext.js CHANGED
@@ -118,7 +118,7 @@ export class BotContext {
118
118
  }
119
119
  }
120
120
 
121
- async sendNote (content, { to, cc, bto, bcc, audience, inReplyTo, thread, context, conversation }) {
121
+ async sendNote (content, { to, cc, bto, bcc, audience, inReplyTo, thread, context, conversation, language }) {
122
122
  assert.ok(content)
123
123
  assert.equal(typeof content, 'string')
124
124
  assert.ok(to || cc || bto || bcc || audience)
@@ -129,6 +129,7 @@ export class BotContext {
129
129
  type: 'note',
130
130
  nanoid: noteNanoid
131
131
  }
132
+ const optional = {}
132
133
  if (!inReplyTo) {
133
134
  if (!thread) {
134
135
  thread = this.#formatter.format({ ...idProps, collection: 'thread' })
@@ -140,6 +141,9 @@ export class BotContext {
140
141
  conversation = thread
141
142
  }
142
143
  }
144
+ if (language) {
145
+ optional.contentMap = { [language]: html }
146
+ }
143
147
  const note = await as2.import({
144
148
  '@context': [
145
149
  'https://www.w3.org/ns/activitystreams',
@@ -163,7 +167,8 @@ export class BotContext {
163
167
  thread,
164
168
  'ostatus:conversation': conversation,
165
169
  published: new Date().toISOString(),
166
- attributedTo: this.#formatter.format({ username: this.#botId })
170
+ attributedTo: this.#formatter.format({ username: this.#botId }),
171
+ ...optional
167
172
  })
168
173
  await this.#objectStorage.create(note)
169
174
  await this.#doActivity({
@@ -183,7 +188,7 @@ export class BotContext {
183
188
  return note
184
189
  }
185
190
 
186
- async sendReply (content, object) {
191
+ async sendReply (content, object, language = null) {
187
192
  const r = this.#getRecipients(object)
188
193
  const attributedTo = object.attributedTo?.first?.id
189
194
  if (r.to) {
@@ -211,6 +216,9 @@ export class BotContext {
211
216
  if (conversationProp) {
212
217
  opt.conversation = Array.from(conversationProp)[0]
213
218
  }
219
+ if (language) {
220
+ opt.language = language
221
+ }
214
222
  return await this.sendNote(content, opt)
215
223
  }
216
224
 
@@ -348,6 +356,12 @@ export class BotContext {
348
356
  assert.equal(typeof content, 'string')
349
357
  const exported = await note.export({ useOriginalContext: true })
350
358
  exported.content = content
359
+ if ('contentMap' in exported) {
360
+ const keys = Object.keys(exported.contentMap)
361
+ if (keys.length === 1) {
362
+ exported.contentMap[keys[0]] = content
363
+ }
364
+ }
351
365
  const updated = await as2.import(exported)
352
366
  const { to, cc, bto, bcc, audience } = this.#getRecipients(note)
353
367
  await this.#objectStorage.update(updated)
package/lib/bots/ok.js CHANGED
@@ -1,12 +1,29 @@
1
1
  import Bot from '../bot.js'
2
2
 
3
3
  export default class OKBot extends Bot {
4
- get fullname () {
5
- return 'OK Bot'
6
- }
4
+ #content
5
+ #language
7
6
 
8
- get description () {
9
- return 'A bot that says "OK" when mentioned.'
7
+ constructor (username, options = {}) {
8
+ const says = ('content' in options)
9
+ ? Array.isArray(options.content)
10
+ ? 'random things'
11
+ : `"${options.content}"`
12
+ : '"OK"'
13
+ options = {
14
+ fullname: `${username} bot`,
15
+ description: `A bot that says ${says} when mentioned.`,
16
+ ...options
17
+ }
18
+ super(username, options)
19
+ this.#content = ('content' in options)
20
+ ? (Array.isArray(options.content))
21
+ ? options.content
22
+ : [options.content]
23
+ : ['OK']
24
+ this.#language = ('language' in options)
25
+ ? options.language
26
+ : null
10
27
  }
11
28
 
12
29
  async onMention (object, activity) {
@@ -27,14 +44,16 @@ export default class OKBot extends Bot {
27
44
  'attributed to'
28
45
  )
29
46
  const wf = await this._context.toWebfinger(attributedTo)
30
- const content = (wf) ? `@${wf} OK` : 'OK'
47
+ const response = this.#response()
48
+ const content = (wf) ? `@${wf} ${response}` : response
31
49
  this._context.logger.info({
32
50
  object: object.id,
33
51
  attributedTo,
34
52
  wf,
35
53
  content
36
54
  }, 'sending reply')
37
- const reply = await this._context.sendReply(content, object)
55
+
56
+ const reply = await this._context.sendReply(content, object, this.#language)
38
57
  this._context.logger.info({
39
58
  reply: reply.id,
40
59
  content,
@@ -55,4 +74,9 @@ export default class OKBot extends Bot {
55
74
  const key = `seen:${id}`
56
75
  return this._context.setData(key, true)
57
76
  }
77
+
78
+ #response () {
79
+ const idx = Math.floor(Math.random() * this.#content.length)
80
+ return this.#content[idx]
81
+ }
58
82
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evanp/activitypub-bot",
3
- "version": "0.53.1",
3
+ "version": "0.54.2",
4
4
  "description": "server-side ActivityPub bot framework",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",