Skip to content
This repository was archived by the owner on Dec 9, 2022. It is now read-only.

Commit 4c9832e

Browse files
committed
Update
1 parent 467d67a commit 4c9832e

14 files changed

+289
-45
lines changed

README.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,17 @@ slack create my-deepl-slack-app -t seratch/run-on-slack-deepl
2020
cd my-deepl-slack-app/
2121
```
2222

23-
### 1. Enable reaction_added trigger
23+
### 1. Enable Setup Workflow
2424

25-
First off, open `triggers/reaction_added.ts` source file and modify it to have a
26-
valid list of channel IDs to enable this app. And then, run the following CLI
27-
command:
25+
First off, let's enable the "setup" workflow!
2826

2927
```bash
3028
slack deploy
31-
slack trigger create --trigger-def triggers/reaction_added.ts
29+
slack trigger create --trigger-def triggers/setup.ts
3230
```
3331

32+
You will get a URL (e.g., `https://slack.com/shortcuts/Ft***/****`) to invoke the setup workflow. Once you can share the URL in your Slack workspace, any users in the workspace can enable the translator app in any public channels.
33+
3434
### 2. Set DeepL API key to the app
3535

3636
Add your DeepL API key to the app env variables:

functions/lang_selector.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { DefineFunction, Schema } from "deno-slack-sdk/mod.ts";
22
import { SlackFunctionHandler } from "deno-slack-sdk/types.ts";
33
import { reactionToLang } from "./languages.ts";
4-
import { getLogger } from "../utils/logger.ts";
5-
import { resolveFunctionSourceFile } from "../utils/source_file_resoluion.ts";
4+
import { Logger } from "../utils/logger.ts";
5+
import { FunctionSourceFile } from "../utils/function_source_file.ts";
66

