├── .env ├── .gitignore ├── ATTRIBUTIONS.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── app.json ├── bot.js ├── components ├── database.js ├── express_webserver.js ├── plugin_glitch.js ├── plugin_identity.js └── routes │ ├── embed.js │ ├── incoming_webhooks.js │ └── sample.js ├── docs ├── botkit_chat_server.md ├── botkit_chat_widget.png ├── botkit_web_client.md ├── edit_menu.gif └── how_to_build_skills.md ├── glitch_readme.md ├── package-lock.json ├── package.json ├── public ├── beep.mp3 ├── bigger.html ├── botkit_icon.png ├── chat.html ├── client.js ├── css │ ├── biggerstyles.css │ ├── embed.css │ ├── embed.css.map │ ├── styles.css │ └── styles.css.map ├── embed.js └── sent.mp3 ├── readme.md ├── sass ├── _botkit.scss ├── _chat.scss ├── _home.scss ├── embed.scss └── styles.scss ├── skills ├── _connection_events.js ├── demo_quick_replies.js ├── message_history.js ├── sample_hears.js ├── super_quit.js └── unhandled_messages.js └── views ├── embed.hbs ├── index.hbs ├── layouts └── default.hbs └── partials ├── footer.hbs ├── head.hbs └── header.hbs /.env: -------------------------------------------------------------------------------- 1 | # Environment Config 2 | 3 | # store your secrets and config variables in here 4 | # only invited collaborators will be able to see your .env values 5 | # reference these in your code with process.env.SECRET 6 | 7 | # Specify a port number for the webserver to run on (default 3000) 8 | PORT=3000 9 | 10 | # Specify a MONGO_URI to enable message history 11 | # mLab.com has a free tier which is great for getting started 12 | MONGO_URI= 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | .data 3 | node_modules 4 | components/routes/_* 5 | .sass-cache/ 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /ATTRIBUTIONS.md: -------------------------------------------------------------------------------- 1 | # CSS Typing Indicator 2 | 3 | Copyright (c) 2018 by Joseph Fusco (https://codepen.io/fusco/pen/XbpaYv) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | 11 | 12 | # json-stable-stringify 13 | 14 | Copyright (c) James Halliday 15 | 16 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | 23 | # key-tree-store 24 | 25 | Copyright (c) Henrik Joreteg 26 | 27 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 28 | 29 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 32 | 33 | 34 | # set-immediate-shim 35 | 36 | Copyright (c) Sindre Sorhus (sindresorhus.com) 37 | 38 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 39 | 40 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 41 | 42 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 43 | 44 | # json-schema 45 | 46 | The "New" BSD License: 47 | ********************** 48 | 49 | Copyright (c) 2005-2018, The JS Foundation 50 | All rights reserved. 51 | 52 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 53 | 54 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 55 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 56 | * Neither the name of the JS Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 57 | 58 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 59 | 60 | 61 | # browser-request 62 | 63 | Copyright (c) 2014 Jason Smith Work , Jason Smith , maxogden 64 | 65 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 66 | 67 | http://www.apache.org/licenses/LICENSE-2.0 68 | 69 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 70 | 71 | 72 | # weak-map 73 | 74 | Copyright (c) 2011 Google Inc., Kris Kowal 75 | 76 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 77 | 78 | http://www.apache.org/licenses/LICENSE-2.0 79 | 80 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 81 | 82 | 83 | # scmp 84 | 85 | Copyright (c) 2014, Sean Lavine 86 | All rights reserved. 87 | 88 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 89 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 90 | * Neither the name of the scmp project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 91 | 92 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 93 | 94 | 95 | # botkit-studio-metrics 96 | 97 | Copyright (c) ben@howdy.ai 98 | 99 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 100 | 101 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 102 | 103 | 104 | # xmpp.js 105 | 106 | Copyright (c) 2017, xmpp.js 107 | 108 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 109 | 110 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 111 | 112 | 113 | # ampersand-version 114 | 115 | Copyright (c) 2014 &yet, LLC and AmpersandJS contributors 116 | 117 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 118 | 119 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 120 | 121 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 122 | 123 | 124 | # array-next 125 | 126 | Copyright (c) Henrik Joreteg 127 | 128 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 129 | 130 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 131 | 132 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 133 | 134 | 135 | # botbuilder 136 | 137 | Copyright (c) 2016 Microsoft 138 | 139 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 140 | 141 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 142 | 143 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 144 | 145 | 146 | # botkit-studio-sdk 147 | 148 | Copyright (c) howdyai 149 | 150 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 151 | 152 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 153 | 154 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 155 | 156 | 157 | # ciscospark 158 | 159 | Copyright (c) 2016-2018 Cisco and/or its affiliates. All Rights Reserved. 160 | 161 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 162 | 163 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 164 | 165 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 166 | 167 | 168 | # concat-map 169 | 170 | Copyright (c) James Halliday 171 | 172 | Permission is hereby granted, free of charge, to any person obtaining a copy of 173 | this software and associated documentation files (the "Software"), to deal in 174 | the Software without restriction, including without limitation the rights to 175 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 176 | the Software, and to permit persons to whom the Software is furnished to do so, 177 | subject to the following conditions: 178 | 179 | The above copyright notice and this permission notice shall be included in all 180 | copies or substantial portions of the Software. 181 | 182 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 183 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 184 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 185 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 186 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 187 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 188 | 189 | 190 | # envify 191 | 192 | Copyright (c) Hugh Kennedy (http://hughskennedy.com/) 193 | 194 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 195 | 196 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 197 | 198 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 199 | 200 | 201 | # es6-promisify 202 | 203 | Copyright (c) 2014 Mike Hall / Digital Design Labs 204 | 205 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 206 | 207 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 208 | 209 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 210 | 211 | # exit-hook 212 | 213 | "Copyright (c) Sindre Sorhus (sindresorhus.com) 214 | 215 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 216 | 217 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 218 | 219 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." 220 | 221 | # fast-json-stable-stringify 222 | 223 | Copyright (c) James Halliday 224 | 225 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 226 | 227 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 228 | 229 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 230 | 231 | # hooks-fixed 232 | 233 | Copyright (c) Brian Noguchi 234 | 235 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 236 | 237 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 238 | 239 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 240 | 241 | 242 | # inquirer 243 | 244 | Copyright (c) 2012 Simon Boudrias 245 | 246 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 247 | 248 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 249 | 250 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 251 | 252 | # invert-kv 253 | 254 | Copyright (c) Sindre Sorhus (sindresorhus.com) 255 | 256 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 257 | 258 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 259 | 260 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 261 | 262 | 263 | # is-typedarray 264 | 265 | Copyright (c) Hugh Kennedy (http://hughsk.io/) 266 | 267 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 268 | 269 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 270 | 271 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 272 | 273 | 274 | # minimist 275 | 276 | Copyright (c) James Halliday 277 | 278 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 279 | 280 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 281 | 282 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 283 | 284 | 285 | # node-xmpp-client 286 | 287 | Copyright (c) 2016 node-xmpp 288 | 289 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 290 | 291 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 292 | 293 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 294 | 295 | # node-xmpp-core 296 | 297 | Copyright (c) 2016 node-xmpp 298 | 299 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 300 | 301 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 302 | 303 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 304 | 305 | 306 | # node-xmpp-tls-connect 307 | 308 | Copyright (c) 2016 node-xmpp 309 | 310 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 311 | 312 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 313 | 314 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 315 | 316 | 317 | # openurl 318 | 319 | Copyright (c) Axel Rauschmayer 320 | 321 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 322 | 323 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 324 | 325 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 326 | 327 | 328 | # pop-iterate 329 | 330 | Copyright (c) Kris Kowal 331 | 332 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 333 | 334 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 335 | 336 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 337 | 338 | # qbox 339 | 340 | Copyright (c) Arunoda Susiripala 341 | 342 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 343 | 344 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 345 | 346 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 347 | 348 | 349 | # readline2 350 | Copyright (c) 2014 Simon Boudrias 351 | 352 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 353 | 354 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 355 | 356 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 357 | 358 | 359 | # regenerator-runtime 360 | 361 | Copyright (c) 2014-present, Facebook, Inc. 362 | 363 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 364 | 365 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 366 | 367 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 368 | 369 | # rootpath 370 | 371 | Copyright (c) Fabrizio Moscon 372 | 373 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 374 | 375 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 376 | 377 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 378 | 379 | # wordwrap 380 | 381 | Copyright (c) James Halliday 382 | 383 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 384 | 385 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 386 | 387 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.0.2 2 | 3 | * Remove dependencies on Botkit Studio 4 | 5 | # 0.0.1 6 | 7 | First public release 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Instructions for Contributing Code 2 | 3 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 4 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 5 | the rights to use your contribution. For details, visit https://cla.microsoft.com. 6 | 7 | When you submit a pull request, a CLA-bot will automatically determine whether you need to provide 8 | a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions 9 | provided by the bot. You will only need to do this once across all repos using our CLA. 10 | 11 | When you submit a pull request, a CLA-bot will automatically determine whether you need to provide 12 | a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions 13 | provided by the bot. You will only need to do this once across all repos using our CLA. 14 | 15 | ## Code of Conduct 16 | 17 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 18 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright XOXCO, Inc, http://xoxco.com, http://howdy.ai 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Botkit Starter Kit", 3 | "description": "A starting point for building custom Slack applications", 4 | "repository": "https://github.com/howdyai/botkit-starter-slack", 5 | "keywords": ["node", "bots", "slack","botkit"], 6 | "website": "https://studio.botkit.ai/", 7 | "success_url":"/", 8 | "addons":[ 9 | { 10 | "plan": "mongolab", 11 | "as": "MONGO" 12 | } 13 | ], 14 | "env": { 15 | "clientId": { 16 | "description": "Client ID provided by Slack" 17 | }, 18 | "clientSecret": { 19 | "description": "Client Secret provided by Slack" 20 | }, 21 | "studio_token": { 22 | "description": "Token associated with your bot in Botkit Studio", 23 | "required":false 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /bot.js: -------------------------------------------------------------------------------- 1 | /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2 | ______ ______ ______ __ __ __ ______ 3 | /\ == \ /\ __ \ /\__ _\ /\ \/ / /\ \ /\__ _\ 4 | \ \ __< \ \ \/\ \ \/_/\ \/ \ \ _"-. \ \ \ \/_/\ \/ 5 | \ \_____\ \ \_____\ \ \_\ \ \_\ \_\ \ \_\ \ \_\ 6 | \/_____/ \/_____/ \/_/ \/_/\/_/ \/_/ \/_/ 7 | 8 | 9 | # EXTEND THE BOT: 10 | 11 | Botkit has many features for building cool and useful bots! 12 | 13 | Read all about it here: 14 | 15 | -> http://howdy.ai/botkit 16 | 17 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 18 | var env = require('node-env-file'); 19 | env(__dirname + '/.env'); 20 | 21 | 22 | var Botkit = require('botkit'); 23 | var debug = require('debug')('botkit:main'); 24 | 25 | var bot_options = { 26 | replyWithTyping: true, 27 | }; 28 | 29 | // Use a mongo database if specified, otherwise store in a JSON file local to the app. 30 | // Mongo is automatically configured when deploying to Heroku 31 | if (process.env.MONGO_URI) { 32 | // create a custom db access method 33 | var db = require(__dirname + '/components/database.js')({}); 34 | bot_options.storage = db; 35 | } else { 36 | bot_options.json_file_store = __dirname + '/.data/db/'; // store user data in a simple JSON format 37 | } 38 | 39 | // Create the Botkit controller, which controls all instances of the bot. 40 | var controller = Botkit.socketbot(bot_options); 41 | 42 | // Set up an Express-powered webserver to expose oauth and webhook endpoints 43 | var webserver = require(__dirname + '/components/express_webserver.js')(controller); 44 | 45 | // Load in some helpers that make running Botkit on Glitch.com better 46 | require(__dirname + '/components/plugin_glitch.js')(controller); 47 | 48 | // Load in a plugin that defines the bot's identity 49 | require(__dirname + '/components/plugin_identity.js')(controller); 50 | 51 | // Open the web socket server 52 | controller.openSocketServer(controller.httpserver); 53 | 54 | // Start the bot brain in motion!! 55 | controller.startTicking(); 56 | 57 | var normalizedPath = require("path").join(__dirname, "skills"); 58 | require("fs").readdirSync(normalizedPath).forEach(function(file) { 59 | require("./skills/" + file)(controller); 60 | }); 61 | 62 | console.log('I AM ONLINE! COME TALK TO ME: http://localhost:' + (process.env.PORT || 3000)) 63 | 64 | function usage_tip() { 65 | console.log('~~~~~~~~~~'); 66 | console.log('Botkit Starter Kit'); 67 | console.log('Execute your bot application like this:'); 68 | console.log('PORT=3000 node bot.js'); 69 | console.log('~~~~~~~~~~'); 70 | } -------------------------------------------------------------------------------- /components/database.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | var ObjectId = mongoose.Schema.Types.ObjectId; 4 | var debug = require('debug')('botkit:db'); 5 | module.exports = function(config) { 6 | 7 | 8 | mongoose.connect(process.env.MONGO_URI, { 9 | useMongoClient: true 10 | }); 11 | 12 | mongoose.Promise = global.Promise; 13 | 14 | var db = mongoose.connection; 15 | db.on('error', console.error.bind(console, 'connection error:')); 16 | db.once('open', function() { 17 | // we're connected! 18 | debug('CONNECTED TO DB!'); 19 | }); 20 | 21 | 22 | var userSchema = new Schema({ 23 | id: { 24 | type: String, 25 | index: true, 26 | }, 27 | name: String, 28 | attributes: { 29 | type: Object, 30 | default: {}, 31 | }, 32 | }) 33 | 34 | var users = mongoose.model('user', userSchema); 35 | 36 | 37 | var historySchema = new Schema({ 38 | userId: { 39 | type: String, 40 | index: true, 41 | }, 42 | message: {}, 43 | date: { 44 | type: Date, 45 | default: Date.now, 46 | } 47 | }) 48 | 49 | var history = mongoose.model('history', historySchema); 50 | 51 | return { 52 | teams: { 53 | get: function(id, cb) { 54 | }, 55 | save: function(data, cb) { 56 | }, 57 | all: function(cb) { 58 | }, 59 | count: function(cb) { 60 | }, 61 | find: function(data, cb) { 62 | } 63 | }, 64 | users: { 65 | get: function(id, cb) { 66 | return users.findOne({ 67 | id: id 68 | }, cb); 69 | 70 | }, 71 | save: function(data, cb) { 72 | users.findOne({ 73 | id: data.id 74 | }, function(err, user) { 75 | if (err) { 76 | if (cb) return cb(err); 77 | } 78 | if (!user) { 79 | user = new users(data); 80 | } 81 | 82 | // copy values 83 | for (var key in data) { 84 | user[key] = data[key]; 85 | } 86 | user.save(function(err) { 87 | if (cb) cb(err, user); 88 | }); 89 | }); 90 | }, 91 | all: function(cb) { 92 | return users.find({}, cb); 93 | }, 94 | find: function(data, cb) { 95 | return users.find(data, cb); 96 | }, 97 | }, 98 | history: { 99 | addToHistory: function(message, user) { 100 | return new Promise(function(resolve, reject) { 101 | var hist = new history({userId: user, message: message}); 102 | hist.save(function(err) { 103 | if (err) { return reject(err) } 104 | resolve(hist); 105 | }); 106 | }); 107 | }, 108 | getHistoryForUser: function(user, limit) { 109 | return new Promise(function(resolve, reject) { 110 | history.find({userId: user}).sort({date: -1}).limit(limit).exec(function(err, history) { 111 | if (err) { return reject(err) } 112 | resolve(history.reverse()); 113 | }); 114 | }); 115 | } 116 | }, 117 | channels: { 118 | get: function(id, cb) { 119 | }, 120 | save: function(data, cb) { 121 | }, 122 | all: function(cb) { 123 | }, 124 | find: function(data, cb) { 125 | } 126 | } 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /components/express_webserver.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var bodyParser = require('body-parser'); 3 | var querystring = require('querystring'); 4 | var debug = require('debug')('botkit:webserver'); 5 | var http = require('http'); 6 | var fs = require('fs'); 7 | var hbs = require('express-hbs'); 8 | 9 | 10 | module.exports = function(controller) { 11 | 12 | 13 | var webserver = express(); 14 | webserver.use(bodyParser.json()); 15 | webserver.use(bodyParser.urlencoded({ extended: true })); 16 | 17 | // set up handlebars ready for tabs 18 | webserver.engine('hbs', hbs.express4({partialsDir: __dirname + '/../views/partials'})); 19 | webserver.set('view engine', 'hbs'); 20 | webserver.set('views', __dirname + '/../views/'); 21 | 22 | webserver.use(express.static('public')); 23 | 24 | var server = http.createServer(webserver); 25 | 26 | server.listen(process.env.PORT || 3000, null, function() { 27 | 28 | debug('Express webserver configured and listening at http://localhost:' + process.env.PORT || 3000); 29 | 30 | }); 31 | 32 | // import all the pre-defined routes that are present in /components/routes 33 | var normalizedPathToRoutes = require('path').join(__dirname, 'routes'); 34 | if (fs.existsSync(normalizedPathToRoutes)) { 35 | fs.readdirSync(normalizedPathToRoutes).forEach(function (file) { 36 | require('./routes/' + file)(webserver, controller); 37 | }); 38 | } 39 | 40 | controller.webserver = webserver; 41 | controller.httpserver = server; 42 | 43 | return webserver; 44 | 45 | }; 46 | -------------------------------------------------------------------------------- /components/plugin_glitch.js: -------------------------------------------------------------------------------- 1 | var request = require('request'); 2 | module.exports = function(controller) { 3 | 4 | function keepalive() { 5 | 6 | request({ 7 | url: 'http://' + process.env.PROJECT_DOMAIN + '.glitch.me', 8 | }, function(err) { 9 | 10 | setTimeout(function() { 11 | keepalive(); 12 | }, 55000); 13 | 14 | }); 15 | 16 | } 17 | 18 | // if this is running on Glitch 19 | if (process.env.PROJECT_DOMAIN) { 20 | 21 | // make a web call to self every 55 seconds 22 | // in order to avoid the process being put to sleep. 23 | keepalive(); 24 | 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /components/plugin_identity.js: -------------------------------------------------------------------------------- 1 | module.exports = function(controller) { 2 | 3 | controller.middleware.spawn.use(function(bot, next) { 4 | 5 | bot.identity = { 6 | name: 'Botkit for Web', 7 | id: 'web', 8 | } 9 | 10 | }); 11 | 12 | 13 | controller.middleware.receive.use(function(bot, message, next) { 14 | 15 | if (!message.user_profile) { 16 | next(); 17 | } else { 18 | controller.storage.users.get(message.user, function(err, user) { 19 | if (!user) { 20 | user = { 21 | id: message.user, 22 | attributes: {}, 23 | } 24 | } 25 | 26 | user.name = message.user_profile.name; 27 | 28 | for (var key in message.user_profile) { 29 | if (key != 'name' && key != 'id') { 30 | user.attributes[key] = message.user_profile[key]; 31 | } 32 | } 33 | 34 | controller.storage.users.save(user, function(err) { 35 | next(); 36 | }); 37 | 38 | }); 39 | } 40 | 41 | }); 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /components/routes/embed.js: -------------------------------------------------------------------------------- 1 | module.exports = function(webserver, controller) { 2 | 3 | 4 | // This creates the /embed route, where an easy-to-copy embed code is available 5 | webserver.get('/embed', function(req,res) { 6 | 7 | res.render('embed', { 8 | layout: 'layouts/default', 9 | base_url: req.hostname 10 | }); 11 | 12 | }); 13 | 14 | // This creates the /embed route, where an easy-to-copy embed code is available 15 | webserver.get('/', function(req,res) { 16 | 17 | res.render('index', { 18 | layout: 'layouts/default', 19 | base_url: req.hostname 20 | }); 21 | 22 | }); 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /components/routes/incoming_webhooks.js: -------------------------------------------------------------------------------- 1 | var debug = require('debug')('botkit:incoming_webhooks'); 2 | 3 | module.exports = function(webserver, controller) { 4 | 5 | debug('Configured /botkit/receive url'); 6 | webserver.post('/botkit/receive', function(req, res) { 7 | 8 | // respond to Slack that the webhook has been received. 9 | res.status(200); 10 | 11 | // Now, pass the webhook into be processed 12 | controller.handleWebhookPayload(req, res); 13 | 14 | }); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /components/routes/sample.js: -------------------------------------------------------------------------------- 1 | module.exports = function(webserver, controller) { 2 | 3 | // add custom routes for your web server here. 4 | 5 | webserver.get('/sample', function(req,res) { 6 | 7 | res.send('Sample route'); 8 | 9 | }); 10 | 11 | 12 | } 13 | -------------------------------------------------------------------------------- /docs/botkit_chat_server.md: -------------------------------------------------------------------------------- 1 | # Botkit Anywhere Chat Server 2 | 3 | In order for users to talk to your bot, the messages have to be sent to and received from the bot application. In traditional chat environments, the sending and receiving is done by an external chat service - like Slack, Facebook Messenger, or in the good ol' days, an IRC server. 4 | 5 | Botkit now includes a built-in chat server, so that messages can be sent and received directly to the bot application without relying on a third-party. The server can be used in two ways: with real-time websocket connections, or with traditional http webhooks. 6 | 7 | ## Using Websockets 8 | 9 | The best way to connect to your bot is using websockets, which enables real-time two-way delivery of messages. Websocket connections remain open, which allows the bot to send multiple messages in sequence, or take longer than usual to process a request before sending a response. 10 | 11 | [The bundled web chat client](botkit_web_client.md) is configured to use websockets by default, and handles all of the complexity of managing the connection, including gracefully handling disconnects and reconnects that may occur. See [connection events below](#connection-events). 12 | 13 | The websocket server accepts connections at the root URL of your Botkit application. If your application is served over SSL, the url will be `wss://`. Otherwise, use the unencrypted url at `ws://` 14 | 15 | 16 | ## Using Webhooks 17 | 18 | If using websockets does not work for your application, Botkit can also receive messages via webhooks. 19 | 20 | When using the webhooks to communicate with Botkit, several limitations apply: 21 | 22 | * The bot MUST respond to every incoming request with some sort of reply 23 | * The bot can only send a SINGLE reply to any incoming message 24 | * Your bot cannot spontaneously message the user without first receiving a message (or other event trigger) 25 | 26 | Messages can be sent to Botkit by sending a `POST` to `/botkit/receive` with a message object as the body of the request. 27 | 28 | ``` 29 | POST /botkit/receive 30 | 31 | { 32 | "text": "Message Text", 33 | "user": "unique user id", 34 | "channel": "webhook" 35 | } 36 | ``` 37 | 38 | The response to this request will be a message object representing the reply from the bot. The reply will be in the same format as the incoming message, with a `text`, `user`, and `channel` field, along with any additional attachments or custom fields sent by the bot. 39 | 40 | ## Connection events 41 | 42 | When first establishing a connection, the client will send a connect event - either a `hello` event, or a `welcome_back` event. These events tell the bot that a user has connected and is ready to chat. 43 | 44 | `hello` events occur when a brand new user opens their first chat session. 45 | 46 | `welcome_back` events occur when a known user returns to the chat. 47 | 48 | In addition, the bot may fire a `reconnect` event. These occur when an ongoing chat is disrupted by a disconnect/reconnect, and may indicate that some messages may not have been delivered. 49 | 50 | These events can be [handled with skill modules](how_to_build_skills.md) to do things like send greetings or trigger analytics events. 51 | 52 | ## Enable Message History 53 | 54 | Botkit Anywhere comes bundled with a message history feature which will capture and track all messages sent during a user's communication with the bot. This log of messages is available via an API call which is used by the built-in client to display previously sent messages when a user reconnects. 55 | 56 | This is particularly useful when the bot is embedded in a site, as each time a user navigates to a new page, the chat widget will have to establish a brand new connection and repopulate the interface. 57 | 58 | By default, the message history feature is disabled. Before you enable this plugin, you must first implement an identity verification mechanism for your users. As implemented, calls to this API are not authenticated, which would allow potentially sensitive information contained in message history to be exposed. This module should be considered a proof of concept reference design only, and is not production ready! 59 | 60 | To enable this module, you'll need to uncomment the code, implement additional security measures, and specify a MongoDB connect string in your applications environment as `MONGO_URI`. Once set, your Botkit will automatically capture and store messages, as well as serve them up to the web chat client. 61 | -------------------------------------------------------------------------------- /docs/botkit_chat_widget.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/howdyai/botkit-starter-web/cfb7a80add3186ea8dcb51b8764f907302db64cc/docs/botkit_chat_widget.png -------------------------------------------------------------------------------- /docs/botkit_web_client.md: -------------------------------------------------------------------------------- 1 | # Botkit Anywhere Web Chat Client 2 | 3 | Included in this project is a web-based chat client for use with Botkit Anywhere's built-in chat service. 4 | This client is built with HTML, CSS and Javascript. 5 | It is our hope that it will be easy to customize both the design and the functionality of the chat client with minimal effort. 6 | 7 | There are three ways to use the web client: 8 | 9 | 1. [Embed the widget using an iFrame](#embed-botkit-in-a-website-with-iframes) 10 | 2. Link directly to full-screen chat interface 11 | 3. Embed the chat interface markup, css and Javascript into an existing web page 12 | 13 | ### Files 14 | 15 | The web chat client is composed of these files: 16 | 17 | * HTML user interface [public/chat.html](../public/chat.html) 18 | * Javascript application [public/client.js](../public/client.js) 19 | * Compiled CSS theme [public/css/styles.css](../public/styles.css) 20 | * Source SASS file [sass/_chat.scss](../sass/_chat.scss) 21 | 22 | Additional functionality used by the iframe embed: 23 | 24 | * Embedded chat app [public/embed.js](../public/embed.js) 25 | * Compiled CSS for embedded window [public/css/embed.css](../public/embed.css) 26 | * Source SASS file [sass/embed.scss](../sass/embed.scss) 27 | 28 | ## Client API 29 | 30 | #### Botkit.boot(user) 31 | 32 | Initialize the embedded chat widget. This will cause the widget to connect to the chat service and start up. 33 | 34 | If your website has user accounts, and you want to identify the existing user to the chat widget, 35 | you can include user profile information in the call to Botkit.boot(). 36 | The user object can contain the following fields: 37 | 38 | | Field | Description 39 | |--- |--- 40 | | id | REQUIRED a unique identifier of this user 41 | | name | the username / display name of the user 42 | | first_name | first name 43 | | last_name | last name 44 | | full_name | full name 45 | | gender | 'male' or 'female' 46 | | timezone | Timezone description "America/Chicago" 47 | 48 | #### Botkit.identifyUser(user) 49 | 50 | Identify an existing user to the chat widget. This can be used to identify the user AFTER the chat begins, instead of passing in the information to Botkit.boot(). 51 | 52 | ## Share User Accounts / Profile data with Botkit 53 | 54 | In order to seamlessly integrate your new bot with your existing app or website, Botkit supports sharing user account information between the main app and the bot. 55 | 56 | To do this, either call [Botkit.boot(user)](#botkitbootuser) with the optional user profile parameter, or call [Botkit.identifyUser(user)](#botkitidentifyuseruser) after the connection has been established. 57 | 58 | It is important to note that Botkit does not provide any mechanism for validating or verifying the identity of the user passed in via these mechanisms. Used without validation, users can potentially access content associated with other users. For this reason, we recommend that Botkit Anywhere not be used to handle sensitive or private information unless substantial security mechanisms are put in place. 59 | 60 | If provided, user information will be used in a variety of ways: 61 | 62 | * [Message history](botkit_chat_server.md#enable-message-history) will be associated with the existing user account 63 | * Your Botkit application can access the account information using the [built-in storage mechanisms](https://github.com/howdyai/botkit/blob/master/docs/storage.md), making it easier to use this information during conversations or inside [skill modules](how_to_build_skills.md). 64 | 65 | ## Embed Botkit in a Website with iFrames 66 | 67 | Botkit's web chat can be added to a website using an iframe to embed the chat functionality. 68 | The starter kit application includes a page with the necessary embed code that can be copy-pasted into the source of your website. 69 | 70 | An example embed code is below. It includes the markup for an iframe, as well as Javascript and CSS links for 71 | styling the embedded chat widget. Note, replace `{{base_url}}` with the url of your Botkit application. 72 | 73 | ```html 74 |
75 |
Chat
76 | 77 |
78 | 79 | 83 | 84 | 85 | ``` 86 | 87 | 88 | Using the code above and the built-in stylesheet, the chat widget will look like this: 89 | 90 | ![Botkit Chat Widget](botkit_chat_widget.png) 91 | 92 | Once embedded, the chat widget has a few additional methods that allow you to control it from the main web page. 93 | 94 | #### Botkit.activate() 95 | 96 | Activate the chat widget. If using the built-in stylesheet, this will cause the widget to slide up from the bottom of the screen. 97 | 98 | #### Botkit.deactivate() 99 | 100 | Deactivate the chat widget. If using the built-in stylesheet, this will cause the widget to disappear, leaving only a small "Chat" widget docked to the bottom of the page. 101 | 102 | #### Botkit.toggle() 103 | 104 | Toggle the active state of the chat widget. 105 | 106 | 107 | ## Customize the look and feel of the chat interface 108 | 109 | The web chat interface is built with HTML and CSS. The look and feel can be extensively customized by modifying the underlying source code. The chat interface is rendered using the [Handlebars template language](http://handlebarsjs.com/). 110 | 111 | The uncompiled SASS used to style the web chat is included in the starter kit as `sass/_chat.scss` Changes made to this file must be compiled into the final stylesheet. To do this, run the following command from the root of the starter kit: 112 | 113 | ``` 114 | sass --update sass/:public/css/ 115 | ``` 116 | 117 | The annotated boilerplate HTML for Botkit's web client is below. 118 | 119 | Note: The functionality of the web chat interface uses the `id` attributes of elements in the template to identify and use them. If you want to use the built in client app, it is important to leave these attributes intact in the template file! 120 | 121 | ```html 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 |
131 | 132 |
133 | Disconnected... reconnecting! 134 |
135 | 136 | 137 |
138 | Offline! Reload to reconnect. 139 |
140 | 141 | 142 |
143 |
144 | 145 | 149 |
150 |
151 | {{#if message.isTyping}} 152 |
153 | ... 154 |
155 | {{/if}} 156 | 157 | {{{message.html}}} 158 | 159 | 160 | {{#if message.open_link}} 161 | {{#if message.link_title}}{{message.link_title}}{{else}}{{message.open_link}}{{/if}} 162 | {{/if}} 163 | 164 | 165 | {{#message.files}} 166 | {{#if image}} 167 | {{{url}}} 168 | {{else}} 169 | {{{url}}} 170 | {{/if}} 171 | {{/message.files}} 172 |
173 |
174 |
175 |
176 | 177 | 178 |
179 |
180 | 181 | 182 |
183 |
184 | 185 | 186 |
187 |
188 |
189 | ``` 190 | 191 | ## Using Botkit CMS custom fields to add custom features 192 | 193 | Botkit CMS's dialog authoring tool supports the ability to add custom fields to any message. 194 | These fields are passed unmodified through the client to your chat widget, and can be used in the template. 195 | 196 | You can include any fields you want in your message objects using this feature, and render them however you desire. The Handlebars template system supports conditionals, iterators and other helper functions which can be used to create interactive elements and more complex attachments. 197 | 198 | For example, the code above looks for a `message.open_link` field. This is not a standard Botkit 199 | message field, but can be added to a message using custom fields. If present, a special link button will be added to the rendered message. 200 | -------------------------------------------------------------------------------- /docs/edit_menu.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/howdyai/botkit-starter-web/cfb7a80add3186ea8dcb51b8764f907302db64cc/docs/edit_menu.gif -------------------------------------------------------------------------------- /docs/how_to_build_skills.md: -------------------------------------------------------------------------------- 1 | # How to build custom skill modules for Botkit 2 | 3 | Botkit bots can do a lot right out of the box, without having to write any code. 4 | But to do the type of jobs bots do - like read and write databases, use APIs, 5 | and interact with files - some code is necessary. 6 | 7 | When you want to add features like this to a Botkit application, 8 | the right place to do that is in a skill module. 9 | Skill modules are Javascript files that live in the projects `skills/` folder, 10 | and get automatically loaded into the application when it boots. 11 | 12 | Skill modules can contain any type of code, but most of the time will consist 13 | of one or more Botkit event handers, middlewares, or related features. [Refer to the main Botkit READMEs for full documentation.](https://botkit.ai/docs/core.html) 14 | 15 | Sample code for a simple skill module is below. To enable this module, copy this code into a file in this project called `skills/example.js` and restart your bot. 16 | 17 | ```js 18 | module.exports = function(controller) { 19 | 20 | // My custom skill module! 21 | // controller is a Botkit application instance 22 | // I can add handlers and specify middlewares here! 23 | 24 | // respond to the `hello` event, fired when a web chat begins with a new user 25 | controller.on('hello', function(bot, message) { 26 | 27 | bot.reply(message,'Welcome, new human!'); 28 | 29 | }); 30 | 31 | // listen for the word "help" 32 | controller.hears('help', 'message_received', function(bot, message) { 33 | 34 | bot.reply(message,'Need help? I am here!'); 35 | 36 | }); 37 | 38 | // add a middleware to log messages sent to the console 39 | controller.middleware.send.use(function(bot, message, next) { 40 | console.log('Sending: ', message); 41 | next(); 42 | }); 43 | 44 | } 45 | ``` 46 | 47 | ## Botkit CMS Skills 48 | 49 | Conversations powered by Botkit CMS's content management APIs have additional middleware endpoints and events that can be used to extend and alter the functionality of the pre-scripted conversations. 50 | 51 | * [Learn more about Botkit CMS script events](https://botkit.ai/docs/readme-studio.html#controllerstudiobefore) 52 | 53 | A skill module built to work with a Botkit CMS script called "onboarding" might look something like this. Note that rather than directly using Botkit's `hears` and `on` handlers, this module uses Botkit CMS's more sophisticated conversation events that provide a cleaner separation of content and functionality. 54 | 55 | ```js 56 | module.exports = function(controller) { 57 | 58 | // this function will before BEFORE the onboarding script runs 59 | controller.studio.before('onboarding', function(convo, next) { 60 | 61 | console.log('STARTING ONBOARDING PROCESS FOR ', convo.context.user); 62 | 63 | next(); 64 | 65 | }); 66 | 67 | // this function will fire AFTER the onboarding script runs 68 | controller.studio.after('onboarding', function(convo, next) { 69 | 70 | console.log('COMPLETED ONBOARDING FOR ', convo.context.user); 71 | 72 | }); 73 | 74 | // this function will fire whenever a user tells the bot what username they desire 75 | controller.studio.validate('onboarding','username', function(convo, next) { 76 | 77 | var username = convo.extractResponse('username'); 78 | 79 | // make sure user's requested username field is less than 8 chars 80 | if (username.length > 8) { 81 | convo.gotoThread('username_length'); 82 | } else { 83 | next(); 84 | } 85 | 86 | }); 87 | } 88 | ``` 89 | -------------------------------------------------------------------------------- /glitch_readme.md: -------------------------------------------------------------------------------- 1 | # Howdy, bot builder! 2 | ## When you see "Show Live" in the upper left corner, your new bot app is live. 3 | ## Click the sunglasses to view your app! 4 | 5 | ### Having trouble? 6 | * You should check [that your .env file](?path=.env:1:0) has all the correct tokens. 7 | 8 | * *Raise your hand!* - Glitch allows users to ask the community for help directly from the editor! For more information on raising your hand, [read this blog post.](https://medium.com/glitch/just-raise-your-hand-how-glitch-helps-aa6564cb1685) 9 | 10 | * Join our thriving community of Botkit developers and bot enthusiasts at large. Over 9000 members strong, [our open community group](http://community.botkit.ai) is _the place_ for people interested in the art and science of making bots. 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "botkit-starter-web", 3 | "version": "0.0.2", 4 | "description": "A starter kit for building a custom bot for your website with Botkit Studio", 5 | "main": "bot.js", 6 | "scripts": { 7 | "start": "node bot.js" 8 | }, 9 | "dependencies": { 10 | "body-parser": "^1.18.3", 11 | "botkit": "^0.7.0", 12 | "botkit-storage-mongo": "^1.0.6", 13 | "debug": "^3.1.0", 14 | "express": "^4.16.4", 15 | "express-hbs": "^1.0.4", 16 | "mongoose": "^4.13.7", 17 | "node-env-file": "^0.1.8", 18 | "querystring": "^0.2.0", 19 | "request": "^2.88.0" 20 | }, 21 | "devDependencies": {}, 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/howdyai/botkit-starter-web.git" 25 | }, 26 | "bugs": { 27 | "url": "https://github.com/howdyai/botkit-starter-web/issues" 28 | }, 29 | "homepage": "https://github.com/howdyai/botkit-starter-web", 30 | "keywords": [ 31 | "bots", 32 | "chatbots", 33 | "web chat" 34 | ], 35 | "author": "ben@howdy.ai", 36 | "license": "MIT" 37 | } 38 | -------------------------------------------------------------------------------- /public/beep.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/howdyai/botkit-starter-web/cfb7a80add3186ea8dcb51b8764f907302db64cc/public/beep.mp3 -------------------------------------------------------------------------------- /public/bigger.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |
11 | Disconnected... reconnecting! 12 |
13 |
14 |
15 |
16 |
17 | {{#if message.typing}} 18 |
19 | ... 20 |
21 | {{/if}} 22 | {{{message.html}}} 23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | 32 | 33 |
34 |
35 |
36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /public/botkit_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/howdyai/botkit-starter-web/cfb7a80add3186ea8dcb51b8764f907302db64cc/public/botkit_icon.png -------------------------------------------------------------------------------- /public/chat.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Botkit Anywhere 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 |
14 | Disconnected... reconnecting! 15 |
16 |
17 | Offline! Reload to reconnect. 18 |
19 |
20 | Powered by Botkit Logo Botkit 21 |
22 |
23 |
24 |
25 |
26 | {{#if message.isTyping}} 27 |
28 | 29 | 30 | 31 |
32 | {{/if}} 33 | {{{message.html}}} 34 | 35 | {{#if message.open_link}} 36 | {{#if message.link_title}}{{message.link_title}}{{else}}{{message.open_link}}{{/if}} 37 | {{/if}} 38 |
39 | {{#message.files}} 40 |
41 | {{#if image}} 42 | {{{url}}} 43 | {{else}} 44 | {{{url}}} 45 | {{/if}} 46 |
47 | {{/message.files}} 48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | 56 | 57 |
58 |
59 |
60 |
61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /public/client.js: -------------------------------------------------------------------------------- 1 | var converter = new showdown.Converter(); 2 | converter.setOption('openLinksInNewWindow', true); 3 | 4 | var Botkit = { 5 | config: { 6 | ws_url: (location.protocol === 'https:' ? 'wss' : 'ws') + '://' + location.host, 7 | reconnect_timeout: 3000, 8 | max_reconnect: 5 9 | }, 10 | options: { 11 | sound: false, 12 | use_sockets: true 13 | }, 14 | reconnect_count: 0, 15 | guid: null, 16 | current_user: null, 17 | on: function(event, handler) { 18 | this.message_window.addEventListener(event, function(evt) { 19 | handler(evt.detail); 20 | }); 21 | }, 22 | trigger: function(event, details) { 23 | var event = new CustomEvent(event, { 24 | detail: details 25 | }); 26 | this.message_window.dispatchEvent(event); 27 | }, 28 | request: function(url, body) { 29 | var that = this; 30 | return new Promise(function(resolve, reject) { 31 | var xmlhttp = new XMLHttpRequest(); 32 | 33 | xmlhttp.onreadystatechange = function() { 34 | if (xmlhttp.readyState == XMLHttpRequest.DONE) { 35 | if (xmlhttp.status == 200) { 36 | var response = xmlhttp.responseText; 37 | var message = null; 38 | try { 39 | message = JSON.parse(response); 40 | } catch (err) { 41 | reject(err); 42 | return; 43 | } 44 | resolve(message); 45 | } else { 46 | reject(new Error('status_' + xmlhttp.status)); 47 | } 48 | } 49 | }; 50 | 51 | xmlhttp.open("POST", url, true); 52 | xmlhttp.setRequestHeader("Content-Type", "application/json"); 53 | xmlhttp.send(JSON.stringify(body)); 54 | }); 55 | 56 | }, 57 | send: function(text, e) { 58 | var that = this; 59 | if (e) e.preventDefault(); 60 | if (!text) { 61 | return; 62 | } 63 | var message = { 64 | type: 'outgoing', 65 | text: text 66 | }; 67 | 68 | this.clearReplies(); 69 | that.renderMessage(message); 70 | 71 | that.deliverMessage({ 72 | type: 'message', 73 | text: text, 74 | user: this.guid, 75 | channel: this.options.use_sockets ? 'socket' : 'webhook' 76 | }); 77 | 78 | this.input.value = ''; 79 | 80 | this.trigger('sent', message); 81 | 82 | return false; 83 | }, 84 | deliverMessage: function(message) { 85 | if (this.options.use_sockets) { 86 | this.socket.send(JSON.stringify(message)); 87 | } else { 88 | this.webhook(message); 89 | } 90 | }, 91 | getHistory: function(guid) { 92 | var that = this; 93 | if (that.guid) { 94 | that.request('/botkit/history', { 95 | user: that.guid 96 | }).then(function(history) { 97 | if (history.success) { 98 | that.trigger('history_loaded', history.history); 99 | } else { 100 | that.trigger('history_error', new Error(history.error)); 101 | } 102 | }).catch(function(err) { 103 | that.trigger('history_error', err); 104 | }); 105 | } 106 | }, 107 | webhook: function(message) { 108 | var that = this; 109 | 110 | that.request('/botkit/receive', message).then(function(message) { 111 | that.trigger(message.type, message); 112 | }).catch(function(err) { 113 | that.trigger('webhook_error', err); 114 | }); 115 | 116 | }, 117 | connect: function(user) { 118 | 119 | var that = this; 120 | 121 | if (user && user.id) { 122 | Botkit.setCookie('botkit_guid', user.id, 1); 123 | 124 | user.timezone_offset = new Date().getTimezoneOffset(); 125 | that.current_user = user; 126 | console.log('CONNECT WITH USER', user); 127 | } 128 | 129 | // connect to the chat server! 130 | if (that.options.use_sockets) { 131 | that.connectWebsocket(that.config.ws_url); 132 | } else { 133 | that.connectWebhook(); 134 | } 135 | 136 | }, 137 | connectWebhook: function() { 138 | var that = this; 139 | if (Botkit.getCookie('botkit_guid')) { 140 | that.guid = Botkit.getCookie('botkit_guid'); 141 | connectEvent = 'welcome_back'; 142 | } else { 143 | that.guid = that.generate_guid(); 144 | Botkit.setCookie('botkit_guid', that.guid, 1); 145 | } 146 | 147 | that.getHistory(); 148 | 149 | // connect immediately 150 | that.trigger('connected', {}); 151 | that.webhook({ 152 | type: connectEvent, 153 | user: that.guid, 154 | channel: 'webhook', 155 | }); 156 | 157 | }, 158 | connectWebsocket: function(ws_url) { 159 | var that = this; 160 | // Create WebSocket connection. 161 | that.socket = new WebSocket(ws_url); 162 | 163 | var connectEvent = 'hello'; 164 | if (Botkit.getCookie('botkit_guid')) { 165 | that.guid = Botkit.getCookie('botkit_guid'); 166 | connectEvent = 'welcome_back'; 167 | } else { 168 | that.guid = that.generate_guid(); 169 | Botkit.setCookie('botkit_guid', that.guid, 1); 170 | } 171 | 172 | that.getHistory(); 173 | 174 | // Connection opened 175 | that.socket.addEventListener('open', function(event) { 176 | console.log('CONNECTED TO SOCKET'); 177 | that.reconnect_count = 0; 178 | that.trigger('connected', event); 179 | that.deliverMessage({ 180 | type: connectEvent, 181 | user: that.guid, 182 | channel: 'socket', 183 | user_profile: that.current_user ? that.current_user : null, 184 | }); 185 | }); 186 | 187 | that.socket.addEventListener('error', function(event) { 188 | console.error('ERROR', event); 189 | }); 190 | 191 | that.socket.addEventListener('close', function(event) { 192 | console.log('SOCKET CLOSED!'); 193 | that.trigger('disconnected', event); 194 | if (that.reconnect_count < that.config.max_reconnect) { 195 | setTimeout(function() { 196 | console.log('RECONNECTING ATTEMPT ', ++that.reconnect_count); 197 | that.connectWebsocket(that.config.ws_url); 198 | }, that.config.reconnect_timeout); 199 | } else { 200 | that.message_window.className = 'offline'; 201 | } 202 | }); 203 | 204 | // Listen for messages 205 | that.socket.addEventListener('message', function(event) { 206 | var message = null; 207 | try { 208 | message = JSON.parse(event.data); 209 | } catch (err) { 210 | that.trigger('socket_error', err); 211 | return; 212 | } 213 | 214 | that.trigger(message.type, message); 215 | }); 216 | }, 217 | clearReplies: function() { 218 | this.replies.innerHTML = ''; 219 | }, 220 | quickReply: function(payload) { 221 | this.send(payload); 222 | }, 223 | focus: function() { 224 | this.input.focus(); 225 | }, 226 | renderMessage: function(message) { 227 | var that = this; 228 | if (!that.next_line) { 229 | that.next_line = document.createElement('div'); 230 | that.message_list.appendChild(that.next_line); 231 | } 232 | if (message.text) { 233 | message.html = converter.makeHtml(message.text); 234 | } 235 | 236 | that.next_line.innerHTML = that.message_template({ 237 | message: message 238 | }); 239 | if (!message.isTyping) { 240 | delete(that.next_line); 241 | } 242 | }, 243 | triggerScript: function(script, thread) { 244 | this.deliverMessage({ 245 | type: 'trigger', 246 | user: this.guid, 247 | channel: 'socket', 248 | script: script, 249 | thread: thread 250 | }); 251 | }, 252 | identifyUser: function(user) { 253 | 254 | user.timezone_offset = new Date().getTimezoneOffset(); 255 | 256 | this.guid = user.id; 257 | Botkit.setCookie('botkit_guid', user.id, 1); 258 | 259 | this.current_user = user; 260 | 261 | this.deliverMessage({ 262 | type: 'identify', 263 | user: this.guid, 264 | channel: 'socket', 265 | user_profile: user, 266 | }); 267 | }, 268 | receiveCommand: function(event) { 269 | switch (event.data.name) { 270 | case 'trigger': 271 | // tell Botkit to trigger a specific script/thread 272 | console.log('TRIGGER', event.data.script, event.data.thread); 273 | Botkit.triggerScript(event.data.script, event.data.thread); 274 | break; 275 | case 'identify': 276 | // link this account info to this user 277 | console.log('IDENTIFY', event.data.user); 278 | Botkit.identifyUser(event.data.user); 279 | break; 280 | case 'connect': 281 | // link this account info to this user 282 | Botkit.connect(event.data.user); 283 | break; 284 | default: 285 | console.log('UNKNOWN COMMAND', event.data); 286 | } 287 | }, 288 | sendEvent: function(event) { 289 | 290 | if (this.parent_window) { 291 | this.parent_window.postMessage(event, '*'); 292 | } 293 | 294 | }, 295 | setCookie: function(cname, cvalue, exdays) { 296 | var d = new Date(); 297 | d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000)); 298 | var expires = "expires=" + d.toUTCString(); 299 | document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"; 300 | }, 301 | getCookie: function(cname) { 302 | var name = cname + "="; 303 | var decodedCookie = decodeURIComponent(document.cookie); 304 | var ca = decodedCookie.split(';'); 305 | for (var i = 0; i < ca.length; i++) { 306 | var c = ca[i]; 307 | while (c.charAt(0) == ' ') { 308 | c = c.substring(1); 309 | } 310 | if (c.indexOf(name) == 0) { 311 | return c.substring(name.length, c.length); 312 | } 313 | } 314 | return ""; 315 | }, 316 | generate_guid: function() { 317 | function s4() { 318 | return Math.floor((1 + Math.random()) * 0x10000) 319 | .toString(16) 320 | .substring(1); 321 | } 322 | return s4() + s4() + '-' + s4() + '-' + s4() + '-' + 323 | s4() + '-' + s4() + s4() + s4(); 324 | }, 325 | boot: function(user) { 326 | 327 | console.log('Booting up'); 328 | 329 | var that = this; 330 | 331 | 332 | that.message_window = document.getElementById("message_window"); 333 | 334 | that.message_list = document.getElementById("message_list"); 335 | 336 | var source = document.getElementById('message_template').innerHTML; 337 | that.message_template = Handlebars.compile(source); 338 | 339 | that.replies = document.getElementById('message_replies'); 340 | 341 | that.input = document.getElementById('messenger_input'); 342 | 343 | that.focus(); 344 | 345 | that.on('connected', function() { 346 | that.message_window.className = 'connected'; 347 | that.input.disabled = false; 348 | that.sendEvent({ 349 | name: 'connected' 350 | }); 351 | }) 352 | 353 | that.on('disconnected', function() { 354 | that.message_window.className = 'disconnected'; 355 | that.input.disabled = true; 356 | }); 357 | 358 | that.on('webhook_error', function(err) { 359 | 360 | alert('Error sending message!'); 361 | console.error('Webhook Error', err); 362 | 363 | }); 364 | 365 | that.on('typing', function() { 366 | that.clearReplies(); 367 | that.renderMessage({ 368 | isTyping: true 369 | }); 370 | }); 371 | 372 | that.on('sent', function() { 373 | if (that.options.sound) { 374 | var audio = new Audio('sent.mp3'); 375 | audio.play(); 376 | } 377 | }); 378 | 379 | that.on('message', function() { 380 | if (that.options.sound) { 381 | var audio = new Audio('beep.mp3'); 382 | audio.play(); 383 | } 384 | }); 385 | 386 | that.on('message', function(message) { 387 | 388 | that.renderMessage(message); 389 | 390 | }); 391 | 392 | that.on('message', function(message) { 393 | if (message.goto_link) { 394 | window.location = message.goto_link; 395 | } 396 | }); 397 | 398 | 399 | that.on('message', function(message) { 400 | that.clearReplies(); 401 | if (message.quick_replies) { 402 | 403 | var list = document.createElement('ul'); 404 | 405 | var elements = []; 406 | for (var r = 0; r < message.quick_replies.length; r++) { 407 | (function(reply) { 408 | 409 | var li = document.createElement('li'); 410 | var el = document.createElement('a'); 411 | el.innerHTML = reply.title; 412 | el.href = '#'; 413 | 414 | el.onclick = function() { 415 | that.quickReply(reply.payload); 416 | } 417 | 418 | li.appendChild(el); 419 | list.appendChild(li); 420 | elements.push(li); 421 | 422 | })(message.quick_replies[r]); 423 | } 424 | 425 | that.replies.appendChild(list); 426 | 427 | // uncomment this code if you want your quick replies to scroll horizontally instead of stacking 428 | // var width = 0; 429 | // // resize this element so it will scroll horizontally 430 | // for (var e = 0; e < elements.length; e++) { 431 | // width = width + elements[e].offsetWidth + 18; 432 | // } 433 | // list.style.width = width + 'px'; 434 | 435 | if (message.disable_input) { 436 | that.input.disabled = true; 437 | } else { 438 | that.input.disabled = false; 439 | } 440 | } else { 441 | that.input.disabled = false; 442 | } 443 | }); 444 | 445 | that.on('history_loaded', function(history) { 446 | if (history) { 447 | for (var m = 0; m < history.length; m++) { 448 | that.renderMessage({ 449 | text: history[m].text, 450 | type: history[m].type == 'message_received' ? 'outgoing' : 'incoming', // set appropriate CSS class 451 | }); 452 | } 453 | } 454 | }); 455 | 456 | 457 | if (window.self !== window.top) { 458 | // this is embedded in an iframe. 459 | // send a message to the master frame to tell it that the chat client is ready 460 | // do NOT automatically connect... rather wait for the connect command. 461 | that.parent_window = window.parent; 462 | window.addEventListener("message", that.receiveCommand, false); 463 | that.sendEvent({ 464 | type: 'event', 465 | name: 'booted' 466 | }); 467 | console.log('Messenger booted in embedded mode'); 468 | 469 | } else { 470 | 471 | console.log('Messenger booted in stand-alone mode'); 472 | // this is a stand-alone client. connect immediately. 473 | that.connect(user); 474 | } 475 | 476 | return that; 477 | } 478 | }; 479 | 480 | 481 | (function() { 482 | // your page initialization code here 483 | // the DOM will be available here 484 | Botkit.boot(); 485 | })(); 486 | -------------------------------------------------------------------------------- /public/css/biggerstyles.css: -------------------------------------------------------------------------------- 1 | html,body { 2 | font-family: 'helvetica', sans-serif; 3 | margin:0; 4 | padding:0; 5 | background: #f0f0f0; 6 | } 7 | * { 8 | box-sizing: border-box; 9 | } 10 | #message_window { 11 | background: #f0f0f0; 12 | /*border: 2px solid #CCC;*/ 13 | /*height: 500px; 14 | width: 320px;*/ 15 | height: 50%; 16 | width: 80%; 17 | margin: 0 auto; 18 | display: flex; 19 | flex-direction: column; 20 | } 21 | #message_window .disconnected { 22 | background: #FFFFCC; 23 | padding: 0.25rem; 24 | } 25 | #message_window.connected .disconnected { 26 | display: none; 27 | } 28 | #message_window section { 29 | flex-grow: 1; 30 | flex-direction: column-reverse; 31 | display: flex; 32 | overflow-y: auto; 33 | } 34 | 35 | #message_window section div div { 36 | margin: 0.25rem; 37 | clear: both; 38 | 39 | } 40 | 41 | #message_window footer { 42 | /*border-top: 1px solid #CCC;*/ 43 | } 44 | 45 | #message_window form { 46 | display: flex; 47 | margin: 0; 48 | padding: 0.25rem; 49 | } 50 | #message_window footer input[type="text"] { 51 | flex-grow: 1; 52 | font-size: 1.25rem; 53 | font-size: 2rem; 54 | outline: none; 55 | } 56 | 57 | #message_window footer button { 58 | width: 50px; 59 | background: none; 60 | border: 0; 61 | cursor: pointer; 62 | background-color: blue; 63 | color: #FFFFFF; 64 | font-weight: bold; 65 | } 66 | 67 | #message_template { 68 | display: none; 69 | } 70 | 71 | .message { 72 | padding: 1rem; 73 | background: #FFF; 74 | border-radius: 5px; 75 | width: auto; 76 | font-size: 2rem; 77 | display: inline-block; 78 | max-width: 75%; 79 | } 80 | 81 | .message p { 82 | margin-top: 0; 83 | margin-bottom: 0.5rem; 84 | } 85 | 86 | .message p:last-child { 87 | margin-bottom: 0; 88 | } 89 | 90 | .message.outgoing { 91 | float: right; 92 | } 93 | 94 | #message_replies { 95 | text-align: center; 96 | } 97 | #message_replies a { 98 | text-decoration: none; 99 | display: inline-block; 100 | margin: 0.5rem; 101 | border: 1px solid blue; 102 | border-radius: 16px; 103 | padding: 0.5rem 1rem; 104 | } 105 | -------------------------------------------------------------------------------- /public/css/embed.css: -------------------------------------------------------------------------------- 1 | #embedded_messenger { 2 | position: fixed; 3 | z-index: 1000; 4 | bottom: -400px; 5 | right: 2rem; 6 | height: 434px; 7 | transition: 0.5s ease-in-out bottom; } 8 | #embedded_messenger.active { 9 | bottom: 0; } 10 | #embedded_messenger iframe { 11 | height: 400px; 12 | width: 100%; 13 | border: 0; } 14 | #embedded_messenger #message_header { 15 | background: #000; 16 | padding: 0.5rem 1rem; 17 | color: #FFF; } 18 | 19 | /*# sourceMappingURL=embed.css.map */ 20 | -------------------------------------------------------------------------------- /public/css/embed.css.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "mappings": "AAEA,mBAAoB;EAElB,QAAQ,EAAE,KAAK;EACf,OAAO,EAAE,IAAI;EAEb,MAAM,EAAE,MAAQ;EAChB,KAAK,EAAE,IAAI;EAEX,MAAM,EAAE,KAAY;EACpB,UAAU,EAAE,uBAAuB;EAEnC,0BAAS;IACP,MAAM,EAAE,CAAC;EAGX,0BAAO;IACL,MAAM,EAlBD,KAAK;IAmBV,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,CAAC;EAGX,mCAAgB;IACd,UAAU,EAAE,IAAI;IAChB,OAAO,EAAE,WAAW;IACpB,KAAK,EAAE,IAAI", 4 | "sources": ["../../sass/embed.scss"], 5 | "names": [], 6 | "file": "embed.css" 7 | } -------------------------------------------------------------------------------- /public/css/styles.css: -------------------------------------------------------------------------------- 1 | /* shared botkit ui styles */ 2 | * { 3 | box-sizing: border-box; } 4 | 5 | body, 6 | html { 7 | margin: 0; 8 | padding: 0; 9 | font-size: 18px; 10 | background-color: #FFF; 11 | font-family: 'helvetica', sans-serif; } 12 | 13 | .wrapper { 14 | position: relative; 15 | max-width: 1000px; 16 | margin: 0 auto; } 17 | .wrapper:after { 18 | content: ""; 19 | display: table; 20 | clear: both; } 21 | 22 | .box { 23 | border: 2px solid #CCC; 24 | padding: 1rem calc(1rem - 2px); 25 | margin-bottom: 1rem; } 26 | .box:after { 27 | content: ""; 28 | display: table; 29 | clear: both; } 30 | .box h1, 31 | .box h2, 32 | .box h3 { 33 | margin-top: 0; } 34 | 35 | footer { 36 | text-align: center; } 37 | 38 | .hero { 39 | text-align: center; 40 | padding: 2rem; } 41 | .hero h1 { 42 | font-size: 4rem; 43 | margin: 0; } 44 | 45 | a { 46 | color: #a795ef; } 47 | 48 | .copyurl { 49 | width: 100%; 50 | font-size: 1.25rem; } 51 | 52 | div.input label { 53 | font-weight: bold; 54 | font-size: smaller; } 55 | 56 | .addon { 57 | display: flex; 58 | border: 1px solid #999; 59 | border-radius: 6px; 60 | padding: 5px; 61 | background: #F0F0F0; } 62 | .addon input, 63 | .addon textarea { 64 | flex-grow: 1; 65 | border: 0; 66 | background: transparent; } 67 | .addon button { 68 | flex-grow: 0; 69 | background: transparent; 70 | border: 1px solid #999; 71 | border-radius: 6px; 72 | font-weight: bold; } 73 | .addon button.textarea { 74 | align-self: flex-start; 75 | padding: 0.5rem; } 76 | .addon button:hover { 77 | background: #FFF; 78 | color: #a795ef; } 79 | 80 | div.hr { 81 | border: 1px dashed #ccc; 82 | width: 10%; 83 | margin: 4rem auto; 84 | height: 1px; } 85 | 86 | a.button { 87 | border: 2px solid #a795ef; 88 | font-weight: bold; 89 | margin: 0; 90 | border-radius: 3px; 91 | display: inline-block; 92 | padding: 0.5rem 2rem; 93 | text-align: center; 94 | text-decoration: none; 95 | box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2); 96 | background-color: #FFF; 97 | transition: box-shadow 0.1s linear; } 98 | a.button:hover { 99 | box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1); } 100 | 101 | /* chat client */ 102 | #message_window { 103 | background: #FFFFFF; 104 | border-left: 1px solid #CCC; 105 | border-right: 1px solid #CCC; 106 | /*height: 500px; 107 | width: 320px;*/ 108 | height: 100%; 109 | width: 100%; 110 | /*margin: 2rem auto;*/ 111 | display: flex; 112 | flex-direction: column; 113 | font-family: 'helvetica', sans-serif; } 114 | #message_window .disconnected { 115 | background: #FFFFCC; 116 | padding: 0.25rem; } 117 | #message_window.connected .disconnected { 118 | display: none; } 119 | #message_window .offline { 120 | display: none; } 121 | #message_window.offline .disconnected { 122 | display: none; } 123 | #message_window.offline .offline { 124 | background: #FF0000; 125 | color: #FFF; 126 | padding: 0.25rem; 127 | display: block; } 128 | #message_window .powered_by { 129 | flex-shrink: 0; 130 | text-align: center; 131 | border-bottom: 1px solid #CCC; 132 | font-size: 14px; 133 | padding: 0.25rem; 134 | color: #666; } 135 | #message_window .powered_by a { 136 | text-decoration: none; 137 | color: #666; } 138 | #message_window .powered_by img { 139 | position: relative; 140 | top: 2px; } 141 | #message_window section { 142 | flex-grow: 1; 143 | flex-direction: column-reverse; 144 | display: flex; 145 | overflow-y: auto; } 146 | #message_window section div div { 147 | margin: 0.25rem; 148 | clear: both; } 149 | #message_window footer { 150 | border-top: 1px solid #CCC; 151 | padding: 0.25rem; } 152 | #message_window footer input[type="text"] { 153 | flex-grow: 1; 154 | font-size: 1.25rem; 155 | outline: none; 156 | border: none; } 157 | #message_window footer button { 158 | width: 50px; 159 | background: none; 160 | border: 0; 161 | cursor: pointer; 162 | color: blue; 163 | font-weight: bold; } 164 | #message_window form { 165 | display: flex; 166 | margin: 0; 167 | padding: 0.25rem; } 168 | #message_window #message_template { 169 | display: none; } 170 | #message_window .message { 171 | font-size: 14px; 172 | padding: 0.5rem; 173 | background: #F0F0F0; 174 | color: #333; 175 | border-radius: 5px; 176 | width: auto; 177 | display: inline-block; 178 | max-width: 75%; } 179 | #message_window .file_attachment { 180 | background: #F0F0F0; 181 | color: #333; 182 | border-radius: 5px; 183 | display: inline-block; 184 | max-width: 75%; } 185 | #message_window .file_attachment img { 186 | max-width: 100%; 187 | display: block; } 188 | #message_window .button_message { 189 | margin: 1rem 0; 190 | border-radius: 4px; 191 | border-color: orange; 192 | border-style: solid; 193 | color: orange; 194 | border-width: 1px; 195 | padding: 0.25rem 1rem; 196 | text-decoration: none; 197 | text-align: center; 198 | box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.25); 199 | display: block; } 200 | #message_window .message p { 201 | margin-top: 0; 202 | margin-bottom: 0.5rem; } 203 | #message_window .message p:last-child { 204 | margin-bottom: 0; } 205 | #message_window .message.outgoing { 206 | float: right; 207 | background: #a795ef; 208 | color: white; } 209 | #message_window #message_replies { 210 | text-align: center; 211 | overflow-x: auto; 212 | flex-shrink: 0; 213 | -webkit-overflow-scrolling: touch; 214 | /* Lets it scroll lazy */ } 215 | #message_window #message_replies ul { 216 | list-style-type: none; 217 | margin: 0px auto; 218 | padding: 0; } 219 | #message_window #message_replies ul li { 220 | display: inline-block; 221 | margin: 0.5rem; 222 | margin-left: 0; } 223 | #message_window #message_replies a { 224 | text-decoration: none; 225 | display: block; 226 | border: 1px solid #a795ef; 227 | color: #a795ef; 228 | border-radius: 16px; 229 | padding: 0.25rem 1rem; 230 | font-size: 14px; 231 | cursor: pointer; } 232 | #message_window #message_replies a:hover { 233 | background: #a795ef; 234 | color: #FFF; } 235 | 236 | /* typing indicator CSS based on code by Joseph Fusco -> https://codepen.io/fusco/pen/XbpaYv */ 237 | .typing-indicator { 238 | display: table; 239 | margin: 0 auto; 240 | position: relative; } 241 | .typing-indicator span { 242 | height: 5px; 243 | width: 5px; 244 | float: left; 245 | margin: 0 1px; 246 | background-color: #333; 247 | display: block; 248 | border-radius: 50%; 249 | opacity: 0.4; } 250 | .typing-indicator span:nth-of-type(1) { 251 | animation: 1s blink infinite 0.3333s; } 252 | .typing-indicator span:nth-of-type(2) { 253 | animation: 1s blink infinite 0.6666s; } 254 | .typing-indicator span:nth-of-type(3) { 255 | animation: 1s blink infinite 0.9999s; } 256 | 257 | @keyframes blink { 258 | 50% { 259 | opacity: 1; } } 260 | /* special styles for homepage */ 261 | #home { 262 | position: relative; 263 | max-width: 650px; 264 | margin: auto; } 265 | #home:after { 266 | content: ""; 267 | display: table; 268 | clear: both; } 269 | 270 | #homepage_bot { 271 | width: 400px; 272 | height: 450px; 273 | float: left; 274 | border: 5px solid #333; 275 | box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.5); } 276 | #homepage_bot iframe { 277 | border: 0; 278 | height: 100%; 279 | width: 100%; } 280 | 281 | #welcome { 282 | margin-left: 440px; } 283 | 284 | /*# sourceMappingURL=styles.css.map */ 285 | -------------------------------------------------------------------------------- /public/css/styles.css.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "mappings": "AAAA,6BAA6B;ACS7B,CAAE;EACE,UAAU,EAAE,UAAU;;AAG1B;IACK;EACD,MAAM,EAAE,CAAC;EACT,OAAO,EAAE,CAAC;EACV,SAAS,EAAE,IAAI;EACf,gBAAgB,EAAE,IAAI;EACtB,WAAW,EAAE,uBAAuB;;AAGxC,QAAS;EAEL,QAAQ,EAAE,QAAQ;EAClB,SAAS,EAAE,MAAM;EACjB,MAAM,EAAE,MAAM;EAxBd,cAAQ;IACJ,OAAO,EAAE,EAAE;IACX,OAAO,EAAE,KAAK;IACd,KAAK,EAAE,IAAI;;AAwBnB,IAAK;EAED,MAAM,EAAE,cAAc;EACtB,OAAO,EAAE,qBAAqB;EAC9B,aAAa,EAAE,IAAI;EA/BnB,UAAQ;IACJ,OAAO,EAAE,EAAE;IACX,OAAO,EAAE,KAAK;IACd,KAAK,EAAE,IAAI;EA8Bf;;SAEG;IACC,UAAU,EAAE,CAAC;;AAIrB,MAAO;EACH,UAAU,EAAE,MAAM;;AAGtB,KAAM;EACF,UAAU,EAAE,MAAM;EAClB,OAAO,EAAE,IAAI;EAEb,QAAG;IACC,SAAS,EAAE,IAAI;IACf,MAAM,EAAE,CAAC;;AAKjB,CAAE;EACE,KAAK,EA1DO,OAAO;;AA6DvB,QAAS;EACL,KAAK,EAAE,IAAI;EACX,SAAS,EAAE,OAAO;;AAIlB,eAAM;EACF,WAAW,EAAE,IAAI;EACjB,SAAS,EAAE,OAAO;;AAI1B,MAAO;EACH,OAAO,EAAE,IAAI;EACb,MAAM,EAAE,cAAc;EACtB,aAAa,EAAE,GAAG;EAClB,OAAO,EAAE,GAAG;EACZ,UAAU,EAAE,OAAO;EAEnB;iBACS;IACL,SAAS,EAAE,CAAC;IACZ,MAAM,EAAE,CAAC;IACT,UAAU,EAAE,WAAW;EAG3B,aAAO;IACH,SAAS,EAAE,CAAC;IACZ,UAAU,EAAE,WAAW;IACvB,MAAM,EAAE,cAAc;IACtB,aAAa,EAAE,GAAG;IAClB,WAAW,EAAE,IAAI;IAEjB,sBAAW;MACP,UAAU,EAAE,UAAU;MACtB,OAAO,EAAE,MAAM;IAGnB,mBAAQ;MACJ,UAAU,EAAE,IAAI;MAChB,KAAK,EArGD,OAAO;;AA0GvB,MAAO;EACH,MAAM,EAAE,eAAe;EACvB,KAAK,EAAE,GAAG;EACV,MAAM,EAAE,SAAS;EACjB,MAAM,EAAE,GAAG;;AAGf,QAAS;EACL,MAAM,EAAE,iBAAwB;EAChC,WAAW,EAAE,IAAI;EAEjB,MAAM,EAAE,CAAC;EACT,aAAa,EAAE,GAAG;EAClB,OAAO,EAAE,YAAY;EACrB,OAAO,EAAE,WAAW;EACpB,UAAU,EAAE,MAAM;EAClB,eAAe,EAAE,IAAI;EAErB,UAAU,EAAE,8BAA2B;EACvC,gBAAgB,EAAE,IAAI;EACtB,UAAU,EAAE,sBAAsB;EAElC,cAAQ;IAEJ,UAAU,EAAE,8BAA2B;;AD/H/C,iBAAiB;AEIjB,eAAgB;EACZ,UAAU,EAPD,OAAO;EAQhB,WAAW,EAAE,cAAc;EAC3B,YAAY,EAAE,cAAc;EAE5B;iBACe;EACf,MAAM,EAAE,IAAI;EACZ,KAAK,EAAE,IAAI;EACX,sBAAsB;EACtB,OAAO,EAAE,IAAI;EACb,cAAc,EAAE,MAAM;EACtB,WAAW,EAAE,uBAAuB;EAEpC,6BAAc;IACV,UAAU,EAAE,OAAO;IACnB,OAAO,EAAE,OAAO;EAEpB,uCAA0B;IACtB,OAAO,EAAE,IAAI;EAEjB,wBAAS;IACL,OAAO,EAAE,IAAI;EAIf,qCAAc;IACZ,OAAO,EAAE,IAAI;EAEf,gCAAS;IACP,UAAU,EAAE,OAAO;IACnB,KAAK,EAAE,IAAI;IACX,OAAO,EAAE,OAAO;IAChB,OAAO,EAAE,KAAK;EAIlB,2BAAY;IACV,WAAW,EAAE,CAAC;IACd,UAAU,EAAE,MAAM;IAElB,aAAa,EAAE,cAAc;IAC7B,SAAS,EAAE,IAAI;IACf,OAAO,EAAE,OAAO;IAChB,KAAK,EAAE,IAAI;IAEX,6BAAE;MACA,eAAe,EAAE,IAAI;MACrB,KAAK,EAAE,IAAI;IAGb,+BAAI;MACF,QAAQ,EAAE,QAAQ;MAClB,GAAG,EAAE,GAAG;EAIb,uBAAQ;IACL,SAAS,EAAG,CAAC;IACb,cAAc,EAAE,cAAc;IAC9B,OAAO,EAAE,IAAI;IACb,UAAU,EAAE,IAAI;IAEjB,+BAAQ;MACL,MAAM,EAAE,OAAO;MACf,KAAK,EAAE,IAAI;EAIjB,sBAAO;IACH,UAAU,EAAE,cAAc;IAC1B,OAAO,EAAE,OAAO;IAChB,yCAAmB;MACf,SAAS,EAAE,CAAC;MACZ,SAAS,EAAE,OAAO;MAClB,OAAO,EAAE,IAAI;MACb,MAAM,EAAE,IAAI;IAEhB,6BAAO;MACJ,KAAK,EAAE,IAAI;MACX,UAAU,EAAE,IAAI;MAChB,MAAM,EAAE,CAAC;MACT,MAAM,EAAE,OAAO;MACf,KAAK,EAAE,IAAI;MACX,WAAW,EAAE,IAAI;EAIxB,oBAAK;IACD,OAAO,EAAE,IAAI;IACb,MAAM,EAAE,CAAC;IACT,OAAO,EAAE,OAAO;EAGpB,iCAAkB;IACd,OAAO,EAAE,IAAI;EAGjB,wBAAS;IACL,SAAS,EAAE,IAAI;IACf,OAAO,EAAE,MAAM;IACf,UAAU,EA1GS,OAAO;IA2G1B,KAAK,EA1GQ,IAAI;IA2GjB,aAAa,EAAE,GAAG;IAClB,KAAK,EAAE,IAAI;IACX,OAAO,EAAE,YAAY;IACrB,SAAS,EAAE,GAAG;EAIlB,gCAAiB;IAEf,UAAU,EArHW,OAAO;IAsH5B,KAAK,EArHU,IAAI;IAsHnB,aAAa,EAAE,GAAG;IAClB,OAAO,EAAE,YAAY;IACrB,SAAS,EAAE,GAAG;IAEd,oCAAI;MACF,SAAS,EAAE,IAAI;MACf,OAAO,EAAE,KAAK;EAKhB,+BAAgB;IACd,MAAM,EAAE,MAAM;IACd,aAAa,EAAE,GAAG;IAClB,YAAY,EAvIH,MAAM;IAwIf,YAAY,EAAE,KAAK;IACnB,KAAK,EAzII,MAAM;IA0If,YAAY,EAAE,GAAG;IACjB,OAAO,EAAE,YAAY;IACrB,eAAe,EAAE,IAAI;IACrB,UAAU,EAAE,MAAM;IAClB,UAAU,EAAE,+BAA4B;IACxC,OAAO,EAAE,KAAK;EAIlB,0BAAW;IACP,UAAU,EAAE,CAAC;IACb,aAAa,EAAE,MAAM;EAGzB,qCAAsB;IAClB,aAAa,EAAE,CAAC;EAGpB,iCAAkB;IACd,KAAK,EAAE,KAAK;IAEZ,UAAU,EA3JW,OAAc;IA4JnC,KAAK,EA3JU,KAAK;EA+JxB,gCAAiB;IACb,UAAU,EAAE,MAAM;IAClB,UAAU,EAAE,IAAI;IAChB,WAAW,EAAE,CAAC;IACd,0BAA0B,EAAE,KAAK;IAAE,yBAAyB;IAE5D,mCAAG;MACC,eAAe,EAAE,IAAI;MACrB,MAAM,EAAE,QAAQ;MAChB,OAAO,EAAE,CAAC;MACV,sCAAG;QACC,OAAO,EAAE,YAAY;QACrB,MAAM,EAAE,MAAM;QACd,WAAW,EAAE,CAAC;IAItB,kCAAE;MACC,eAAe,EAAE,IAAI;MACrB,OAAO,EAAE,KAAK;MACd,MAAM,EAAE,iBAAwB;MAChC,KAAK,EArLa,OAAc;MAsLhC,aAAa,EAAE,IAAI;MACnB,OAAO,EAAE,YAAY;MACrB,SAAS,EAAE,IAAI;MACf,MAAM,EAAE,OAAO;MAEf,wCAAQ;QACN,UAAU,EA5LM,OAAc;QA6L9B,KAAK,EAAE,IAAI;;AAQtB,+FAA+F;AAC/F,iBAAkB;EAChB,OAAO,EAAE,KAAK;EACd,MAAM,EAAE,MAAM;EACd,QAAQ,EAAE,QAAQ;EAClB,sBAAK;IACH,MAAM,EAAE,GAAG;IACX,KAAK,EAAE,GAAG;IACV,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,KAAK;IACb,gBAAgB,EAhND,IAAI;IAiNnB,OAAO,EAAE,KAAK;IACd,aAAa,EAAE,GAAG;IAClB,OAAO,EAAE,GAAG;IAEV,qCAAqB;MACnB,SAAS,EAAE,yBAA+B;IAD5C,qCAAqB;MACnB,SAAS,EAAE,yBAA+B;IAD5C,qCAAqB;MACnB,SAAS,EAAE,yBAA+B;;AAMlD,gBAIC;EAHC,GAAI;IACF,OAAO,EAAE,CAAC;AF3Nd,iCAAiC;AGNjC,KAAM;EAEF,QAAQ,EAAE,QAAQ;EAClB,SAAS,EAAE,KAAK;EAChB,MAAM,EAAE,IAAI;EFFZ,WAAQ;IACJ,OAAO,EAAE,EAAE;IACX,OAAO,EAAE,KAAK;IACd,KAAK,EAAE,IAAI;;AEEnB,aAAc;EACV,KAAK,EAAE,KAAK;EACZ,MAAM,EAAE,KAAK;EACb,KAAK,EAAE,IAAI;EACX,MAAM,EAAE,cAAc;EAOtB,UAAU,EAAE,8BAA2B;EALvC,oBAAO;IACH,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,IAAI;IACZ,KAAK,EAAE,IAAI;;AAKnB,QAAS;EACL,WAAW,EAAE,KAAK", 4 | "sources": ["../../sass/styles.scss","../../sass/_botkit.scss","../../sass/_chat.scss","../../sass/_home.scss"], 5 | "names": [], 6 | "file": "styles.css" 7 | } -------------------------------------------------------------------------------- /public/embed.js: -------------------------------------------------------------------------------- 1 | var Botkit = { 2 | 3 | setCookie: function(cname, cvalue, exdays) { 4 | var d = new Date(); 5 | d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000)); 6 | var expires = "expires=" + d.toUTCString(); 7 | document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"; 8 | }, 9 | getCookie: function(cname) { 10 | var name = cname + "="; 11 | var decodedCookie = decodeURIComponent(document.cookie); 12 | var ca = decodedCookie.split(';'); 13 | for (var i = 0; i < ca.length; i++) { 14 | var c = ca[i]; 15 | while (c.charAt(0) == ' ') { 16 | c = c.substring(1); 17 | } 18 | if (c.indexOf(name) == 0) { 19 | return c.substring(name.length, c.length); 20 | } 21 | } 22 | return ""; 23 | }, 24 | active: false, 25 | activate: function() { 26 | this.active = true; 27 | if (this.container) { 28 | this.container.className = 'active'; 29 | } 30 | this.setCookie('botkit_messenger_active', this.active); 31 | }, 32 | deactivate: function() { 33 | this.active = false; 34 | if (this.container) { 35 | this.container.className = ''; 36 | } 37 | this.setCookie('botkit_messenger_active', this.active); 38 | }, 39 | toggle: function() { 40 | if (this.active) { 41 | this.deactivate(); 42 | } else { 43 | this.activate(); 44 | } 45 | }, 46 | trigger: function(event) { 47 | this.chatClient.postMessage(event, '*'); 48 | }, 49 | receiveMessage: function(message) { 50 | // message contains the following fields: 51 | // message.data, message.origin, message.source 52 | 53 | switch (message.data.name) { 54 | case 'booted': 55 | Botkit.trigger({ 56 | name: 'connect', 57 | user: Botkit.current_user ? Botkit.current_user : null, 58 | }); 59 | 60 | if (Botkit.getCookie('botkit_messenger_active') == 'true') { 61 | Botkit.activate(); 62 | } 63 | console.log('Embedded Botkit: Ready!'); 64 | break; 65 | case 'connected': 66 | // console.log('100% CONNECTED AND READY TO GO'); 67 | break; 68 | } 69 | }, 70 | triggerScript: function(script, thread) { 71 | 72 | this.trigger({ 73 | type: 'event', 74 | name: 'trigger', 75 | script: script, 76 | thread: thread, 77 | }); 78 | }, 79 | identifyUser: function(user) { 80 | 81 | // user should contain any of the following: 82 | // id, email, name, first_name, last_name, full_name, gender, timezone, timezone_offset 83 | 84 | this.current_user = user; 85 | 86 | this.trigger({ 87 | type: 'event', 88 | name: 'identify', 89 | user: user, 90 | }); 91 | 92 | 93 | }, 94 | boot: function(user) { 95 | var that = this; 96 | 97 | that.container = document.getElementById('embedded_messenger'); 98 | that.header = document.getElementById('messenger_header'); 99 | that.chatClient = document.getElementById('botkit_client').contentWindow; 100 | 101 | if (user) { 102 | that.current_user = user; 103 | } 104 | 105 | if (!that.chatClient) { 106 | console.error('Cannot find Botkit chat client iframe. Make sure your iframe has the id #botkit_client'); 107 | } 108 | 109 | window.addEventListener('message', that.receiveMessage, false); 110 | 111 | return this; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /public/sent.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/howdyai/botkit-starter-web/cfb7a80add3186ea8dcb51b8764f907302db64cc/public/sent.mp3 -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # This repo is deprecated! 2 | 3 | To get a fresh Botkit starter kit, use the Yeoman generator or [remix a starter kit on Glitch](https://glitch.com/botkit) 4 | 5 | ``` 6 | npm install -g yo generator-botkit 7 | yo botkit 8 | ``` 9 | 10 | 11 | # Botkit Anywhere 12 | 13 | Embed a bot in any web page or app with Botkit for the Web. 14 | 15 | Botkit Anywhere is a self-contained chat server, API and web-based messaging client that has been built on top of the industry leading Botkit development stack. 16 | 17 | ## Get Started 18 | 19 | You can deploy this starter kit project directly to Glitch, or clone it to your own development environment: 20 | 21 | * [Remix on Glitch](https://glitch.com/~botkit-web) 22 | 23 | * Use the Botkit command line utility to install locally: 24 | 25 | ```bash 26 | npm i -g botkit 27 | botkit new -p web 28 | ``` 29 | 30 | ## Add Features with Botkit CMS 31 | 32 | Bots can be thought of as a series of pre-defined conversations, navigated by users who exchange messages with the bot application. The bot is responsible for replying with the appropriate message, and taking whatever automated actions are necessary to satisfy the user. Each "feature" of your bot will consist of one or more conversations, along with some code to power the related actions. 33 | 34 | [Botkit CMS](https://github.com/howdyai/botkit-cms) is an optional add-on for Botkit that enables developers, designers, copywriters and other botmakers to build features for bots without writing any code by providing dialog authoring and content management tools. The visual authoring environment in Botkit CMS can be used to create branching conversations, Q&A systems, complex transactions, or any other type of conversational content. 35 | 36 | Conversational content in Botkit CMS can be updated and expanded at any time, without requiring changes to the bot's code. 37 | 38 | Then, with just a bit of code, your bot can access and use information from databases, 39 | APIs and third party services as part of the conversation. The business logic 40 | of your bot stays clean and easy to maintain by separating the form from the functionality. 41 | 42 | [Botkit CMS](https://github.com/howdyai/botkit-cms) 43 | 44 | 45 | ## The full power of Botkit, in your app or site 46 | 47 | Botkit's SDK powers tens-of-thousands of bots, and supports development of chatbots on 48 | all major messaging platforms. Members of the Botkit developer community have created dozens of useful plugins, 49 | including plugins that add compatibility with top A.I. technologies like IBM Watson, DialogFlow, and RASA. 50 | 51 | New code-driven features can be added to this starter kit by creating "skills" which are 52 | Javascript modules containing a set of specialized pattern matchers, handler functions and middlewares. 53 | 54 | * **[How to build Botkit Skill Modules](docs/how_to_build_skills.md)** 55 | * [Full Botkit Documentation](https://github.com/howdyai/botkit/blob/master/docs/readme.md#developing-with-botkit) 56 | 57 | 58 | ## Customizable web-based chat client 59 | 60 | Botkit Anywhere includes an easy to customize chat client that can be used as a full-screen web app, built into the structure 61 | of an existing page, or embedded in an entire site with an iframe. 62 | 63 | The built-in client uses websocket connections to establish a real time connection 64 | to your Botkit app in order to instantly send and receive messages. It supports bot-friendly 65 | features like quick replies and image attachments. It gracefully handles failed connections 66 | and reconnects. 67 | 68 | The chat client is built with HTML, CSS and vanilla Javascript. 69 | Developers can customize the look and feel of the client by modifying the included markup and CSS. 70 | New chat features such as custom cards or actions can be added with just a little bit of code. 71 | 72 | * **[Web Chat Client Overview](docs/botkit_web_client.md)** 73 | * [How to embed a bot in your website](docs/botkit_web_client.md#embed-botkit-in-a-website-with-iframes) 74 | * [How to customize the look and feel of your web chat](docs/botkit_web_client.md#customize-the-look-and-feel-of-the-chat-interface) 75 | * [How to extend the UI of your web chat with custom fields](docs/botkit_web_client.md#using-botkit-studio-custom-fields-to-add-custom-features) 76 | * [How to share user account/profile info with Botkit](docs/botkit_web_client.md#share-user-accounts--profile-data-with-botkit) 77 | 78 | ## Chat Server and API 79 | 80 | Botkit Anywhere's built-in chat server can handle thousands of simultaneous one-on-one conversations with your users. 81 | The chat server provides both a websocket and a webhook based interface for sending and receiving messages. 82 | It is a great solution for including one-on-one chat in a web site or native app. 83 | 84 | Additionally, Botkit Anywhere includes APIs for retrieving a user's conversation history, 85 | and account-linking features that enable you to identify existing users to your bot. 86 | 87 | * **[Chat Server Overview](docs/botkit_chat_server.md)** 88 | * [Communicating with Websockets](docs/botkit_chat_server.md#using-websockets) 89 | * [Communicating with Webhooks](docs/botkit_chat_server.md#using-webhooks) 90 | * [How to enable message history API](docs/botkit_chat_server.md#enable-message-history) 91 | 92 | # Developer & Support Community 93 | 94 | You can find full documentation for Botkit on [our website](https://botkit.ai/docs). 95 | 96 | ### Need more help? 97 | * Glitch allows users to ask the community for help directly from the editor! For more information on raising your hand, [read this blog post.](https://medium.com/glitch/just-raise-your-hand-how-glitch-helps-aa6564cb1685) 98 | 99 | * Join our thriving community of Botkit developers and bot enthusiasts at large. Over 9000 members strong, [our open Slack group](http://community.botkit.ai) is _the place_ for people interested in the art and science of making bots. 100 | 101 | Come to ask questions, share your progress, and commune with your peers! 102 | 103 | * We also host a [regular meetup and annual conference called TALKABOT.](http://talkabot.ai) Come meet and learn from other bot developers! 104 | 105 | [Full video of our 2016 event is available on Youtube.](https://www.youtube.com/playlist?list=PLD3JNfKLDs7WsEHSal2cfwG0Fex7A6aok) 106 | 107 | # About Botkit 108 | 109 | Botkit is a product of [Howdy](https://howdy.ai) and made in Austin, TX with the help of a worldwide community of botheads. 110 | -------------------------------------------------------------------------------- /sass/_botkit.scss: -------------------------------------------------------------------------------- 1 | $botkit_purple: #a795ef; 2 | @mixin clearfix { 3 | &:after { 4 | content: ""; 5 | display: table; 6 | clear: both; 7 | } 8 | } 9 | 10 | * { 11 | box-sizing: border-box; 12 | } 13 | 14 | body, 15 | html { 16 | margin: 0; 17 | padding: 0; 18 | font-size: 18px; 19 | background-color: #FFF; 20 | font-family: 'helvetica', sans-serif; 21 | } 22 | 23 | .wrapper { 24 | @include clearfix; 25 | position: relative; 26 | max-width: 1000px; 27 | margin: 0 auto; 28 | } 29 | 30 | .box { 31 | @include clearfix; 32 | border: 2px solid #CCC; 33 | padding: 1rem calc(1rem - 2px); 34 | margin-bottom: 1rem; 35 | 36 | h1, 37 | h2, 38 | h3 { 39 | margin-top: 0; 40 | } 41 | } 42 | 43 | footer { 44 | text-align: center; 45 | } 46 | 47 | .hero { 48 | text-align: center; 49 | padding: 2rem; 50 | 51 | h1 { 52 | font-size: 4rem; 53 | margin: 0; 54 | 55 | } 56 | } 57 | 58 | a { 59 | color: $botkit_purple; 60 | } 61 | 62 | .copyurl { 63 | width: 100%; 64 | font-size: 1.25rem; 65 | } 66 | 67 | div.input { 68 | label { 69 | font-weight: bold; 70 | font-size: smaller; 71 | } 72 | } 73 | 74 | .addon { 75 | display: flex; 76 | border: 1px solid #999; 77 | border-radius: 6px; 78 | padding: 5px; 79 | background: #F0F0F0; 80 | 81 | input, 82 | textarea { 83 | flex-grow: 1; 84 | border: 0; 85 | background: transparent; 86 | } 87 | 88 | button { 89 | flex-grow: 0; 90 | background: transparent; 91 | border: 1px solid #999; 92 | border-radius: 6px; 93 | font-weight: bold; 94 | 95 | &.textarea { 96 | align-self: flex-start; 97 | padding: 0.5rem; 98 | } 99 | 100 | &:hover { 101 | background: #FFF; 102 | color: $botkit_purple; 103 | } 104 | } 105 | } 106 | 107 | div.hr { 108 | border: 1px dashed #ccc; 109 | width: 10%; 110 | margin: 4rem auto; 111 | height: 1px; 112 | } 113 | 114 | a.button { 115 | border: 2px solid $botkit_purple; 116 | font-weight: bold; 117 | // font-size: 4rem; 118 | margin: 0; 119 | border-radius: 3px; 120 | display: inline-block; 121 | padding: 0.5rem 2rem; 122 | text-align: center; 123 | text-decoration: none; 124 | // color: #FFF; 125 | box-shadow: 5px 5px 5px rgba(0,0,0,0.2); 126 | background-color: #FFF; 127 | transition: box-shadow 0.1s linear; 128 | 129 | &:hover { 130 | // background-color: rgba($botkit_purple, 0.1); 131 | box-shadow: 1px 1px 5px rgba(0,0,0,0.1); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /sass/_chat.scss: -------------------------------------------------------------------------------- 1 | $action_color: orange; 2 | $background: #FFFFFF; 3 | $bot_message_background: #F0F0F0; 4 | $bot_message_text: #333; 5 | $human_message_background: $botkit_purple; 6 | $human_message_text: white; 7 | 8 | #message_window { 9 | background: $background; 10 | border-left: 1px solid #CCC; 11 | border-right: 1px solid #CCC; 12 | 13 | /*height: 500px; 14 | width: 320px;*/ 15 | height: 100%; 16 | width: 100%; 17 | /*margin: 2rem auto;*/ 18 | display: flex; 19 | flex-direction: column; 20 | font-family: 'helvetica', sans-serif; 21 | 22 | .disconnected { 23 | background: #FFFFCC; 24 | padding: 0.25rem; 25 | } 26 | &.connected .disconnected { 27 | display: none; 28 | } 29 | .offline { 30 | display: none; 31 | } 32 | &.offline { 33 | 34 | .disconnected { 35 | display: none; 36 | } 37 | .offline { 38 | background: #FF0000; 39 | color: #FFF; 40 | padding: 0.25rem; 41 | display: block; 42 | } 43 | } 44 | 45 | .powered_by { 46 | flex-shrink: 0; 47 | text-align: center; 48 | 49 | border-bottom: 1px solid #CCC; 50 | font-size: 14px; 51 | padding: 0.25rem; 52 | color: #666; 53 | 54 | a { 55 | text-decoration: none; 56 | color: #666; 57 | } 58 | 59 | img { 60 | position: relative; 61 | top: 2px; 62 | } 63 | } 64 | 65 | section { 66 | flex-grow: 1; 67 | flex-direction: column-reverse; 68 | display: flex; 69 | overflow-y: auto; 70 | 71 | div div { 72 | margin: 0.25rem; 73 | clear: both; 74 | } 75 | } 76 | 77 | footer { 78 | border-top: 1px solid #CCC; 79 | padding: 0.25rem; 80 | input[type="text"] { 81 | flex-grow: 1; 82 | font-size: 1.25rem; 83 | outline: none; 84 | border: none; 85 | } 86 | button { 87 | width: 50px; 88 | background: none; 89 | border: 0; 90 | cursor: pointer; 91 | color: blue; 92 | font-weight: bold; 93 | } 94 | } 95 | 96 | form { 97 | display: flex; 98 | margin: 0; 99 | padding: 0.25rem; 100 | } 101 | 102 | #message_template { 103 | display: none; 104 | } 105 | 106 | .message { 107 | font-size: 14px; 108 | padding: 0.5rem; 109 | background: $bot_message_background; 110 | color: $bot_message_text; 111 | border-radius: 5px; 112 | width: auto; 113 | display: inline-block; 114 | max-width: 75%; 115 | 116 | } 117 | 118 | .file_attachment { 119 | 120 | background: $bot_message_background; 121 | color: $bot_message_text; 122 | border-radius: 5px; 123 | display: inline-block; 124 | max-width: 75%; 125 | 126 | img { 127 | max-width: 100%; 128 | display: block; 129 | } 130 | 131 | } 132 | 133 | .button_message { 134 | margin: 1rem 0; 135 | border-radius: 4px; 136 | border-color: $action_color; 137 | border-style: solid; 138 | color: $action_color; 139 | border-width: 1px; 140 | padding: 0.25rem 1rem; 141 | text-decoration: none; 142 | text-align: center; 143 | box-shadow: 1px 1px 2px rgba(0,0,0,0.25); 144 | display: block; 145 | } 146 | 147 | 148 | .message p { 149 | margin-top: 0; 150 | margin-bottom: 0.5rem; 151 | } 152 | 153 | .message p:last-child { 154 | margin-bottom: 0; 155 | } 156 | 157 | .message.outgoing { 158 | float: right; 159 | 160 | background: $human_message_background; 161 | color: $human_message_text; 162 | 163 | } 164 | 165 | #message_replies { 166 | text-align: center; 167 | overflow-x: auto; 168 | flex-shrink: 0; 169 | -webkit-overflow-scrolling: touch; /* Lets it scroll lazy */ 170 | 171 | ul { 172 | list-style-type: none; 173 | margin: 0px auto; 174 | padding: 0; 175 | li { 176 | display: inline-block; 177 | margin: 0.5rem; 178 | margin-left: 0; 179 | } 180 | } 181 | 182 | a { 183 | text-decoration: none; 184 | display: block; 185 | border: 1px solid $botkit_purple; 186 | color: $botkit_purple; 187 | border-radius: 16px; 188 | padding: 0.25rem 1rem; 189 | font-size: 14px; 190 | cursor: pointer; 191 | 192 | &:hover { 193 | background: $botkit_purple; 194 | color: #FFF; 195 | } 196 | } 197 | } 198 | } 199 | 200 | 201 | 202 | /* typing indicator CSS based on code by Joseph Fusco -> https://codepen.io/fusco/pen/XbpaYv */ 203 | .typing-indicator { 204 | display: table; 205 | margin: 0 auto; 206 | position: relative; 207 | span { 208 | height: 5px; 209 | width: 5px; 210 | float: left; 211 | margin: 0 1px; 212 | background-color: $bot_message_text; 213 | display: block; 214 | border-radius: 50%; 215 | opacity: 0.4; 216 | @for $i from 1 through 3 { 217 | &:nth-of-type(#{$i}) { 218 | animation: 1s blink infinite ($i * .3333s); 219 | } 220 | } 221 | } 222 | } 223 | 224 | @keyframes blink { 225 | 50% { 226 | opacity: 1; 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /sass/_home.scss: -------------------------------------------------------------------------------- 1 | #home { 2 | @include clearfix; 3 | position: relative; 4 | max-width: 650px; 5 | margin: auto; 6 | } 7 | 8 | #homepage_bot { 9 | width: 400px; 10 | height: 450px; 11 | float: left; 12 | border: 5px solid #333; 13 | 14 | iframe { 15 | border: 0; 16 | height: 100%; 17 | width: 100%; 18 | } 19 | box-shadow: 5px 5px 5px rgba(0,0,0,0.5); 20 | } 21 | 22 | #welcome { 23 | margin-left: 440px; 24 | } 25 | -------------------------------------------------------------------------------- /sass/embed.scss: -------------------------------------------------------------------------------- 1 | $height: 400px; 2 | 3 | #embedded_messenger { 4 | 5 | position: fixed; 6 | z-index: 1000; 7 | 8 | bottom: -$height; 9 | right: 2rem; 10 | 11 | height: $height + 34; 12 | transition: 0.5s ease-in-out bottom; 13 | 14 | &.active { 15 | bottom: 0; 16 | } 17 | 18 | iframe { 19 | height: $height; 20 | width: 100%; 21 | border: 0; 22 | } 23 | 24 | #message_header { 25 | background: #000; 26 | padding: 0.5rem 1rem; 27 | color: #FFF; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /sass/styles.scss: -------------------------------------------------------------------------------- 1 | /* shared botkit ui styles */ 2 | @import "_botkit"; 3 | 4 | /* chat client */ 5 | @import "_chat"; 6 | 7 | /* special styles for homepage */ 8 | @import "_home"; 9 | -------------------------------------------------------------------------------- /skills/_connection_events.js: -------------------------------------------------------------------------------- 1 | /* This module kicks in if no Botkit Studio token has been provided */ 2 | 3 | module.exports = function(controller) { 4 | 5 | controller.on('hello', conductOnboarding); 6 | controller.on('welcome_back', conductOnboarding); 7 | 8 | function conductOnboarding(bot, message) { 9 | 10 | bot.startConversation(message, function(err, convo) { 11 | 12 | convo.say({ 13 | text: 'Hello human! I am brand new Botkit bot, ready to be customized to your needs!', 14 | quick_replies: [ 15 | { 16 | title: 'Help', 17 | payload: 'help', 18 | }, 19 | ] 20 | }); 21 | 22 | 23 | }); 24 | 25 | } 26 | 27 | controller.hears(['help','contact','documentation','docs','community'], 'message_received', function(bot, message) { 28 | 29 | bot.startConversation(message, function(err, convo) { 30 | 31 | // set up a menu thread which other threads can point at. 32 | convo.ask({ 33 | text: 'I can point you to resources, and connect you with experts who can help.', 34 | quick_replies: [ 35 | { 36 | title: 'Read the Docs', 37 | payload: 'documentation', 38 | }, 39 | { 40 | title: 'Join the Community', 41 | payload: 'community', 42 | }, 43 | { 44 | title: 'Expert Help', 45 | payload: 'contact us', 46 | }, 47 | ] 48 | },[ 49 | { 50 | pattern: 'documentation', 51 | callback: function(res, convo) { 52 | convo.gotoThread('docs'); 53 | convo.next(); 54 | } 55 | }, 56 | { 57 | pattern: 'community', 58 | callback: function(res, convo) { 59 | convo.gotoThread('community'); 60 | convo.next(); 61 | } 62 | }, 63 | { 64 | pattern: 'contact', 65 | callback: function(res, convo) { 66 | convo.gotoThread('contact'); 67 | convo.next(); 68 | } 69 | }, 70 | { 71 | default: true, 72 | callback: function(res, convo) { 73 | convo.gotoThread('end'); 74 | } 75 | } 76 | ]); 77 | 78 | // set up docs threads 79 | convo.addMessage({ 80 | text: 'I do not know how to help with that. Say `help` at any time to access this menu.' 81 | },'end'); 82 | 83 | // set up docs threads 84 | convo.addMessage({ 85 | text: 'Botkit is extensively documented! Here are some useful links:\n\n[Botkit Studio Help Desk](https://botkit.groovehq.com/help_center)\n\n[Botkit Anywhere README](https://github.com/howdyai/botkit-starter-web/blob/master/readme.md#botkit-anywhere)\n\n[Botkit Developer Guide](https://github.com/howdyai/botkit/blob/master/readme.md#build-your-bot)', 86 | },'docs'); 87 | 88 | convo.addMessage({ 89 | action: 'default' 90 | }, 'docs'); 91 | 92 | 93 | // set up community thread 94 | convo.addMessage({ 95 | text: 'Our developer community has thousands of members, and there are always friendly people available to answer questions about building bots!', 96 | },'community'); 97 | 98 | convo.addMessage({ 99 | text: '[Join our community Slack channel](https://community.botkit.ai) to chat live with the Botkit team, representatives from major messaging platforms, and other developers just like you!', 100 | },'community'); 101 | 102 | convo.addMessage({ 103 | text: '[Checkout the Github Issue Queue](https://github.com/howdyai/botkit/issues) to find frequently asked questions, bug reports and more.', 104 | },'community'); 105 | 106 | convo.addMessage({ 107 | action: 'default' 108 | }, 'community'); 109 | 110 | 111 | 112 | // set up contact thread 113 | convo.addMessage({ 114 | text: 'The team who built me can help you build the perfect robotic assistant! They can answer all of your questions, and work with you to develop custom applications and integrations.\n\n[Use this form to get in touch](https://botkit.ai/contact.html), or email us directly at [help@botkit.ai](mailto:help@botkit.ai), and a real human will get in touch!', 115 | },'contact'); 116 | convo.addMessage({ 117 | action: 'default' 118 | }, 'contact'); 119 | 120 | }); 121 | 122 | }); 123 | 124 | 125 | } 126 | -------------------------------------------------------------------------------- /skills/demo_quick_replies.js: -------------------------------------------------------------------------------- 1 | module.exports = function(controller) { 2 | 3 | controller.hears('quick replies','message_received', function(bot, message) { 4 | 5 | bot.reply(message, { 6 | text: 'Look, quick replies!', 7 | quick_replies: [ 8 | { 9 | title: 'Hello', 10 | payload: 'hello' 11 | }, 12 | { 13 | title: 'Test', 14 | payload: 'test' 15 | }, 16 | ] 17 | },function() {}); 18 | 19 | 20 | }); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /skills/message_history.js: -------------------------------------------------------------------------------- 1 | module.exports = function(controller) { 2 | 3 | /* 4 | * A NOTE ABOUT SECURITY AND PRIVACY 5 | * PLEASE READ! 6 | * 7 | * This plugin creates a sample endpoint which demonstrates a method 8 | * for loading message history to display in the chat widget so that 9 | * the UI can restore itself between sessions. 10 | * Note that this is DISABLED by default because there is no authentication 11 | * mechanism in place, and revealing message history could potentially reveal 12 | * PII and other sensitive material. 13 | * 14 | * Do not use this plugin if your bot will deal with any sort of sensitive 15 | * information. You *must* validate the user's identity before displaying 16 | * sensitive message history. 17 | */ 18 | 19 | // if (controller.storage && controller.storage.history) { 20 | 21 | // // expose history as an endpoint 22 | // controller.webserver.post('/botkit/history', function(req, res) { 23 | // if (req.body.user) { 24 | // // IMPORTANT: The identify of this user should be validated before returning message history! 25 | // controller.storage.history.getHistoryForUser(req.body.user, 10).then(function(history) { 26 | // res.json({success: true, history: history.map(function(h) { return h.message; })}); 27 | // }).catch(function(err) { 28 | // res.json({success: false, history: [], error: err}); 29 | // }) 30 | // } else { 31 | // res.json({success: false, history: [], error: 'no user specified'}); 32 | // } 33 | // }); 34 | 35 | // function logMessage(message, user) { 36 | 37 | // if (message.type == 'message' || message.type == 'message_received') { 38 | // controller.storage.history.addToHistory(message, message.user).catch(function(err) { 39 | // console.error('Error storing history: ',err); 40 | // }) 41 | // } 42 | // } 43 | 44 | // // log incoming messages to the user history 45 | // controller.middleware.receive.use(function(bot, message, next) { 46 | // controller.storage.users.get(message.user, function(err, user) { 47 | // logMessage(message,user); 48 | // }); 49 | // next(); 50 | // }); 51 | 52 | 53 | // controller.middleware.format.use(function(bot, message, platform_message, next) { 54 | // controller.storage.users.get(message.to, function(err, user) { 55 | // logMessage(platform_message,user); 56 | // }); 57 | // next(); 58 | // }); 59 | 60 | // } else { 61 | // console.log("Configure a MONGO_URI to enable message history"); 62 | // controller.webserver.post('/botkit/history', function(req, res) { 63 | // res.json({success:true, history: []}); 64 | // }); 65 | // } 66 | 67 | controller.webserver.post('/botkit/history', function(req, res) { 68 | res.json({success:true, history: []}); 69 | }); 70 | 71 | 72 | } 73 | -------------------------------------------------------------------------------- /skills/sample_hears.js: -------------------------------------------------------------------------------- 1 | module.exports = function(controller) { 2 | 3 | 4 | controller.hears('test','message_received', function(bot, message) { 5 | 6 | bot.reply(message,'I heard a test'); 7 | 8 | }); 9 | 10 | controller.hears('typing','message_received', function(bot, message) { 11 | 12 | bot.reply(message,{ 13 | text: 'This message used the automatic typing delay', 14 | typing: true, 15 | }, function() { 16 | 17 | bot.reply(message,{ 18 | text: 'This message specified a 5000ms typing delay', 19 | typingDelay: 5000, 20 | }); 21 | 22 | }); 23 | 24 | }); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /skills/super_quit.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | THIS MODULE IS DISABLED BY DEFAULT! 4 | 5 | UNCOMMENT THE CODE BELOW TO ACTIVATE THIS FEATURE 6 | 7 | This module causes your bot to listen for variations on the word "Quit" 8 | and causes it to immediately quit any ongoing conversation. 9 | */ 10 | 11 | module.exports = function(controller) { 12 | 13 | // controller.middleware.receive.use(function(bot, message, next) { 14 | // 15 | // if (message.text && message.text.match(bot.utterances.quit)) { 16 | // bot.findConversation(message, function(convo) { 17 | // if (convo) { 18 | // // stop the conversation and swallow this message 19 | // convo.stop('quit'); 20 | // bot.reply(message,'Quitting.'); 21 | // } else { 22 | // // nothing ongoing, this message passes through 23 | // next(); 24 | // } 25 | // }); 26 | // } else { 27 | // next(); 28 | // } 29 | // 30 | // }); 31 | 32 | } 33 | -------------------------------------------------------------------------------- /skills/unhandled_messages.js: -------------------------------------------------------------------------------- 1 | module.exports = function(controller) { 2 | 3 | controller.on('message_received', function(bot, message) { 4 | 5 | bot.reply(message, { 6 | text: 'I do not know how to respond to that message yet. Define new features by adding skills in my `skills/` folder. [Read more about building skills](https://github.com/howdyai/botkit-starter-web/blob/master/docs/how_to_build_skills.md).\n\n(This message is from the unhandled_messages skill.)', 7 | quick_replies: [ 8 | { 9 | title: 'Help', 10 | payload: 'help', 11 | }, 12 | ] 13 | }); 14 | 15 | }); 16 | 17 | } -------------------------------------------------------------------------------- /views/embed.hbs: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Embed Your Bot

4 |

5 | Copy the code below to embed this bot in a different website. 6 | 7 | Read the guide to using embedded Botkit. 8 |

9 | 10 |
11 | 23 | 24 |
25 | 26 |
27 | 28 | 29 | 30 |
31 |
Chat
32 | 33 |
34 | 35 | 36 | 39 | -------------------------------------------------------------------------------- /views/index.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 | 6 | 13 |
14 | 15 | 18 | -------------------------------------------------------------------------------- /views/layouts/default.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{> head}} 4 | 5 | {{>header}} 6 |
7 | {{{body}}} 8 |
9 | {{> footer}} 10 | 11 | 12 | -------------------------------------------------------------------------------- /views/partials/footer.hbs: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /views/partials/head.hbs: -------------------------------------------------------------------------------- 1 | 2 | Botkit Starter Kit 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /views/partials/header.hbs: -------------------------------------------------------------------------------- 1 | 6 | --------------------------------------------------------------------------------