@volter/twin-standard 1.0.78 → 1.0.79
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/dist/src/clients/slack.js +14 -9
- package/package.json +2 -2
- package/src/clients/slack.ts +12 -9
|
@@ -4,13 +4,18 @@ const UPLOADS = 'https://docs.slack.dev/messaging/working-with-files';
|
|
|
4
4
|
const MESSAGE_FIELDS = new Set(['type', 'subtype', 'channel', 'user', 'text', 'ts', 'event_ts', 'channel_type', 'thread_ts', 'parent_user_id', 'blocks', 'team', 'client_msg_id', 'bot_id', 'app_id', 'bot_profile', 'attachments', 'files', 'edited', 'username', 'icons', 'upload', 'display_as_bot']);
|
|
5
5
|
/** A workspace, a channel, and an app with Socket Mode subscribed to the channel's messages, installed by the workspace's
|
|
6
6
|
* owner through Slack's own pages and API, as a team sets one up. */
|
|
7
|
-
async function install(c) {
|
|
7
|
+
async function install(c, channelName) {
|
|
8
8
|
const person = 'xoxp-UMAYA';
|
|
9
9
|
const call = async (url, body, token) => c.fetch(url, { method: 'POST', redirect: 'manual', headers: { 'content-type': 'application/json', ...(token ? { authorization: `Bearer ${token}` } : {}) }, body: JSON.stringify(body) });
|
|
10
10
|
const json = async (r) => (await r.json());
|
|
11
|
-
await call('https://slack.com/get-started', { email: 'maya@kiln.studio', name: 'Maya Okafor' }, person);
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
const workspace = await call('https://slack.com/get-started', { email: 'maya@kiln.studio', name: 'Maya Okafor' }, person);
|
|
12
|
+
if (workspace.status !== 201 && workspace.status !== 409)
|
|
13
|
+
throw new Error(`workspace setup answered ${workspace.status}`);
|
|
14
|
+
const identity = await json(await call('https://slack.com/api/auth.test', {}, person));
|
|
15
|
+
if (!identity.ok || typeof identity.team_id !== 'string')
|
|
16
|
+
throw new Error(`auth.test did not identify the workspace: ${JSON.stringify(identity)}`);
|
|
17
|
+
const channel = (await json(await call('https://slack.com/api/conversations.create', { name: channelName }, person))).channel?.id;
|
|
18
|
+
const config = /access token is (xoxe\.xoxp-[A-Za-z0-9-]+)/.exec(await (await call('https://api.slack.com/apps', { team: identity.team_id }, person)).text())?.[1];
|
|
14
19
|
if (!channel || !config)
|
|
15
20
|
throw new Error(`the workspace or its app configuration token was not made (channel ${channel}, token ${config})`);
|
|
16
21
|
const app = await json(await call('https://slack.com/api/apps.manifest.create', { manifest: {
|
|
@@ -38,10 +43,10 @@ export const cases = [
|
|
|
38
43
|
asks: "an app's Socket Mode client hears a person's message in its bot's channel as Slack documents the event, with nothing else in it",
|
|
39
44
|
source: `${MESSAGE} "The semantics for this event"`,
|
|
40
45
|
async run(c) {
|
|
41
|
-
const installed = await install(c);
|
|
46
|
+
const installed = await install(c, 'conformance-socket');
|
|
42
47
|
const { SocketModeClient } = await import('@slack/socket-mode');
|
|
43
48
|
const { WebClient, LogLevel } = await import('@slack/web-api');
|
|
44
|
-
const socket = new SocketModeClient({ appToken: installed.appToken, logLevel: LogLevel.ERROR
|
|
49
|
+
const socket = new SocketModeClient({ appToken: installed.appToken, logLevel: LogLevel.ERROR });
|
|
45
50
|
const heard = new Promise((resolve) => {
|
|
46
51
|
const timer = setTimeout(() => resolve('no message event in 10 s'), 10_000);
|
|
47
52
|
socket.on('message', async ({ event, ack }) => { await ack(); clearTimeout(timer); resolve(event); });
|
|
@@ -49,7 +54,7 @@ export const cases = [
|
|
|
49
54
|
const started = await socket.start().then(() => undefined, (e) => e.message);
|
|
50
55
|
if (started)
|
|
51
56
|
return [`the Socket Mode client did not connect: ${started}`];
|
|
52
|
-
const person = new WebClient(installed.person, {
|
|
57
|
+
const person = new WebClient(installed.person, { logLevel: LogLevel.ERROR });
|
|
53
58
|
await person.chat.postMessage({ channel: installed.channel, text: 'The client brief is in.' });
|
|
54
59
|
const event = await heard;
|
|
55
60
|
await socket.disconnect().catch(() => undefined);
|
|
@@ -72,9 +77,9 @@ export const cases = [
|
|
|
72
77
|
asks: "an app's files.uploadV2 sends a file's bytes to the upload URL Slack answers and completes the upload into a thread, where the file reads back with its size",
|
|
73
78
|
source: `${UPLOADS} "Post the contents of your file to the URL returned by the files.getUploadURLExternal API method"`,
|
|
74
79
|
async run(c) {
|
|
75
|
-
const installed = await install(c);
|
|
80
|
+
const installed = await install(c, 'conformance-files');
|
|
76
81
|
const { WebClient, LogLevel } = await import('@slack/web-api');
|
|
77
|
-
const bot = new WebClient(installed.botToken, {
|
|
82
|
+
const bot = new WebClient(installed.botToken, { logLevel: LogLevel.ERROR });
|
|
78
83
|
const root = await bot.chat.postMessage({ channel: installed.channel, text: 'Render finished' });
|
|
79
84
|
if (!root.ts)
|
|
80
85
|
return [`the thread's first message was not posted: ${JSON.stringify(root).slice(0, 160)}`];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/twin-standard",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.79",
|
|
4
4
|
"description": "The Protocol 3 standard for Volter twin packs: the criteria a pack is graded by, the grader, the gate a pack repository and the twin catalog run on every change and submission, and the spec derivation the grade checks against. Pin a version: every place a pack is judged judges by that standard.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"twin",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"yaml": "2.9.0"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
|
-
"@volter/world-core": "3.0.
|
|
78
|
+
"@volter/world-core": "3.0.87",
|
|
79
79
|
"@volter/world-runtime": "*",
|
|
80
80
|
"playwright": "^1.63.0"
|
|
81
81
|
},
|
package/src/clients/slack.ts
CHANGED
|
@@ -13,13 +13,16 @@ type Installed = { channel: string; botUser: string; botToken: string; appToken:
|
|
|
13
13
|
|
|
14
14
|
/** A workspace, a channel, and an app with Socket Mode subscribed to the channel's messages, installed by the workspace's
|
|
15
15
|
* owner through Slack's own pages and API, as a team sets one up. */
|
|
16
|
-
async function install(c: ClientContext): Promise<Installed> {
|
|
16
|
+
async function install(c: ClientContext, channelName: string): Promise<Installed> {
|
|
17
17
|
const person = 'xoxp-UMAYA';
|
|
18
18
|
const call = async (url: string, body: unknown, token?: string): Promise<Response> => c.fetch(url, { method: 'POST', redirect: 'manual', headers: { 'content-type': 'application/json', ...(token ? { authorization: `Bearer ${token}` } : {}) }, body: JSON.stringify(body) });
|
|
19
19
|
const json = async (r: Response): Promise<Record<string, any>> => (await r.json()) as Record<string, any>;
|
|
20
|
-
await call('https://slack.com/get-started', { email: 'maya@kiln.studio', name: 'Maya Okafor' }, person);
|
|
21
|
-
|
|
22
|
-
const
|
|
20
|
+
const workspace = await call('https://slack.com/get-started', { email: 'maya@kiln.studio', name: 'Maya Okafor' }, person);
|
|
21
|
+
if (workspace.status !== 201 && workspace.status !== 409) throw new Error(`workspace setup answered ${workspace.status}`);
|
|
22
|
+
const identity = await json(await call('https://slack.com/api/auth.test', {}, person));
|
|
23
|
+
if (!identity.ok || typeof identity.team_id !== 'string') throw new Error(`auth.test did not identify the workspace: ${JSON.stringify(identity)}`);
|
|
24
|
+
const channel = (await json(await call('https://slack.com/api/conversations.create', { name: channelName }, person))).channel?.id as string;
|
|
25
|
+
const config = /access token is (xoxe\.xoxp-[A-Za-z0-9-]+)/.exec(await (await call('https://api.slack.com/apps', { team: identity.team_id }, person)).text())?.[1];
|
|
23
26
|
if (!channel || !config) throw new Error(`the workspace or its app configuration token was not made (channel ${channel}, token ${config})`);
|
|
24
27
|
const app = await json(await call('https://slack.com/api/apps.manifest.create', { manifest: {
|
|
25
28
|
display_information: { name: 'Conformance Bot' },
|
|
@@ -44,17 +47,17 @@ export const cases: ClientCase[] = [
|
|
|
44
47
|
asks: "an app's Socket Mode client hears a person's message in its bot's channel as Slack documents the event, with nothing else in it",
|
|
45
48
|
source: `${MESSAGE} "The semantics for this event"`,
|
|
46
49
|
async run(c) {
|
|
47
|
-
const installed = await install(c);
|
|
50
|
+
const installed = await install(c, 'conformance-socket');
|
|
48
51
|
const { SocketModeClient } = await import('@slack/socket-mode');
|
|
49
52
|
const { WebClient, LogLevel } = await import('@slack/web-api');
|
|
50
|
-
const socket = new SocketModeClient({ appToken: installed.appToken, logLevel: LogLevel.ERROR
|
|
53
|
+
const socket = new SocketModeClient({ appToken: installed.appToken, logLevel: LogLevel.ERROR });
|
|
51
54
|
const heard = new Promise<Record<string, unknown> | string>((resolve) => {
|
|
52
55
|
const timer = setTimeout(() => resolve('no message event in 10 s'), 10_000);
|
|
53
56
|
socket.on('message', async ({ event, ack }: { event: Record<string, unknown>; ack: () => Promise<void> }) => { await ack(); clearTimeout(timer); resolve(event); });
|
|
54
57
|
});
|
|
55
58
|
const started = await socket.start().then(() => undefined, (e: Error) => e.message);
|
|
56
59
|
if (started) return [`the Socket Mode client did not connect: ${started}`];
|
|
57
|
-
const person = new WebClient(installed.person, {
|
|
60
|
+
const person = new WebClient(installed.person, { logLevel: LogLevel.ERROR });
|
|
58
61
|
await person.chat.postMessage({ channel: installed.channel, text: 'The client brief is in.' });
|
|
59
62
|
const event = await heard;
|
|
60
63
|
await socket.disconnect().catch(() => undefined);
|
|
@@ -71,9 +74,9 @@ export const cases: ClientCase[] = [
|
|
|
71
74
|
asks: "an app's files.uploadV2 sends a file's bytes to the upload URL Slack answers and completes the upload into a thread, where the file reads back with its size",
|
|
72
75
|
source: `${UPLOADS} "Post the contents of your file to the URL returned by the files.getUploadURLExternal API method"`,
|
|
73
76
|
async run(c) {
|
|
74
|
-
const installed = await install(c);
|
|
77
|
+
const installed = await install(c, 'conformance-files');
|
|
75
78
|
const { WebClient, LogLevel } = await import('@slack/web-api');
|
|
76
|
-
const bot = new WebClient(installed.botToken, {
|
|
79
|
+
const bot = new WebClient(installed.botToken, { logLevel: LogLevel.ERROR });
|
|
77
80
|
const root = await bot.chat.postMessage({ channel: installed.channel, text: 'Render finished' });
|
|
78
81
|
if (!root.ts) return [`the thread's first message was not posted: ${JSON.stringify(root).slice(0, 160)}`];
|
|
79
82
|
const bytes = Buffer.from('frame,seconds\n1,0.42\n2,0.40\n');
|