77
export const def = DefineFunction({
88
callback_id: "lang_selector",
99
title: "Language selector",
1010
description: "A funtion to identify the language to translate into",
11-
source_file: resolveFunctionSourceFile(import.meta.url),
11+
source_file: FunctionSourceFile(import.meta.url),
1212
input_parameters: {
1313
properties: {
1414
reaction: {
@@ -31,7 +31,7 @@ const handler: SlackFunctionHandler<typeof def.definition> = async ({
3131
inputs,
3232
env,
3333
}) => {
34-
const logger = await getLogger(env.logLevel);
34+
const logger = Logger(env.LOG_LEVEL);
3535
logger.debug(`lang_selector inputs: ${JSON.stringify(inputs)}`);
3636
const reactionName = inputs.reaction;
3737
let lang = undefined;

functions/setup.ts

+201
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
import { DefineFunction, Schema, SlackFunction } from "deno-slack-sdk/mod.ts";
2+
import { SlackAPI } from "deno-slack-api/mod.ts";
3+
import { Logger } from "../utils/logger.ts";
4+
import { FunctionSourceFile } from "../utils/function_source_file.ts";
5+
6+
/**
7+
* See https://api.slack.com/future/functions/custom
8+
*/
9+
export const def = DefineFunction({
10+
callback_id: "manage-reaction-added-event-trigger",
11+
title: "Manage a reaction_added event trigger",
12+
source_file: FunctionSourceFile(import.meta.url),
13+
input_parameters: {
14+
properties: {
15+
interactivity: { type: Schema.slack.types.interactivity },
16+
workflowCallbackId: { type: Schema.types.string },
17+
},
18+
required: ["interactivity", "workflowCallbackId"],
19+
},
20+
output_parameters: {
21+
properties: {},
22+
required: [],
23+
},
24+
});
25+
26+
export default SlackFunction(def, async ({
27+
inputs,
28+
env,
29+
token,
30+
}) => {
31+
const logger = Logger(env.logLevel);
32+
logger.debug(inputs);
33+
34+
const client = SlackAPI(token);
35+
const allTriggers = await client.workflows.triggers.list({});
36+
logger.info(allTriggers);
37+
let triggerToUpdate = undefined;
38+
// find the trigger to update
39+
if (allTriggers.triggers) {
40+
for (const trigger of allTriggers.triggers) {
41+
if (
42+
trigger.workflow.callback_id === inputs.workflowCallbackId &&
43+
trigger.event_type === "slack#/events/reaction_added"
44+
) {
45+
triggerToUpdate = trigger;
46+
}
47+
}
48+
}
49+
const channelIds = triggerToUpdate?.channel_ids != undefined
50+
? triggerToUpdate.channel_ids
51+
: [];
52+
await client.views.open({
53+
interactivity_pointer: inputs.interactivity.interactivity_pointer,
54+
view: {
55+
"type": "modal",
56+
"callback_id": "configure-workflow",
57+
"title": {
58+
"type": "plain_text",
59+
"text": "Workflow Configuration",
60+
},
61+
"notify_on_close": true,
62+
"submit": {
63+
"type": "plain_text",
64+
"text": "Confirm",
65+
},
66+
"blocks": [
67+
{
68+
"type": "input",
69+
"block_id": "block",
70+
"element": {
71+
"type": "multi_channels_select",
72+
"placeholder": {
73+
"type": "plain_text",
74+
"text": "Select channels to add",
75+
},
76+
"initial_channels": channelIds,
77+
"action_id": "channels",
78+
},
79+
"label": {
80+
"type": "plain_text",
81+
"text": "Channels to enable the workflow",
82+
},
83+
},
84+
],
85+
},
86+
});
87+
return {
88+
completed: false,
89+
};
90+
})
91+
.addViewSubmissionHandler(
92+
["configure-workflow"],
93+
async ({ view, inputs, env, token }) => {
94+
const logger = Logger(env.logLevel);
95+
const { workflowCallbackId } = inputs;
96+
const channelIds = view.state.values.block.channels.selected_channels;
97+
const triggerInputs = {
98+
channelId: {
99+
value: "{{data.channel_id}}",
100+
},
101+
messageTs: {
102+
value: "{{data.message_ts}}",
103+
},
104+
reaction: {
105+
value: "{{data.reaction}}",
106+
},
107+
};
108+
109+
const client = SlackAPI(token);
110+
const allTriggers = await client.workflows.triggers.list({});
111+
let modalMessage = "The configuration is done!";
112+
try {
113+
let triggerToUpdate = undefined;
114+
// find the trigger to update
115+
if (allTriggers.triggers) {
116+
for (const trigger of allTriggers.triggers) {
117+
if (
118+
trigger.workflow.callback_id === workflowCallbackId &&
119+
trigger.event_type === "slack#/events/reaction_added"
120+
) {
121+
triggerToUpdate = trigger;
122+
break;
123+
}
124+
}
125+
}
126+
127+
if (triggerToUpdate === undefined) {
128+
const creation = await client.workflows.triggers.create({
129+
type: "event",
130+
name: "reaction_added event trigger",
131+
workflow: `#/workflows/${workflowCallbackId}`,
132+
event: {
133+
event_type: "slack#/events/reaction_added",
134+
channel_ids: channelIds,
135+
},
136+
inputs: triggerInputs,
137+
});
138+
logger.info(`A new trigger created: ${JSON.stringify(creation)}`);
139+
} else {
140+
const update = await client.workflows.triggers.update({
141+
trigger_id: triggerToUpdate.id,
142+
type: "event",
143+
name: "reaction_added event trigger",
144+
workflow: `#/workflows/${workflowCallbackId}`,
145+
event: {
146+
event_type: "slack#/events/reaction_added",
147+
channel_ids: channelIds,
148+
},
149+
inputs: triggerInputs,
150+
});
151+
logger.info(`A new trigger updated: ${JSON.stringify(update)}`);
152+
}
153+
for (const channelId of channelIds) {
154+
const joinResult = await client.conversations.join({
155+
channel: channelId,
156+
});
157+
logger.debug(joinResult);
158+
if (joinResult.error) {
159+
modalMessage =
160+
`Failed to join <#${channelId}> due to ${joinResult.error}`;
161+
}
162+
}
163+
} catch (e) {
164+
logger.error(e);
165+
modalMessage = e;
166+
}
167+
// nothing to return if you want to close this modal
168+
return {
169+
response_action: "update",
170+
view: {
171+
"type": "modal",
172+
"callback_id": "configure-workflow",
173+
"notify_on_close": true,
174+
"title": {
175+
"type": "plain_text",
176+
"text": "Workflow Configuration",
177+
},
178+
"blocks": [
179+
{
180+
"type": "section",
181+
"text": {
182+
"type": "mrkdwn",
183+
"text": modalMessage,
184+
},
185+
},
186+
],
187+
},
188+
};
189+
},
190+
)
191+
.addViewClosedHandler(
192+
["configure-workflow"],
193+
({ view, env }) => {
194+
const logger = Logger(env.logLevel);
195+
logger.debug(JSON.stringify(view, null, 2));
196+
return {
197+
outputs: {},
198+
completed: true,
199+
};
200+
},
201+
);

functions/translator.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ import { DefineFunction, Schema } from "deno-slack-sdk/mod.ts";
22
import { SlackFunctionHandler } from "deno-slack-sdk/types.ts";
33
import { SlackAPI } from "deno-slack-api/mod.ts";
44
import { SlackAPIClient } from "deno-slack-api/types.ts";
5-
import { getLogger } from "../utils/logger.ts";
6-
import { resolveFunctionSourceFile } from "../utils/source_file_resoluion.ts";
5+
import { Logger } from "../utils/logger.ts";
6+
import { FunctionSourceFile } from "../utils/function_source_file.ts";
77

88
export const def = DefineFunction({
99
callback_id: "translator",
1010
title: "Translator",
1111
description: "A funtion to translate a Slack message",
12-
source_file: resolveFunctionSourceFile(import.meta.url),
12+
source_file: FunctionSourceFile(import.meta.url),
1313
input_parameters: {
1414
properties: {
1515
channelId: {
@@ -39,7 +39,7 @@ const handler: SlackFunctionHandler<typeof def.definition> = async ({
3939
token,
4040
env,
4141
}) => {
42-
const logger = await getLogger(env.logLevel);
42+
const logger = Logger(env.LOG_LEVEL);
4343
logger.debug(`translator inputs: ${JSON.stringify(inputs)}`);
4444
const emptyOutputs = { outputs: {} };
4545
if (inputs.lang === undefined) {

import_map.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"imports": {
3-
"deno-slack-sdk/": "https://deno.land/x/deno_slack_sdk@1.1.2/",
4-
"deno-slack-api/": "https://deno.land/x/deno_slack_api@1.1.0/"
3+
"deno-slack-sdk/": "https://deno.land/x/deno_slack_sdk@1.2.0/",
4+
"deno-slack-api/": "https://deno.land/x/deno_slack_api@1.2.0/"
55
}
66
}

manifest.ts

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
11
import { Manifest } from "deno-slack-sdk/mod.ts";
22
import reacjilator from "./workflows/reacjilator.ts";
3+
import setup from "./workflows/setup.ts";
34

45
export default Manifest({
56
name: "DeepL for Slack",
67
description: "A beta app that enbales using DeepL for Slack messages",
78
icon: "assets/icon.png",
8-
workflows: [reacjilator],
9-
outgoingDomains: ["api-free.deepl.com", "api.deepl.com"],
9+
workflows: [
10+
reacjilator,
11+
setup,
12+
],
13+
outgoingDomains: [
14+
"api-free.deepl.com",
15+
"api.deepl.com",
16+
],
1017
botScopes: [
18+
// reacjilator
1119
"commands",
1220
"chat:write",
1321
"channels:history",
1422
"reactions:read",
23+
// setup
24+
"triggers:read",
25+
"triggers:write",
26+
"channels:join",
1527
],
1628
});

slack.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"hooks": {
3-
"get-hooks": "deno run -q --allow-read --allow-net https://deno.land/x/deno_slack_hooks@0.3.0/mod.ts"
3+
"get-hooks": "deno run -q --allow-read --allow-net https://deno.land/x/deno_slack_hooks@0.4.0/mod.ts"
44
}
55
}

triggers/reaction_added.ts triggers/hard-code-example.ts

+3-9
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,9 @@ const trigger: Trigger<typeof workflowDef.definition> = {
1212
channel_ids: ["CLT1F93TP"],
1313
},
1414
inputs: {
15-
channelId: {
16-
value: "{{data.channel_id}}",
17-
},
18-
messageTs: {
19-
value: "{{data.message_ts}}",
20-
},
21-
reaction: {
22-
value: "{{data.reaction}}",
23-
},
15+
channelId: { value: "{{data.channel_id}}" },
16+
messageTs: { value: "{{data.message_ts}}" },
17+
reaction: { value: "{{data.reaction}}" },
2418
},
2519
};
2620

triggers/setup.ts

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Trigger } from "deno-slack-api/types.ts";
2+
import workflowDef from "../workflows/setup.ts";
3+
4+
const trigger: Trigger<typeof workflowDef.definition> = {
5+
type: "shortcut",
6+
name: "Open the configuration modal for reacjilator",
7+
workflow: `#/workflows/${workflowDef.definition.callback_id}`,
8+
inputs: {
9+
interactivity: { value: "{{data.interactivity}}" },
10+
},
11+
};
12+
13+
export default trigger;

utils/function_source_file.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export const FunctionSourceFile = function (
2+
// Pass the value of import.meta.url in a function code
3+
importMetaUrl: string,
4+
// If you have sub diretories under "functions" dir, set the depth.
5+
// When you place functions/pto/data_submission.ts, the depth for the source file is 1.
6+
depth = 0,
7+
): string {
8+
const sliceStart = -2 - depth;
9+
const path = new URL("", importMetaUrl).pathname;
10+
return path.split("/").slice(sliceStart).join("/");
11+
};

utils/logger.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import * as log from "https://deno.land/std@0.157.0/log/mod.ts";
22

3-
export async function getLogger(
3+
export const Logger = function (
44
level?: string,
5-
): Promise<log.Logger> {
5+
): log.Logger {
66
const logLevel: log.LevelName = level === undefined
77
? "DEBUG"
88
: level as log.LevelName;
9-
await log.setup({
9+
log.setup({
1010
handlers: {
1111
console: new log.handlers.ConsoleHandler(logLevel),
1212
},
@@ -18,4 +18,4 @@ export async function getLogger(
1818
},
1919
});
2020
return log.getLogger();
21-
}
21+
};

utils/source_file_resoluion.ts

-5
This file was deleted.

0 commit comments

Comments
 (0)