@rhizogrip/spore-plugin-telegram 0.8.1 → 0.9.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
@@ -121,6 +121,15 @@ It is then shown `reading data/report.md`, `running git log --oneline -5`,
121
121
  `source forgejo --path …` — the first line, cut short. Choose it for a chat you would let
122
122
  read the commands; the others keep the kinds. A group takes `"details": true` the same way.
123
123
 
124
+ A bot that serves people who should see their answers and not the machinery — your customers, say —
125
+ takes `"brief": true` beside `people` and `groups`: each chat is then told its spore's state and its
126
+ answers, and nothing of how it runs. `/status` says `working`, `idle`, `paused` or how many messages
127
+ wait — never the model, the spend, the turns or the limits; an answer comes without how the turn
128
+ ended, a turn with none says `no answer this time` and the log says why; a limit reached shows as
129
+ `wrapping up`; and `/extend` is not offered, since what a turn may spend is yours. A person or a
130
+ group takes `"brief"` as well, `false` to be told everything in a brief bot; `details` is refused
131
+ beside it.
132
+
124
133
  A chat can be shown a turn more quietly, which is what a group gets unless its entry says
125
134
  otherwise: `"show": "react"` puts a 👨‍💻 on your message when the spore takes it and shows Telegram's
126
135
  `typing…` while the turn runs, then takes the 👨‍💻 off when the answer arrives — or puts a 💔 there
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rhizogrip/spore-plugin-telegram",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "description": "Telegram for spores: a bot of yours in chats, each chat reaching its spore, run as a service",
5
5
  "author": {
6
6
  "name": "Christophe Le Bars",
@@ -31,13 +31,13 @@
31
31
  "marked": "^18.0.11"
32
32
  },
33
33
  "peerDependencies": {
34
- "@rhizogrip/spore": "^1.21.0"
34
+ "@rhizogrip/spore": "^1.23.0"
35
35
  },
36
36
  "scripts": {
37
37
  "test": "vitest run"
38
38
  },
