@tusile/bot 0.5.0 → 0.7.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 +3 -1
- package/package.json +33 -33
- package/src/bot.js +41 -0
- package/src/core.js +13 -0
- package/src/permissions.js +2 -0
package/README.md
CHANGED
|
@@ -93,6 +93,8 @@ app before the bot is ever asked anything.
|
|
|
93
93
|
|
|
94
94
|
The command list is registered on every connect, so a redeploy with a changed set takes
|
|
95
95
|
effect immediately and a bot added to a community while it was down still registers.
|
|
96
|
+
Since 0.6.0 the same list is also sent to Core, so the bot directory shows your commands to
|
|
97
|
+
somebody deciding whether to add the bot.
|
|
96
98
|
|
|
97
99
|
**Who may run it.** A command that acts on the server rather than answering a question can
|
|
98
100
|
say what the person running it must hold:
|
|
@@ -305,7 +307,7 @@ bot.command({ name: 'setup', description: 'Build the standard channels' }, async
|
|
|
305
307
|
`view_channel`, `send_messages`, `manage_messages`, `manage_channels`, `manage_roles`,
|
|
306
308
|
`manage_members`, `administrator`, `connect`, `speak`, `mute_members`, `deafen_members`,
|
|
307
309
|
`manage_server`, `create_invite`, `attach_files`, `stream`, `use_camera`, `manage_emojis`,
|
|
308
|
-
`manage_emoji_policy`, `timeout_members`. A name this package does not know throws, rather
|
|
310
|
+
`manage_emoji_policy`, `timeout_members`, `use_soundboard`, `manage_soundboard`. A name this package does not know throws, rather
|
|
309
311
|
than quietly denying nothing.
|
|
310
312
|
|
|
311
313
|
Two rules the server keeps, whatever the bot asks for: it cannot create a role above its
|
package/package.json
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@tusile/bot",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Write a Tusile bot: slash commands, buttons, forms and voice, over an outbound WebSocket.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "src/index.js",
|
|
7
|
-
"exports": {
|
|
8
|
-
".": "./src/index.js"
|
|
9
|
-
},
|
|
10
|
-
"files": [
|
|
11
|
-
"src",
|
|
12
|
-
"README.md",
|
|
13
|
-
"LICENSE"
|
|
14
|
-
],
|
|
15
|
-
"engines": {
|
|
16
|
-
"node": ">=20"
|
|
17
|
-
},
|
|
18
|
-
"scripts": {
|
|
19
|
-
"test": "node --test"
|
|
20
|
-
},
|
|
21
|
-
"dependencies": {
|
|
22
|
-
"ws": "^8.18.0"
|
|
23
|
-
},
|
|
24
|
-
"keywords": [
|
|
25
|
-
"tusile",
|
|
26
|
-
"bot",
|
|
27
|
-
"chat",
|
|
28
|
-
"slash-commands"
|
|
29
|
-
],
|
|
30
|
-
"homepage": "https://tusile.com/bots",
|
|
31
|
-
"license": "MIT",
|
|
32
|
-
"author": "Tusile"
|
|
33
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@tusile/bot",
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "Write a Tusile bot: slash commands, buttons, forms and voice, over an outbound WebSocket.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"src",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=20"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"test": "node --test"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"ws": "^8.18.0"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"tusile",
|
|
26
|
+
"bot",
|
|
27
|
+
"chat",
|
|
28
|
+
"slash-commands"
|
|
29
|
+
],
|
|
30
|
+
"homepage": "https://tusile.com/bots",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"author": "Tusile"
|
|
33
|
+
}
|
package/src/bot.js
CHANGED
|
@@ -98,6 +98,10 @@ export class Bot {
|
|
|
98
98
|
#stopped = false;
|
|
99
99
|
#declared;
|
|
100
100
|
#declaredOk = false;
|
|
101
|
+
// The command list Core last accepted for the directory, as JSON, so it is sent again only
|
|
102
|
+
// when it changes. Its own tries, so a Core without the route cannot use up the permissions'.
|
|
103
|
+
#publishedCommands = null;
|
|
104
|
+
#publishTries = 0;
|
|
101
105
|
#declareTries = 0;
|
|
102
106
|
#watching = null;
|
|
103
107
|
#pollBackoff = 0;
|
|
@@ -235,6 +239,43 @@ export class Bot {
|
|
|
235
239
|
* of, and refusing to run without it would be worse than running without it.
|
|
236
240
|
*/
|
|
237
241
|
async #declare() {
|
|
242
|
+
// Each step is awaited only when it has something to send, so a pass with nothing to say
|
|
243
|
+
// costs the loop no extra turns before its next request.
|
|
244
|
+
if (this.#commandsToPublish() !== null) await this.#publishCommands();
|
|
245
|
+
if (this.#declared !== null && !this.#declaredOk && this.#declareTries < DECLARE_TRIES) {
|
|
246
|
+
await this.#declarePermissions();
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** The command list as JSON when Core has not yet taken it, else null. */
|
|
251
|
+
#commandsToPublish() {
|
|
252
|
+
if (this.#publishTries >= DECLARE_TRIES) return null;
|
|
253
|
+
const json = JSON.stringify(this.#dispatcher.definitions());
|
|
254
|
+
return json === this.#publishedCommands ? null : json;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Tell Core what commands this bot offers, for the bot directory.
|
|
259
|
+
*
|
|
260
|
+
* Without it the directory could show a name and a line and nothing else, and the only way
|
|
261
|
+
* to find out what a bot does was to add it somewhere. Sent again whenever the list differs
|
|
262
|
+
* from the one Core last took, and retried like the declaration below. Never fatal: a Core
|
|
263
|
+
* that predates the route answers 404, and the bot runs the same either way.
|
|
264
|
+
*/
|
|
265
|
+
async #publishCommands() {
|
|
266
|
+
const json = this.#commandsToPublish();
|
|
267
|
+
if (json === null) return;
|
|
268
|
+
this.#publishTries += 1;
|
|
269
|
+
try {
|
|
270
|
+
await this.#core.publishCommands(JSON.parse(json));
|
|
271
|
+
this.#publishedCommands = json;
|
|
272
|
+
this.#publishTries = 0;
|
|
273
|
+
} catch (err) {
|
|
274
|
+
if (this.#publishTries >= DECLARE_TRIES) this.#report(err);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async #declarePermissions() {
|
|
238
279
|
// Nothing to say, or already said. A bot that declares nothing must not overwrite what its
|
|
239
280
|
// author ticked in the app with an empty list.
|
|
240
281
|
if (this.#declared === null || this.#declaredOk) return;
|
package/src/core.js
CHANGED
|
@@ -108,6 +108,19 @@ export class CoreClient {
|
|
|
108
108
|
});
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
/**
|
|
112
|
+
* Tell Core what commands this bot offers, so the bot directory can show them to somebody
|
|
113
|
+
* deciding whether to add it.
|
|
114
|
+
*
|
|
115
|
+
* The same definitions every community gets. Core keeps only names, descriptions and option
|
|
116
|
+
* schemas for display; the copy each community holds is still the one that is run.
|
|
117
|
+
*/
|
|
118
|
+
publishCommands(definitions) {
|
|
119
|
+
return this.#transport.request('PUT', '/bots/me/commands', {
|
|
120
|
+
commands: definitions,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
111
124
|
/**
|
|
112
125
|
* A token for one community.
|
|
113
126
|
*
|