ããã«ã¡ã¯ãæ¥œæ¥œå€æ ããã¯ãšã³ãããŒã ã® mako _makokã§ãã çæ§ã¯Slack ã¢ããªãéçºããããšãã¯ã©ããã£ãŠéçºãããŠããŸããïŒ Hubotã§ããããïŒãããšãHttp ClientããçŽã§å©ããŠããŸããïŒ ä»åã¯Slack API ãç°¡åã«äœ¿ããçéã§Slack ã¢ããªãéçºããããã® ãã¬ãŒã ã¯ãŒã¯ Bolt ã®ã玹ä»ããããŠããã ããŸãã Boltãšã¯ JavaScript Java Python ã¹ã©ãã·ã¥ã³ãã³ãã«ã€ã㊠ã¹ã©ãã·ã¥ã³ãã³ãã®æŠèŠ ã¹ã©ãã·ã¥ã³ãã³ããç»é²ãã Boltã䜿ã Slack API ãå©ããŠã¿ã ã¹ã©ãã·ã¥ã³ãã³ããäœã ã¬ã·ãŒããŒã®äœæ ã¹ã©ãã·ã¥ã³ãã³ãã®ã¢ã¯ã·ã§ã³ãäœæãã äœè«: Block Kit ãŸãšã ããŸã åè Boltãšã¯ Slack API ããŒã 謹補ã®Slack API Client + ã¹ã©ãã·ã¥ã³ãã³ãçšã®ã¬ã·ãŒããå
ŒãåãããSlack ã¢ããªéçº çšã® ãã¬ãŒã ã¯ãŒã¯ ã§ãã çŸæç¹ã§ã¯ JavaScript (TypeScript), Java , Python ã® SDK ãé
åžãããŠããŸãã ãªãŒãã³ãœãŒã¹ ã§éçºãããŠãããã ãªããžã㪠ã¯ãããã以äžã§ãã JavaScript äž»ã«TypeScriptã§éçºãããŠããŸãã åãã¬ã³ããªãŒã«Slack API ã䜿çšããããšãã§ããŸãã github.com Java kotlinçšã®extensionãSpring BootãKtorãªã© Java , Kotlinã®Web Frameworkãžã®extensionããµã³ãã«ãªã©ãè±å¯ã«ãããŸãã github.com Python type hintãã¬ã³ããªãŒãªéçºãã§ããŸãã äžéšç¬èªã®ã¢ãããŒãã§Slack ã¢ããªãæ±ããåé¡ã解決ãããããŠããŸãã github.com ã©ã®èšèªã§ã现ããªå·®ã¯ãããŸãããåºæ¬çãªã€ã³ã¿ãŒãã§ãŒã¹ã¯åããªã®ã§èªèº«ã®å¥œããªèšèªãéžæããŠãã ããã ä»å㯠bolt-js ã䜿çšããŠè§£èª¬ããŠããããšæããŸãã ã¹ã©ãã·ã¥ã³ãã³ãã«ã€ã㊠Boltã®èª¬æã«å
¥ãåã«ãã¹ã©ãã·ã¥ã³ãã³ãã«ã€ããŠèª¬æããŸãã ã¹ã©ãã·ã¥ã³ãã³ãã¯ãSlackäžã§ /foo ã®ããã«ãããããããªã¬ãŒãšããŠæ§ã
ãªã¢ã¯ã·ã§ã³ãè¡ãããšãã§ããŸãã ããæ¢ã«Slackãã€ã³ã¹ããŒã«ããŠããã®ã§ããã°ã / ãšå
¥åããŠã¿ãŠãã ããã ããã©ã«ãã§ç»é²ãããŠããããã€ãã®ã¹ã©ãã·ã¥ã³ãã³ããäžèŠ§è¡šç€ºãããŸãã ç¶ã㊠/remind ãšå
¥åããŠã¿ããšããªãã€ã³ããŒç»é²çšã®ã¢ãŒãã«ã衚瀺ããããšæããŸãã ã¹ã©ãã·ã¥ã³ãã³ãã®æŠèŠ ã¹ã©ãã·ã¥ã³ãã³ããç°¡ç¥åãããšã以äžã®ããã«åããŠããŸãã ã¹ã©ãã·ã¥ã³ãã³ããå
¥åãããšãæå®ãããšã³ããã€ã³ãã«SlackããHttp RequestãéãããŠããŸãã ã¹ã©ãã·ã¥ã³ãã³ããç»é²ãã ã¹ã©ãã·ã¥ã³ãã³ã㯠https://api.slack.com/apps/{app_id}/slash-commands ã«ã¢ã¯ã»ã¹ãã Create New Command ãæŒããšä»¥äžã®ç»é¢ãåºãŠããŸãã Commandã«ç»é²ãããã¹ã©ãã·ã¥ã³ãã³ããRequest URLã«Slackããã®ãªã¯ ãšã¹ ããåãåããšã³ããã€ã³ããèšå®ããŸãã Boltã䜿ã Slack API ã䜿ãããã«ãBoltã®åæåãããŠãããŸãã ä»å㯠bolt-js ãå©çšããã®ã§ãnpmã§ã€ã³ã¹ããŒã«ããŸãã ãããžã§ã¯ããäœæãã npm install @slack/bolt ãšããã ãã§ã€ã³ã¹ããŒã«ãå®äºããŸãã TypeScriptã¯ã奜ã¿ã§å
¥ããŠãã ããã æåã«Appã ã€ã³ã¹ã¿ã³ã¹ åããŸãã import { App } from '@slack/bolt' const app = new App ( { token: process .env.SLACK_BOT_TOKEN , signingSecret: process .env.SLACK_SIGNING_SECRET , } ) (async () => { // Start your app await app.start ( process .env.PORT || 3000 ); console .log ( 'â¡ïž Bolt app is running!' ); } )(); tokenãšsingleSecretã¯ä»¥äžããååŸããããšãã§ããŸãã singleSecret https://api.slack.com/apps/{app_id} token https://api.slack.com/apps/{app_id} Slack API ãå©ããŠã¿ã Boltã®åæåã¯å®äºããã®ã§ãå®éã«Slack API ãå©ããŠãããŸãã 以äžã¯ chat.postMessage ãå©ããŠãããŒã ã¡ã³ããŒãåå ãããã¡ãã»ãŒãžãéä¿¡ãããµã³ãã«ã§ãã app.event ( 'team_join' , async ( { event , client } ) => { try { await client.chat.postMessage ( { channel: 'foo' , text: `ããã£ãããã<@ ${ event.user.id } >! ð` , } ) } catch ( error ) { console .error ( error ) } } ) 第äžåŒæ°ã«ã€ãã³ãåã第äºåŒæ°ã«ãªã¹ããŒãèšå®ããŸãã ãªã¹ããŒã®clientã¯èªèšŒæžã¿ã®ãã®ãæž¡ãããã®ã§ãããã«Slack API ãå©ãããšãã§ããŸãã ãŸãããªã¹ããŒãä»ããSlack API ãå©ãããšãå¯èœã§ãã Appèªäœã WebClient ãæã£ãŠããã®ã§ãtokenãéœåºŠæž¡ããŠå®è¡ããŸãã await app.client.chat.postMessage ( { token: process .env.SLACK_BOT_TOKEN , channel: 'foo' , text: `ããã£ãããã<@ ${ event.user.id } >! ð` , } ) ããã«ãBoltã«ã¯Slack API ãæŽã«ç°¡åã«æ±ãããã®ãŠãŒãã£ãªãã£ãçšæãããŠããŸãã äŸãã°ãæŽã«ç°¡åã« chat.postMessage ãå®è¡ã§ãã say() 颿°ãªã©ããããŸãã 以äžã¯ããã¯ããããšããã¡ãã»ãŒãžã«åå¿ããŠãã仿¥ããã倩æ°!ããšè¿ã ã¹ã¯ãªãã ã§ãã app.message ( 'ãã¯ãã' , async ( { message , say } ) => { await say ( `仿¥ããã倩æ°!` ); } ); say() 颿°ã¯ãã£ã³ãã«ãç¹å®ã§ããã€ãã³ãã§ããã°ã䜿çšããããšãã§ããŸãã ã¹ã©ãã·ã¥ã³ãã³ããäœã Boltã§ã¯ç°¡åã«ã¹ã©ãã·ã¥ã³ãã³ããäœãããšãã§ããŸãã ã¬ã·ãŒããŒã®äœæ Slackããã®ãªã¯ ãšã¹ ããåãåã£ãŠã¬ã¹ãã³ã¹ãè¿ãã¬ã·ãŒããŒã以äžã®ããã«èšå®ããŸãã import { App , ExpressReceiver } from '@slack/bolt' export const expressReceiver = new ExpressReceiver ( { signingSecret: process .env.SLACK_SIGNING_SECRET , endpoints: '/events' } ) export const app = new App ( { receiver: expressReceiver , token: process .env.SLACK_BOT_TOKEN } ) ExpressReceiver ã App ã«æž¡ãã ãã§ãç°¡åã«ã¬ã·ãŒããŒã®èšå®ãããããšãã§ããŸãã ãšã³ããã€ã³ã㯠/events ãšèšèŒããŠãããŸãããå®éã®ã«ãŒãã£ã³ã°ã¯ /slack/events ãšãªãã®ã§æ³šæããŠãã ããã ãã®ãšã³ããã€ã³ãããã¹ã©ãã·ã¥ã³ãã³ãã«ã€ããŠãã§ç޹ä»ãããRequest URLã«èšå®ããŠæºåå®äºã§ãã ã¹ã©ãã·ã¥ã³ãã³ãã®ã¢ã¯ã·ã§ã³ãäœæãã ã¹ã©ãã·ã¥ã³ãã³ããå
¥åããããšãã®ã¢ã¯ã·ã§ã³ãæžããŠãããŸãã ä»å㯠/register-word ãšããã¹ã©ãã·ã¥ã³ãã³ããå
¥åããããšãã¢ãŒãã«ãéããæ
å ±ãå
¥åããŠsubmitãããšå
¥åãããå
容ãéä¿¡ããããããåããšããµã³ãã«ã§ãã const close: PlainTextElement = { type : 'plain_text' , text: 'ãã£ã³ã»ã«' } const submit: PlainTextElement = { type : 'plain_text' , text: 'ç»é²' } function buildBlocks () : KnownBlock [] { const keywordInput: InputBlock = { type : 'input' , block_id: 'keyword' , label: { type : 'plain_text' , text: 'æ€çŽ¢ã¯ãŒã' , } , element: { type : 'plain_text_input' , action_id: 'keyword_input' , } , } // ... return [ keywordInput ] } const VIEW_ID = 'dialog_1' export const register = ( app: App ) => { app.command ( '/register-word' , async ( { ack , body , context , command } ) => { await ack () try { await app.client.views.open ( { token: context.botToken , trigger_id: body.trigger_id , view: { type : 'modal' , callback_id: VIEW_ID , title: { type : 'plain_text' , text: 'æ€çŽ¢ã¯ãŒãã®ç»é²' , } , blocks: buildBlocks (), private_metadata: command.channel_id , close , submit } , } ) } catch ( err ) { console .error ( err ) } } ) app.view ( VIEW_ID , async ( { ack , view , context , body } ) => { await ack () const values = view.state.values const keyword = values.keyword.keyword_input.value // ... } ) } ãŸã app.command ã§ã¹ã©ãã·ã¥ã³ãã³ããåŸ
æ©ããŸãã app.command ( '/register-word' , async ( { ack , body , context , command } ) => { } æåã« ack() 颿°ãå®è¡ããŸãã ãã®é¢æ°ã¯Slackã«å¯ŸããŠå³åº§ã«ã¬ã¹ãã³ã¹ãè¿ãããšãã§ããŸãã ã¹ã©ãã·ã¥ã³ãã³ãã¯3ç§ä»¥å
ã«ã¬ã¹ãã³ã¹ãè¿ããªããšãããªããšããã«ãŒã«ãããã®ã§ããŸãã¯ã¬ã¹ãã³ã¹ãè¿ããŸãã åŸç¶ã®åŠçã¯éåæã§å®è¡ãããŸãã await ack () ã¹ã©ãã·ã¥ã³ãã³ããåãåã£ãã views.open ãå©ããŸãã ãã® API ã§ã¯ Block Kit ã§äœæãããViewã衚瀺ããããšãã§ããŸãã submitã«ç»é²ãããŠãããªããžã§ã¯ããã¯ãªãã¯ããããšå®éã«submitãããŸãã submitããããã©ãŒã ã®ããŒã¿ã¯ app.view ã§åãåãããšãã§ããŸãã app.view ( VIEW_ID , async ( { ack , view , context , body } ) => { await ack () const values = view.state.values const keyword = values.keyword.keyword_input.value // ... } ) views.open ã§èšå®ããã callback_id ãæ€ç¥ããŠçºç«ããŸãã ãã©ãŒã ã®äžèº«ã¯ view.state.values ã®äžã«ãããŸãã values以äžã®ãªããžã§ã¯ãæ§é 㯠views.open ã§äœ¿çšããŠããBlock Kitå
ã§æ±ºããããšãã§ããŸãã const keywordInput: InputBlock = { type : 'input' , block_id: 'keyword' , label: { type : 'plain_text' , text: 'æ€çŽ¢ã¯ãŒã' , } , element: { type : 'plain_text_input' , // action_idãvalues以äžã®ããããã£ãšãªã action_id: 'keyword_input' , } , } äœè«: Block Kit Block Kitãå©çšããããšã§ãè€éãªUIã衚çŸããäºãã§ããŸãã Block Kitã«ã€ããŠã¯å
¬åŒã®ããã¥ã¡ã³ããããããããã§ãã api.slack.com ãŸãã Block Kit Builder ãšããããŒã«ãããããã¬ãã¥ãŒããªããUIãæ§ç¯ããããšãå¯èœã§ãã ãŸãšã ä»åã¯Boltã«ã€ããŠç޹ä»ãããŠããã ããŸããã ä»å玹ä»ããããªãã£ãæ©èœãããããããã®ã§ãã¿ãªããããã²è§Šã£ãŠã¿ãŠSlackã¢ããªãäœã£ãŠã¿ãŠãã ããã æ¹ããŠãBoltã®ã¡ãªããããŸãšããŸãã Slack API ããŒã ãã¡ã³ããã³ã¹ããŠããã®ã§ãä¿¡é Œæ§ãé«ã åå®å
šã«Slack API ãæ±ãããšãã§ãã Slack API ãç°¡åã«äœ¿ãããã®äŸ¿å©ãªãŠãŒãã£ãªã㣠ã¹ã©ãã·ã¥ã³ãã³ãçšã®ã¬ã·ãŒããŒãç°¡åã«çæã§ãã ããŸã Bolt + Firestore + Cloud Functions ææç©ãšããŠãã¹ã©ãã·ã¥ã³ãã³ãããã¯ãŒãç»é²ã¢ãŒãã«ãéããæ¯æãããã§ååŒ·äŒæ
å ±ãéç¥ããŠããããµã³ãã«ãäœã£ãŠã¿ãŸããã ãããããã°åèã«ããŠã¿ãŠãã ããã github.com åè Boltå
¥éã¬ã€ã( https://slack.dev/bolt-js/ja-jp/tutorial/getting-started ) Slack API ( https://api.slack.com/apis ) Bolt for JavaScript ã䜿ã£ã Slack ã¢ããªéçº ã§ç¥ã£ãŠãããšæã 7 ã€ã®ããš( https://qiita.com/seratch/items/dcedc7476eac5f681d30 ) â»åé ç»åã®SlackããŽã¯ã å
¬åŒãµã€ã ã§é
åžãããŠãããã®ã䜿çšããŠãããŸãã ãšã³ãžã㢠äžéæ¡çš ãµã€ã ã©ã¯ ã¹ã§ã¯ããšã³ãžãã¢ã»ãã¶ã€ããŒã® äžéæ¡çš ãç©æ¥µçã«è¡ã£ãŠãããŸãïŒ ãèå³ãããŸãããæ¯éã確èªããé¡ãããŸãã https://career-recruit.rakus.co.jp/career_engineer/ ã«ãžã¥ã¢ã«é¢è«ãç³èŸŒã¿ãã©ãŒã ã©ã®è·çš®ã«å¿åããã°è¯ããããããªããšããæ¹ã¯ãã«ãžã¥ã¢ã«é¢è«ãéæè¡ã£ãŠãããŸãã 以äžãã©ãŒã ãããç³èŸŒã¿ãã ããã rakus.hubspotpagebuilder.com ã©ã¯ ã¹Developersç»é²ãã©ãŒã https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ ã€ãã³ãæ
å ± äŒç€Ÿã®é°å²æ°ãç¥ãããæ¹ã¯ãæ¯é±éå¬ããŠããã€ãã³ãã«ãåå ãã ããïŒ âTECH PLAY techplay.jp âconnpass rakus.connpass.com