├── .gitignore ├── .prettierrc.json ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── cloudbuild.yaml ├── extra-credit ├── kittenbot.js └── package.json ├── package-lock.json ├── package.json ├── start ├── kittenbot.js └── package.json └── test └── kittenbot.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 28 | node_modules 29 | 30 | # Optional npm cache directory 31 | .npm 32 | 33 | # Optional REPL history 34 | .node_repl_history 35 | 36 | # Generated files with project-specific information 37 | slack-token 38 | 39 | .DS_Store 40 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 2, 4 | "semi": true, 5 | "singleQuote": true 6 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to become a contributor and submit your own code 2 | 3 | ## Contributor License Agreements 4 | 5 | We'd love to accept your sample apps and patches! Before we can take them, we 6 | have to jump a couple of legal hurdles. 7 | 8 | Please fill out either the individual or corporate Contributor License Agreement 9 | (CLA). 10 | 11 | * If you are an individual writing original source code and you're sure you 12 | own the intellectual property, then you'll need to sign an [individual CLA] 13 | (https://developers.google.com/open-source/cla/individual). 14 | * If you work for a company that wants to allow you to contribute your work, 15 | then you'll need to sign a [corporate CLA] 16 | (https://developers.google.com/open-source/cla/corporate). 17 | 18 | Follow either of the two links above to access the appropriate CLA and 19 | instructions for how to sign and return it. Once we receive it, we'll be able to 20 | accept your pull requests. 21 | 22 | ## Contributing A Patch 23 | 24 | 1. Submit an issue describing your proposed change to the repo in question. 25 | 1. The repo owner will respond to your issue promptly. 26 | 1. If your proposed change is accepted, and you haven't already done so, sign a Contributor License Agreement (see details above). 27 | 1. Fork the desired repo, develop and test your code changes. 28 | 1. Ensure that your code adheres to the existing style in the sample to which 29 | you are contributing. Refer to the [Standard JS Code 30 | Style](http://standardjs.com/) for the recommended coding standards. 31 | 1. Ensure that your code has an appropriate set of unit tests which all pass. 32 | 1. Submit a pull request! 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Build a Slack Bot with Node.js on Cloud Run 2 | 3 | This repository contains the source code for the [Build a Slack Bot with Node.js on Cloud Run 4 | Google codelab][cloud-slack-bot-codelab]. 5 | 6 | [![Build Status][travis-status-image]][travis-status] 7 | 8 | [cloud-slack-bot-codelab]: https://codelabs.developers.google.com/codelabs/cloud-slack-bot/ 9 | [travis-status]: https://travis-ci.org/googlecodelabs/cloud-slack-bot 10 | [travis-status-image]: https://travis-ci.org/googlecodelabs/cloud-slack-bot.svg?branch=master 11 | 12 | ## Resources 13 | 14 | ### Codelabs and Samples 15 | 16 | - [Google codelabs][codelabs] 17 | - [Slack integrations on Google Cloud Platform samples][slack-samples] 18 | - [Node.js on Google Cloud Platform samples][nodejs-samples] 19 | 20 | [codelabs]: https://g.co/codelabs 21 | [nodejs-samples]: https://github.com/GoogleCloudPlatform/nodejs-docs-samples 22 | [slack-samples]: https://github.com/GoogleCloudPlatform/slack-samples 23 | 24 | ### Documentation 25 | 26 | - [Google Cloud Platform][cloud] 27 | - [Google Cloud Run][run] 28 | - [Google Container Engine][container-engine] 29 | - [Google Container Registry][container-registry] 30 | - [Google Secret Manager][secret-manager] 31 | - [Slack API][slack-api] 32 | 33 | [cloud]: https://cloud.google.com/ 34 | [run]: https://cloud.google.com/run 35 | [container-engine]: https://cloud.google.com/container-engine/ 36 | [container-registry]: https://cloud.google.com/container-registry/ 37 | [secret-manager]: https://cloud.google.com/secret-manager 38 | [slack-api]: https://api.slack.com/ 39 | 40 | ## Contributing 41 | 42 | Contributions welcome! 43 | 44 | See [CONTRIBUTING.md](CONTRIBUTING.md). 45 | 46 | ## License 47 | 48 | Apache Version 2.0 49 | 50 | See [LICENSE](LICENSE). 51 | -------------------------------------------------------------------------------- /cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | steps: 16 | - name: node 17 | entrypoint: npm 18 | args: ['install'] 19 | - name: node 20 | entrypoint: npm 21 | args: ['test'] 22 | -------------------------------------------------------------------------------- /extra-credit/kittenbot.js: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | Copyright 2020 Google LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License") 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | ******************************************************************************** 16 | 17 | This is a sample Slack bot built with Botkit. 18 | */ 19 | 20 | const { Botkit, BotkitConversation } = require('botkit'); 21 | const { 22 | SlackAdapter, 23 | SlackEventMiddleware, 24 | } = require('botbuilder-adapter-slack'); 25 | const { SecretManagerServiceClient } = require('@google-cloud/secret-manager'); 26 | 27 | /** 28 | * Returns the secret string from Google Cloud Secret Manager 29 | * @param {string} name The name of the secret. 30 | * @return {Promise} The string value of the secret. 31 | */ 32 | async function accessSecretVersion(name) { 33 | const client = new SecretManagerServiceClient(); 34 | const projectId = process.env.PROJECT_ID; 35 | const [version] = await client.accessSecretVersion({ 36 | name: `projects/${projectId}/secrets/${name}/versions/1`, 37 | }); 38 | 39 | // Extract the payload as a string. 40 | const payload = version.payload.data.toString('utf8'); 41 | 42 | return payload; 43 | } 44 | 45 | /** 46 | * Function to initialize kittenbot. 47 | */ 48 | async function kittenbotInit() { 49 | const adapter = new SlackAdapter({ 50 | clientSigningSecret: await accessSecretVersion('client-signing-secret'), 51 | botToken: await accessSecretVersion('bot-token'), 52 | }); 53 | 54 | adapter.use(new SlackEventMiddleware()); 55 | 56 | const controller = new Botkit({ 57 | webhook_uri: '/api/messages', 58 | adapter: adapter, 59 | }); 60 | 61 | // Add Kitten Dialog 62 | const convo = createKittenDialog(controller); 63 | controller.addDialog(convo); 64 | 65 | // Controller is ready 66 | controller.ready(() => { 67 | controller.hears( 68 | ['hello', 'hi', 'hey'], 69 | ['message', 'direct_message'], 70 | async (bot, message) => { 71 | await bot.reply(message, 'Meow. :smile_cat:'); 72 | return; 73 | } 74 | ); 75 | 76 | // START: listen for cat emoji delivery 77 | controller.hears( 78 | ['cat', 'cats', 'kitten', 'kittens'], 79 | ['message', 'direct_message'], 80 | async (bot, message) => { 81 | // Don't respond to self 82 | if (message.bot_id !== message.user) { 83 | await bot.startConversationInChannel(message.channel, message.user); 84 | await bot.beginDialog('kitten-delivery'); 85 | return; 86 | } 87 | } 88 | ); 89 | // END: listen for cat emoji delivery 90 | 91 | // START: slash commands 92 | controller.on('slash_command', async (bot, message) => { 93 | const numCats = parseInt(message.text); 94 | const response = makeCatMessage(numCats); 95 | bot.httpBody({ text: response }); 96 | }); 97 | // END: slash commands 98 | }); 99 | } 100 | 101 | const maxCats = 20; 102 | const catEmojis = [ 103 | ':smile_cat:', 104 | ':smiley_cat:', 105 | ':joy_cat:', 106 | ':heart_eyes_cat:', 107 | ':smirk_cat:', 108 | ':kissing_cat:', 109 | ':scream_cat:', 110 | ':crying_cat_face:', 111 | ':pouting_cat:', 112 | ':cat:', 113 | ':cat2:', 114 | ':leopard:', 115 | ':lion_face:', 116 | ':tiger:', 117 | ':tiger2:', 118 | ]; 119 | 120 | /** 121 | * Function to concatenate cat emojis 122 | * @param {number} numCats Number of cat emojis. 123 | * @return {string} The string message of cat emojis. 124 | */ 125 | function makeCatMessage(numCats) { 126 | let catMessage = ''; 127 | for (let i = 0; i < numCats; i++) { 128 | // Append a random cat from the list 129 | catMessage += catEmojis[Math.floor(Math.random() * catEmojis.length)]; 130 | } 131 | return catMessage; 132 | } 133 | 134 | /** 135 | * Function to create the kitten conversation 136 | * @param {Object} controller The botkit controller. 137 | * @return {Object} The BotkitConversation object. 138 | */ 139 | function createKittenDialog(controller) { 140 | const convo = new BotkitConversation('kitten-delivery', controller); 141 | 142 | convo.ask('Does someone need a kitten delivery?', [ 143 | { 144 | pattern: 'yes', 145 | handler: async (response, convo, bot) => { 146 | await convo.gotoThread('yes_kittens'); 147 | }, 148 | }, 149 | { 150 | pattern: 'no', 151 | handler: async (response, convo, bot) => { 152 | await convo.gotoThread('no_kittens'); 153 | }, 154 | }, 155 | { 156 | default: true, 157 | handler: async (response, convo, bot) => { 158 | await convo.gotoThread('default'); 159 | }, 160 | }, 161 | ]); 162 | 163 | convo.addQuestion( 164 | 'How many would you like?', 165 | [ 166 | { 167 | pattern: '^[0-9]+?', 168 | handler: async (response, convo, bot, message) => { 169 | const numCats = parseInt(response); 170 | if (numCats > maxCats) { 171 | await convo.gotoThread('too_many'); 172 | } else { 173 | convo.setVar('full_cat_message', makeCatMessage(numCats)); 174 | await convo.gotoThread('cat_message'); 175 | } 176 | }, 177 | }, 178 | { 179 | default: true, 180 | handler: async (response, convo, bot, message) => { 181 | if (response) { 182 | await convo.gotoThread('ask_again'); 183 | } else { 184 | // The response '0' is interpreted as null 185 | await convo.gotoThread('zero_kittens'); 186 | } 187 | }, 188 | }, 189 | ], 190 | 'num_kittens', 191 | 'yes_kittens' 192 | ); 193 | 194 | // If numCats is too large, jump to start of the yes_kittens thread 195 | convo.addMessage( 196 | 'Sorry, {{vars.num_kittens}} is too many cats. Pick a smaller number.', 197 | 'too_many' 198 | ); 199 | convo.addAction('yes_kittens', 'too_many'); 200 | 201 | // If response is not a number, jump to start of the yes_kittens thread 202 | convo.addMessage("Sorry I didn't understand that", 'ask_again'); 203 | convo.addAction('yes_kittens', 'ask_again'); 204 | 205 | // If numCats is 0, send a dog instead 206 | convo.addMessage( 207 | { 208 | text: 209 | 'Sorry to hear you want zero kittens. ' + 210 | 'Here is a dog, instead. :dog:', 211 | attachments: [ 212 | { 213 | fallback: 'Chihuahua Bubbles - https://youtu.be/s84dBopsIe4', 214 | text: '!', 215 | }, 216 | ], 217 | }, 218 | 'zero_kittens' 219 | ); 220 | 221 | // Send cat message 222 | convo.addMessage('{{vars.full_cat_message}}', 'cat_message'); 223 | 224 | convo.addMessage('Perhaps later.', 'no_kittens'); 225 | 226 | return convo; 227 | } 228 | // END: kitten-delivery convo 229 | 230 | kittenbotInit(); 231 | -------------------------------------------------------------------------------- /extra-credit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloud-slack-bot-example", 3 | "version": "2.0.0", 4 | "description": "A sample Slack Botkit bot.", 5 | "main": "kittenbot.js", 6 | "scripts": { 7 | "start": "node kittenbot.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "engines": { 11 | "node": "16" 12 | }, 13 | "dependencies": { 14 | "@google-cloud/secret-manager": "4", 15 | "botbuilder-adapter-slack": "1", 16 | "request": "2" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/googlecodelabs/cloud-slack-bot.git" 21 | }, 22 | "author": "Google Inc.", 23 | "license": "Apache-2.0" 24 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloud-slack-bot-example", 3 | "version": "2.0.0", 4 | "description": "A sample Slack Botkit bot.", 5 | "main": "kittenbot.js", 6 | "dependencies": { 7 | "@google-cloud/secret-manager": "^3.6.0", 8 | "assert": "2.0.0", 9 | "axios": ">=0.21.1", 10 | "botbuilder-adapter-slack": "^1.0.10", 11 | "botkit": "^4.8.1" 12 | }, 13 | "devDependencies": { 14 | "standard": "^14.3.3" 15 | }, 16 | "scripts": { 17 | "test": "standard \"**/*.js\" && node test/kittenbot.js" 18 | }, 19 | " repository": { 20 | "type": "git", 21 | "url": "https://github.com/googlecodelabs/cloud-slack-bot.git" 22 | }, 23 | "author": "Google Inc.", 24 | "license": "Apache-2.0" 25 | } 26 | -------------------------------------------------------------------------------- /start/kittenbot.js: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | Copyright 2016 Google LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License") 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | ******************************************************************************** 16 | 17 | This is a sample Slack bot built with Botkit. 18 | */ 19 | 20 | const { Botkit } = require('botkit'); 21 | const { 22 | SlackAdapter, 23 | SlackEventMiddleware, 24 | } = require('botbuilder-adapter-slack'); 25 | const { SecretManagerServiceClient } = require('@google-cloud/secret-manager'); 26 | 27 | /** 28 | * Returns the secret string from Google Cloud Secret Manager 29 | * @param {string} name The name of the secret. 30 | * @return {Promise} The string value of the secret. 31 | */ 32 | async function accessSecretVersion(name) { 33 | const client = new SecretManagerServiceClient(); 34 | const projectId = process.env.PROJECT_ID; 35 | const [version] = await client.accessSecretVersion({ 36 | name: `projects/${projectId}/secrets/${name}/versions/1`, 37 | }); 38 | 39 | // Extract the payload as a string. 40 | const payload = version.payload.data.toString('utf8'); 41 | 42 | return payload; 43 | } 44 | 45 | /** 46 | * Function to initialize kittenbot. 47 | */ 48 | async function kittenbotInit() { 49 | const adapter = new SlackAdapter({ 50 | clientSigningSecret: await accessSecretVersion('client-signing-secret'), 51 | botToken: await accessSecretVersion('bot-token'), 52 | }); 53 | 54 | adapter.use(new SlackEventMiddleware()); 55 | 56 | const controller = new Botkit({ 57 | webhook_uri: '/api/messages', 58 | adapter: adapter, 59 | }); 60 | 61 | controller.ready(() => { 62 | controller.hears( 63 | ['hello', 'hi', 'hey'], 64 | ['message', 'direct_message'], 65 | async (bot, message) => { 66 | await bot.reply(message, 'Meow. :smile_cat:'); 67 | } 68 | ); 69 | }); 70 | } 71 | 72 | kittenbotInit(); 73 | -------------------------------------------------------------------------------- /start/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloud-slack-bot-example", 3 | "version": "2.0.0", 4 | "description": "A sample Slack Botkit bot.", 5 | "main": "kittenbot.js", 6 | "scripts": { 7 | "start": "node kittenbot.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "engines": { 11 | "node": "16" 12 | }, 13 | "dependencies": { 14 | "@google-cloud/secret-manager": "4", 15 | "botbuilder-adapter-slack": "1", 16 | "request": "2" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/googlecodelabs/cloud-slack-bot.git" 21 | }, 22 | "author": "Google Inc.", 23 | "license": "Apache-2.0" 24 | } -------------------------------------------------------------------------------- /test/kittenbot.js: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | Copyright 2016 Google LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License") 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | ******************************************************************************** 16 | 17 | This is a sample Slack bot built with Botkit. 18 | */ 19 | const assert = require('assert') 20 | 21 | /** 22 | * Function to test Botkit Controller Configurations. 23 | */ 24 | function testBotkitController () { 25 | const { Botkit, BotkitConversation } = require('botkit') 26 | const { SlackAdapter, SlackEventMiddleware } = require( 27 | 'botbuilder-adapter-slack') 28 | 29 | const adapter = new SlackAdapter({ 30 | enable_incomplete: true, 31 | debug: true 32 | }) 33 | 34 | adapter.use(new SlackEventMiddleware()) 35 | 36 | const controller = new Botkit({ 37 | webhook_uri: '/api/messages', 38 | adapter: adapter 39 | }) 40 | 41 | // test controller version 42 | assert(parseInt(controller.version) >= 4) 43 | 44 | // test webhook endpoint 45 | const config = controller.getConfig() 46 | assert(config.webhook_uri === '/api/messages') 47 | 48 | // testBotkitConversation 49 | const convo = new BotkitConversation('test-dialog', controller) 50 | controller.addDialog(convo) 51 | assert('test-dialog' in controller.dialogSet.dialogs) 52 | 53 | controller.shutdown() 54 | } 55 | 56 | /** 57 | * Function to test Secret Manager Client. 58 | */ 59 | function testSecretManagerClient () { 60 | const { SecretManagerServiceClient } = require('@google-cloud/secret-manager') 61 | const client = new SecretManagerServiceClient() 62 | assert(client) 63 | }; 64 | 65 | testBotkitController() 66 | testSecretManagerClient() 67 | --------------------------------------------------------------------------------