Skip to content

Commit

Permalink
Merge 175fe8e into 0e469c9
Browse files Browse the repository at this point in the history
  • Loading branch information
shaydewael authored Mar 23, 2021
2 parents 0e469c9 + 175fe8e commit 0e3e8cf
Show file tree
Hide file tree
Showing 16 changed files with 307 additions and 22 deletions.
19 changes: 11 additions & 8 deletions docs/_basic/ja_listening_actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Bolt アプリは `action` メソッドを用いて、ボタンのクリック

```javascript
// action_id が "approve_button" のインタラクティブコンポーネントがトリガーされる毎にミドルウェアが呼び出される
app.action('approve_button', async ({ ack, say }) => {
app.action('approve_button', async ({ ack }) => {
await ack();
// アクションを反映してメッセージをアップデート
});
Expand All @@ -35,15 +35,18 @@ app.action('approve_button', async ({ ack, say }) => {
```javascript
// action_id が 'select_user' と一致し、block_id が 'assign_ticket' と一致する場合のみミドルウェアが呼び出される
app.action({ action_id: 'select_user', block_id: 'assign_ticket' },
async ({ body, action, ack, context }) => {
async ({ body, client, ack }) => {
await ack();
try {
const result = await app.client.reactions.add({
token: context.botToken,
name: 'white_check_mark',
timestamp: action.ts,
channel: body.channel.id
});
if (body.message) {
const result = await client.reactions.add({
name: 'white_check_mark',
timestamp: body.message.ts,
channel: body.channel.id
});

console.log(result);
}
}
catch (error) {
console.error(error);
Expand Down
2 changes: 1 addition & 1 deletion docs/_basic/ja_listening_events.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ app.event('team_join', async ({ event, client }) => {
<div class="secondary-content" markdown="0">
`message()` リスナーは `event('message')` と等価の機能を提供します。

イベントのサブタイプをフィルタリングしたい場合、組み込みの `matchEventSubtype()` ミドルウェアを使用できます。 `bot_message``message_replied` のような一般的なメッセージサブタイプの情報は、[メッセージイベントのドキュメント](https://api.slack.com/events/message#message_subtypes)を参照してください。
イベントのサブタイプをフィルタリングしたい場合、組み込みの `subtype()` ミドルウェアを使用できます。 `bot_message``message_replied` のような一般的なメッセージサブタイプの情報は、[メッセージイベントのドキュメント](https://api.slack.com/events/message#message_subtypes)を参照してください。
</div>

```javascript
Expand Down
2 changes: 1 addition & 1 deletion docs/_basic/ja_web_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Bolt アプリケーションは、トップレベルに `app.client` も持っ

```javascript
// September 30, 2019 11:59:59 PM を Unix エポックタイムで表示
const whenSeptemberEnds = 1569887999;
const whenSeptemberEnds = '1569887999';

app.message('wake me up', async ({ message, context }) => {
try {
Expand Down
24 changes: 14 additions & 10 deletions docs/_basic/listening_actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ You’ll notice in all `action()` examples, `ack()` is used. It is required to c
</div>

```javascript
// Your middleware will be called every time an interactive component with the action_id "approve_button" is triggered
app.action('approve_button', async ({ ack, say }) => {
// Your listener function will be called every time an interactive component with the action_id "approve_button" is triggered
app.action('approve_button', async ({ ack }) => {
await ack();
// Update the message to reflect the action
});
Expand All @@ -33,17 +33,21 @@ You can use a constraints object to listen to `callback_id`s, `block_id`s, and `
</div>

```javascript
// Your middleware will only be called when the action_id matches 'select_user' AND the block_id matches 'assign_ticket'
// Your listener function will only be called when the action_id matches 'select_user' AND the block_id matches 'assign_ticket'
app.action({ action_id: 'select_user', block_id: 'assign_ticket' },
async ({ body, action, ack, context }) => {
async ({ body, client, ack }) => {
await ack();
try {
const result = await app.client.reactions.add({
token: context.botToken,
name: 'white_check_mark',
timestamp: action.ts,
channel: body.channel.id
});
// Make sure the action isn't from a view (modal or app home)
if (body.message) {
const result = await client.reactions.add({
name: 'white_check_mark',
timestamp: body.message.ts,
channel: body.channel.id
});

console.log(result);
}
}
catch (error) {
console.error(error);
Expand Down
2 changes: 1 addition & 1 deletion docs/_basic/listening_events.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ app.event('team_join', async ({ event, client }) => {
<div class="secondary-content" markdown="0">
A `message()` listener is equivalent to `event('message')`

You can filter on subtypes of events by using the built-in `matchEventSubtype()` middleware. Common message subtypes like `bot_message` and `message_replied` can be found [on the message event page](https://api.slack.com/events/message#message_subtypes).
You can filter on subtypes of events by using the built-in `subtype()` middleware. Common message subtypes like `bot_message` and `message_replied` can be found [on the message event page](https://api.slack.com/events/message#message_subtypes).
</div>

```javascript
Expand Down
2 changes: 1 addition & 1 deletion docs/_basic/web_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Since the introduction of [org wide app installations](https://api.slack.com/ent

```javascript
// Unix Epoch time for September 30, 2019 11:59:59 PM
const whenSeptemberEnds = 1569887999;
const whenSeptemberEnds = '1569887999';

app.message('wake me up', async ({ message, client }) => {
try {
Expand Down
6 changes: 6 additions & 0 deletions docs/_tutorials/using-typescript.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ layout: tutorial
permalink: /tutorial/using-typescript
---
# Using TypeScript
> ⚠️ This guide is a work-in-progress.
See [the sample TypeScript project][1] to see a TypeScript equivalent of the [Getting Started app][2] (and a few other basic examples).

This project is written and built using [TypeScript](https://www.typescriptlang.org/), which means many of the APIs have type information metadata 🎉. If you’re using a code editor like VSCode, Atom, or many others that know how to read that metadata, or if you’re using TypeScript in your own project, you’ll benefit from improved documentation as your write code, early detection of errors, easier refactoring, and more.

Expand All @@ -15,3 +18,6 @@ This page helps describe how to use this package from a project that also uses T
### Minimum version

The latest major version of `@slack/bolt` is supported to build against a minimum TypeScript version of v4.1.

[1]: https://github.com/slackapi/bolt-js/tree/main/examples/getting-started-typescript
[2]: https://github.com/slackapi/bolt-js-getting-started-app
2 changes: 2 additions & 0 deletions examples/getting-started-typescript/.env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
SLACK_BOT_TOKEN=xoxb-your-bot-token
SLACK_SIGNING_SECRET=your-signing-secret
4 changes: 4 additions & 0 deletions examples/getting-started-typescript/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.env
dist/
node_modules/
package-lock.json
8 changes: 8 additions & 0 deletions examples/getting-started-typescript/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Getting started with TypeScript ⚡️ Bolt for JavaScript
> TypeScript equivalent for Slack app example from 📚 [Getting started with Bolt for JavaScript tutorial][1]
This is a temporary example app intending to show how to work with Bolt's type system. Because the type system is currently lacking,
this app will change in the future. See [#826][2] for more context.

[1]: https://slack.dev/bolt-js/tutorial/getting-started
[2]: https://github.com/slackapi/bolt-js/issues/826
21 changes: 21 additions & 0 deletions examples/getting-started-typescript/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "getting-started-typescript",
"version": "1.0.0",
"description": "Bolt getting started app in TypeScript",
"main": "dist/app.js",
"scripts": {
"build": "tsc -p .",
"build:watch": "tsc -w -p ./src",
"start": "npm run build && node dist/app.js"
},
"license": "MIT",
"dependencies": {
"@slack/bolt": "^3.3.0",
"dotenv": "^8.2.0"
},
"devDependencies": {
"@types/node": "^14.14.35",
"ts-node": "^9.1.1",
"typescript": "^4.2.3"
}
}
52 changes: 52 additions & 0 deletions examples/getting-started-typescript/src/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import "./utils/env";
import { App, LogLevel } from '@slack/bolt';
import { isGenericMessageEvent } from './utils/helpers'

const app = new App({
token: process.env.SLACK_BOT_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET,
logLevel: LogLevel.DEBUG
});

// Listens to incoming messages that contain "hello"
app.message('hello', async ({ message, say }) => {
// Filter out message events with subtypes (see https://api.slack.com/events/message)
// Is there a way to do this in listener middleware with current type system?
if (!isGenericMessageEvent(message)) return;
// say() sends a message to the channel where the event was triggered

await say({
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text: `Hey there <@${message.user}>!`
},
accessory: {
type: 'button',
text: {
type: 'plain_text',
text: 'Click Me'
},
action_id: 'button_click'
}
}
],
text: `Hey there <@${message.user}>!`
});
});

app.action('button_click', async ({ body, ack, say }) => {
// Acknowledge the action
await ack();
await say(`<@${body.user.id}> clicked the button`);
});

(async () => {
// Start your app
await app.start(Number(process.env.PORT) || 3000);

console.log('⚡️ Bolt app is running!');
})();

146 changes: 146 additions & 0 deletions examples/getting-started-typescript/src/basic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import "./utils/env";
import { App, LogLevel, subtype, BotMessageEvent, UsersSelectAction, BlockAction } from '@slack/bolt';
import {
isGenericMessageEvent,
isMessageItem
} from './utils/helpers'

const app = new App({
token: process.env.SLACK_BOT_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET,
logLevel: LogLevel.DEBUG
});

/**
* Listening to messages
*/
// This will match any message that contains 👋
app.message(':wave:', async ({ message, say }) => {
if (!isGenericMessageEvent(message)) return;

await say(`Hello, <@${message.user}>`);
});

/**
* Sending messages
*/
// Listens for messages containing "knock knock" and responds with an italicized "who's there?"
app.message('knock knock', async ({ say }) => {
await say(`_Who's there?_`);
});

// Sends a section block with datepicker when someone reacts with a 📅 emoji
app.event('reaction_added', async ({ event, client }) => {
// Could be a file that was reacted upon
if (event.reaction === 'calendar' && isMessageItem(event.item)) {
await client.chat.postMessage({
text: 'Pick a reminder date',
channel: event.item.channel,
blocks: [{
type: 'section',
text: {
type: 'mrkdwn',
text: 'Pick a date for me to remind you'
},
accessory: {
type: 'datepicker',
action_id: 'datepicker_remind',
initial_date: '2019-04-28',
placeholder: {
type: 'plain_text',
text: 'Select a date'
}
}
}]
});
}
});

/**
* Listening to events
*/
const welcomeChannelId = 'C12345';

// When a user joins the team, send a message in a predefined channel asking them to introduce themselves
app.event('team_join', async ({ event, client }) => {
try {
// Call chat.postMessage with the built-in client
const result = await client.chat.postMessage({
channel: welcomeChannelId,
text: `Welcome to the team, <@${event.user}>! 🎉 You can introduce yourself in this channel.`
});
console.log(result);
}
catch (error) {
console.error(error);
}
});

app.message(subtype('bot_message'), async ({ message }) => {
console.log(`The bot user ${(message as BotMessageEvent).user} said ${(message as BotMessageEvent).text}`);
});

/**
* Using the Web API
*/
// Unix Epoch time for September 30, 2019 11:59:59 PM
const whenSeptemberEnds = '1569887999';

app.message('wake me up', async ({ message, client }) => {
try {
// Call chat.scheduleMessage with the built-in client
const result = await client.chat.scheduleMessage({
channel: message.channel,
post_at: whenSeptemberEnds,
text: 'Summer has come and passed'
});
}
catch (error) {
console.error(error);
}
});

/**
* Listening to actions
*/
// Your listener function will be called every time an interactive component with the action_id "approve_button" is triggered
app.action('approve_button', async ({ ack }) => {
await ack();
// Update the message to reflect the action
});

// Your listener function will only be called when the action_id matches 'select_user' AND the block_id matches 'assign_ticket'
app.action({ action_id: 'select_user', block_id: 'assign_ticket' },
async ({ body, client, ack }) => {
await ack();
// TODO
body = body as BlockAction;
try {
// Make sure the event is not in a view
if (body.message) {
await client.reactions.add({
name: 'white_check_mark',
timestamp: body.message?.ts,
channel: body.channel?.id
});
}
}
catch (error) {
console.error(error);
}
});

// Your middleware will be called every time an interactive component with the action_id “approve_button” is triggered
app.action('approve_button', async ({ ack, say }) => {
// Acknowledge action request
await ack();
await say('Request approved 👍');
});

(async () => {
// Start your app
await app.start(Number(process.env.PORT) || 3000);

console.log('⚡️ Bolt app is running!');
})();

6 changes: 6 additions & 0 deletions examples/getting-started-typescript/src/utils/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// for details see https://github.com/motdotla/dotenv/blob/master/examples/typescript/
import { resolve } from 'path'
import { config } from 'dotenv'

const pathToConfig = '../../.env';
config({ path: resolve(__dirname, pathToConfig) });
15 changes: 15 additions & 0 deletions examples/getting-started-typescript/src/utils/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {
GenericMessageEvent,
MessageEvent,
ReactionAddedEvent,
ReactionMessageItem
} from '@slack/bolt';


export const isGenericMessageEvent = (msg: MessageEvent): msg is GenericMessageEvent => {
return (msg as GenericMessageEvent).subtype === undefined;
}

export const isMessageItem = (item: ReactionAddedEvent["item"]): item is ReactionMessageItem => {
return (item as ReactionMessageItem).type === 'message';
}
Loading

0 comments on commit 0e3e8cf

Please sign in to comment.