39
39
  "spore": {
40
- "works": "^1.21.0",
40
+ "works": "^1.23.0",
41
41
  "services": {
42
42
  "telegram": {
43
43
  "what": "a bot of yours in Telegram chats, each chat reaching its spore",
package/src/adapter.js CHANGED
@@ -27,8 +27,8 @@ const REACTIONS = [
27
27
  ]
28
28
 
29
29
  const FIELDS = ['token', 'people', 'groups', 'react']
30
- const PERSON_FIELDS = ['spore', 'details', 'show']
31
- const GROUP_FIELDS = ['spores', 'people', 'details', 'show', 'overhear']
30
+ const PERSON_FIELDS = ['spore', 'details', 'show', 'brief']
31
+ const GROUP_FIELDS = ['spores', 'people', 'details', 'show', 'overhear', 'brief']
32
32
  const PERSON = /^\d+$/
33
33
  const GROUP = /^-\d+$/
34
34
 
@@ -43,6 +43,8 @@ const strays = (path, kind, id, entry, takes) => {
43
43
  throw invalid(path, `gives ${kind} ${id} ${extra.join(', ')}, which a ${kind} does not take`, `a ${kind} takes ${takes.join(', ')}${bots.length ? `; ${bots.join(', ')} is the bot's, beside people and groups` : ''}`)
44
44
  }
45
45
 
46
+ const briefOf = (entry) => typeof entry.brief === 'boolean' ? entry.brief : null
47
+
46
48
  const flags = (path, kind, id, entry, names) => {
47
49
  for (const name of names) {
48
50
  if (entry[name] !== undefined && typeof entry[name] !== 'boolean') throw invalid(path, `has a ${kind} ${id} whose ${name} is not true or false`)
@@ -50,13 +52,14 @@ const flags = (path, kind, id, entry, names) => {
50
52
  }
51
53
 
52
54
  /**
53
- * The private chats, `people: { "<person id>": "<spore>" }` or `{ "spore", "details", "show" }`,
54
- * and the groups, `groups: { "<group id>": { "spores": ["<spore>"], "people": [...], "details",
55
- * "show", "overhear" } }`. `details` shows a chat what each call does — its command or path — rather
56
- * than only what kind of call it is, since a Telegram message leaves the machine; `show` how a turn
57
- * shows itself there. A group names who may address its spore — user ids, or `*` for everyone in it
58
- * — and has one spore: in a group a bot speaks as itself, so a second spore is a second bot.
59
- * `overhear` gives the spore what they say there that is not to it.
55
+ * The private chats, `people: { "<person id>": "<spore>" }` or `{ "spore", "details", "show",
56
+ * "brief" }`, and the groups, `groups: { "<group id>": { "spores": ["<spore>"], "people": [...],
57
+ * "details", "show", "overhear", "brief" } }`. `details` shows a chat what each call does — its
58
+ * command or path — rather than only what kind of call it is, since a Telegram message leaves the
59
+ * machine; `show` how a turn shows itself there; `brief` the bot's own, for this chat. A group names
60
+ * who may address its spore — user ids, or `*` for everyone in it — and has one spore: in a group a
61
+ * bot speaks as itself, so a second spore is a second bot. `overhear` gives the spore what they say
62
+ * there that is not to it.
60
63
  */
61
64
  function routes (raw, path) {
62
65
  if (raw.people === undefined && raw.groups === undefined) {
@@ -73,28 +76,29 @@ function routes (raw, path) {
73
76
  const shaped = typeof entry === 'object' && entry !== null && !Array.isArray(entry)
74
77
  if (shaped) {
75
78
  strays(path, 'person', person, entry, PERSON_FIELDS)
76
- flags(path, 'person', person, entry, ['details'])
79
+ flags(path, 'person', person, entry, ['details', 'brief'])
77
80
  }
78
81
  const spore = shaped ? entry.spore : entry
79
82
  if (typeof spore !== 'string' || !spore) throw invalid(path, `maps person ${person} to something that is not a spore`, 'each person is their id against the name of the spore they reach, or { "spore": "<name>", "details": true, "show": "react" }')
80
- table.set(person, { spores: [spore], details: shaped && entry.details === true, show: shaped ? entry.show ?? null : null, people: null, overhear: false })
83
+ table.set(person, { spores: [spore], details: shaped && entry.details === true, show: shaped ? entry.show ?? null : null, brief: shaped ? briefOf(entry) : null, people: null, overhear: false })
81
84
  }
82
85
  for (const [group, entry] of entries('groups')) {
83
86
  if (!GROUP.test(group)) throw invalid(path, `names "${group}" among its groups, which is not a group's id`, 'a group\'s id is negative (-100… for a supergroup); a person\'s goes under people')
84
87
  if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) throw invalid(path, `has a group ${group} that is not { "spores": [...], "people": [...] }`)
85
88
  strays(path, 'group', group, entry, GROUP_FIELDS)
86
- flags(path, 'group', group, entry, ['details', 'overhear'])
89
+ flags(path, 'group', group, entry, ['details', 'overhear', 'brief'])
87
90
  if (!Array.isArray(entry.spores) || !entry.spores.length || !entry.spores.every(spore => typeof spore === 'string' && spore)) throw invalid(path, `has a group ${group} with no spores`)
88
91
  if (entry.spores.length > 1) throw invalid(path, `gives group ${group} ${entry.spores.length} spores`, 'a bot speaks in a group as itself, so a group has one spore — a second spore there is a second bot')
89
92
  if (!Array.isArray(entry.people) || !entry.people.every(who => who === '*' || (typeof who === 'string' && PERSON.test(who)))) {
90
93
  throw invalid(path, `has a group ${group} whose people are not user ids or *`, 'people is who may address its spore — ["<user id>", …], or ["*"] for everyone in the group, written out')
91
94
  }
92
- table.set(group, { spores: entry.spores, details: entry.details === true, show: entry.show ?? null, people: entry.people, overhear: entry.overhear === true })
95
+ table.set(group, { spores: entry.spores, details: entry.details === true, show: entry.show ?? null, brief: briefOf(entry), people: entry.people, overhear: entry.overhear === true })
93
96
  }
94
97
  return table
95
98
  }
96
99
 
97
- const HELP = 'Anything you write to me in private goes to the spore; in a group, mention me or reply to me. /status says what it is doing; /stop ends the turn it is running and sets aside what you sent that it had not read; /wrap [10m] asks it to wrap up; /extend wall 30m gives it more, as far as its spore.cmk allows.'
100
+ const HELP = 'Anything you write to me in private goes to the spore; in a group, mention me or reply to me. /status says what it is doing; /stop ends the turn it is running and sets aside what you sent that it had not read; /wrap [10m] asks it to wrap up'
101
+ const EXTEND = '/extend wall 30m gives it more, as far as its spore.cmk allows'
98
102
 
99
103
  export const TELEGRAM = {
100
104
  kind: 'telegram',
@@ -107,7 +111,7 @@ export const TELEGRAM = {
107
111
  refusals: ['TELEGRAM_CONFLICT'],
108
112
  example: SHAPE,
109
113
  routes,
110
- usage: { help: HELP, extend: '/extend wall 30m, /extend cost 1.50 or /extend rounds 20' },
114
+ usage: { help: `${HELP}; ${EXTEND}.`, briefHelp: `${HELP}.`, extend: '/extend wall 30m, /extend cost 1.50 or /extend rounds 20' },
111
115
  limits: { message: 4096, file: 50 * 1024 * 1024, editEveryMs: 2500, typingEveryMs: 4000 },
112
116
  can: { upload: true, remove: true, react: true, typing: true, reply: true },
113
117
  showShared: 'react',