diff --git a/.kokoro/language-slackbot.cfg b/.kokoro/language-slackbot.cfg deleted file mode 100644 index 257e80a1b8..0000000000 --- a/.kokoro/language-slackbot.cfg +++ /dev/null @@ -1,13 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Set the folder in which the tests are run -env_vars: { - key: "PROJECT" - value: "language/slackbot" -} - -# Tell the trampoline which build file to use. -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/nodejs-docs-samples/.kokoro/build.sh" -} diff --git a/language/README.md b/language/README.md deleted file mode 100644 index c904ef79dc..0000000000 --- a/language/README.md +++ /dev/null @@ -1,9 +0,0 @@ -Most samples for the [Google Cloud Natural Language API Node.js Client][client] -have moved to [github.com/googleapis/nodejs-language/tree/master/samples/][samples]. - -[client]: https://github.com/googleapis/nodejs-language -[samples]: https://github.com/googleapis/nodejs-language/tree/master/samples - -### Slack Bot sample - -View the [README](slackbot/README.md). diff --git a/language/slackbot/.gitignore b/language/slackbot/.gitignore deleted file mode 100644 index 3997beadf8..0000000000 --- a/language/slackbot/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.db \ No newline at end of file diff --git a/language/slackbot/Dockerfile b/language/slackbot/Dockerfile deleted file mode 100644 index 9b602efd14..0000000000 --- a/language/slackbot/Dockerfile +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2016 Google Inc. All Rights Reserved. -# -# 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 -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# 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. - -FROM node:5.4 - -RUN apt-get update -RUN apt-get install -y sqlite3 - -# Install app dependencies. -COPY package.json /src/package.json -WORKDIR /src -RUN npm install - -# Bundle app source. -COPY demo_bot.js /src - -CMD ["node", "/src/demo_bot.js"] diff --git a/language/slackbot/README.md b/language/slackbot/README.md deleted file mode 100644 index 0e23c0d726..0000000000 --- a/language/slackbot/README.md +++ /dev/null @@ -1,281 +0,0 @@ - -# Building a Botkit-based Slack Bot that uses the GCP NL API and runs on Google Container Engine - - - [Setting up your environment](#setting-up-your-environment) - - [Container Engine prerequisites](#container-engine-prerequisites) - - [NL API prerequisites](#nl-api-prerequisites) - - [Create a cluster](#create-a-cluster) - - [Install Docker](#install-docker) - - [Get a Slack token and invite the bot to a Slack channel](#get-a-slack-token-and-invite-the-bot-to-a-slack-channel) - - [Running the slackbot on Kubernetes](#running-the-slackbot-on-kubernetes) - - [Upload the slackbot token to Kubernetes](#upload-the-slackbot-token-to-kubernetes) - - [Build the bot's container](#build-the-bots-container) - - [Running the container](#running-the-container) - - [Running the bot locally](#running-the-bot-locally) - - [Using the Bot](#using-the-bot) - - [Sentiment Analysis](#sentiment-analysis) - - [Entity Analysis](#entity-analysis) - - [Optional: Create a slackbot app that uses persistent storage](#optional-create-a-slackbot-app-that-uses-persistent-storage) - - [Shutting down](#shutting-down) - - [Cleanup](#cleanup) - - -This example shows a Slack bot built using the [Botkit](https://github.com/howdyai/botkit) library. -It runs on a Google Container Engine (Kubernetes) cluster, and uses one of the Google Cloud Platform's ML -APIs, the Natural Language (NL) API, to interact in a Slack channel. - -It uses the NL API in two different ways. -First, it uses the [Google Cloud NL API](https://cloud.google.com/natural-language/) to assess -the [sentiment](https://cloud.google.com/natural-language/docs/basics) of any message posted to -the channel, and if the positive or negative magnitude of the statement is -sufficiently large, it sends a 'thumbs up' or 'thumbs down' in reaction. - -Additionally, it uses the NL API to identify [entities](https://cloud.google.com/natural-language/docs/basics) in each -posted message, and tracks them in a database (using sqlite3). Then, at any time you can query the NL slackbot to ask -it for the top N entities used in the channel. - -The example uses [Google Container -Engine](https://cloud.google.com/container-engine/), a hosted version of -[Kubernetes](http://kubernetes.io), to run the bot, and uses [Google Container -Registry](https://cloud.google.com/container-registry/) to store a Docker image -for the bot. - - -## Setting up your environment - -### Container Engine prerequisites - -First, set up the Google Container Engine -[prerequisites](https://cloud.google.com/container-engine/docs/before-you-begin), including [installation of the Google Cloud SDK](https://cloud.google.com/sdk/downloads). - -### NL API prerequisites - -Next, enable the NL API for your project and authenticate to your service account as described [here](https://cloud.google.com/natural-language/docs/getting-started). (The service account step is not necessary when running the bot on Container Engine, but it is useful if you're testing locally). - -### Create a cluster - -Next, -[create a Kubernetes cluster](https://cloud.google.com/container-engine/docs/clusters/operations#creating_a_container_cluster) using Container Engine as follows: - -```bash -gcloud container clusters create "slackbot-cluster" --scopes "https://www.googleapis.com/auth/cloud-platform" -``` - -You can name the cluster something other than "slackbot-cluster" if you like. - -### Install Docker - -If you do not already have [Docker](https://www.docker.com/) installed locally, follow the [installation instructions](https://docs.docker.com/engine/installation/) on the Docker site. - -## Get a Slack token and invite the bot to a Slack channel - -Then, create a [Slack bot user](https://api.slack.com/bot-users) and get an -authentication token. - -Then, 'invite' your new bot to a channel on a Slack team. - -## Running the slackbot on Kubernetes - -### Upload the slackbot token to Kubernetes - -We will be loading this token in our bot using -[secrets](http://kubernetes.io/v1.1/docs/user-guide/secrets.html). - -Run the following script to create a secret .yaml file (replacing `MY-SLACK-TOKEN` with your token), then use that yaml file to create a secret on your Kubernetes cluster. - -```bash -./generate-secret.sh MY-SLACK-TOKEN -kubectl create -f slack-token-secret.yaml -``` - -### Build the bot's container - -We'll run the slackbot app in our Kubernetes cluster as a [Replication Controller](http://kubernetes.io/docs/user-guide/replication-controller/) with one replica. - -So, first, we need to build its Docker container. Replace `my-cloud-project-id` below with your -Google Cloud Project ID. This tags the container so that gcloud can upload it to -your private Google Container Registry. - -```bash -export PROJECT_ID=my-cloud-project-id -docker build -t gcr.io/${PROJECT_ID}/slack-bot . -``` - -Once the build completes, upload it to the Google Container registry: - -```bash -gcloud docker -- push gcr.io/${PROJECT_ID}/slack-bot -``` - - -### Running the container - -First, create a Replication Controller configuration, populated with your Google -Cloud Project ID, so that Kubernetes knows where to find the Docker image. - -```bash -./generate-rc.sh $PROJECT_ID -``` - -Now, tell Kubernetes to create the bot's replication controller. This will launch 1 pod replica running the bot. - -```bash -kubectl create -f slack-bot-rc.yaml -``` - -You can check the status of your bot with: - -```bash -kubectl get pods -``` - -Now your bot should be online. As a sanity check, check that it responds to a "Hello" message directed to it. - -Note: if you have forgotten to create the secret first, the pod won't come up successfully. - -## Running the bot locally - -If you want, you can run your slackbot locally instead. This is handy if -you've made some changes and want to test them out before redeploying. To do -this, first run: - -```bash -npm install -``` - -Then, set GCLOUD_PROJECT to your project id: - -```bash -export GCLOUD_PROJECT=my-cloud-project-id -``` - -Then, create a file containing your Slack token, and point `SLACK_TOKEN_PATH` to that file when you run the script -(substitute `my-slack-token` with your actual token): - - echo my-slack-token > slack-token - SLACK_TOKEN_PATH=./slack-token node demo_bot.js - -## Using the Bot - -Once you've confirmed the bot is running, you can start putting it through its paces. - -### Sentiment Analysis - -The slackbot will give a 'thumbs up' or 'thumbs down' if it thinks a message is above a certain magnitude in positive or negative sentiment. - -E.g., try posting this message to the channel (you don't need to explicitly mention the bot in this message): - -``` -I hate bananas. -``` - -You should see that bot give a thumbs down in reply, indicatig that the NL API -reported negative sentiment for this sentence. Next, try: - -``` -I love coffee. -``` - -This should generate a thumbs up. Posted text won't get a reply from the bot -unless the magnitude of the sentiment is above a given threshold, 30 by -default. E.g., with a neutral statement like `The temperature is seventy -degrees.` the bot is unlikely to give a response. - -### Entity Analysis - -For every message posted to the channel, the bot-- behind the scenes-- is -analyzing and storing information about the entities it detects. At any time -you can query the bot to get the current N most frequent entities, where N is -20 by default. It will be more interesting if you wait until a few messages -have been posted to the channel, so that the bot has the chance to identify -and log some entities. - -E.g., suppose your bot is called `nlpbot`. -To see the top entities, send it this message: - -``` -@nlpbot top entities -``` - - -## Optional: Create a slackbot app that uses persistent storage - -Kubernetes will keep your slackbot running — we have specified that we want one pod replica, and so if this pod goes down for some reason, Kubernetes will restart it. -You might be pondering what happens to your sqlite3 database if this happens. -With the configuration above, you will lose your data if the pod needs to be restarted. - -One way to address that would be to use a more persistent database service instead of sqlite3, and configure your bot to connect to that instead. ([Cloud SQL](https://cloud.google.com/sql/) would be an option for such a service.) - -Alternatively (for this simple scenario), we can just create a persistent disk on which to store our sqlite3 database, and configure our pod replica to access it. That way, if the slackbot pod needs to be restarted, the database file won't be lost. We'll do that for this example. - -We'll accomplish this by defining a [Persistent Volume](http://kubernetes.io/docs/user-guide/persistent-volumes/) resource, and then creating a [Persistent Volume Claim](http://kubernetes.io/docs/user-guide/persistent-volumes/#persistentvolumeclaims) on that resource which will be used by the slackbot app. - -First, create a persistent disk to use with this app, as follows. Name it `slackbotstore`. You can adjust its size as you like. - -```bash -gcloud compute disks create --size=20GB --zone= slackbotstore -``` - -Then, edit `demo_bot.js` to use `/var/sqlite3/slackDB.db` as its sqlite3 db file: - -```javascript -// create our database if it does not already exist. -// const db = new sqlite3.cached.Database(path.join(__dirname, './slackDB.db')); -const db = new sqlite3.cached.Database('/var/sqlite3/slackDB.db'); -``` - -Once you've done that, rebuild your docker image to capture that code change: - -```bash -export PROJECT_ID=my-cloud-project-id -docker build -t gcr.io/${PROJECT_ID}/slack-bot . -``` - -Generate a different .yaml file to use for this configuration: - -```bash -./generate-dep.sh $PROJECT_ID -``` - -If you take a look at the result, in `slack-bot-dep.yaml`, you will see that it contains the specification of the persistent volume as well as a persistent volume claim on that volume. Then, you'll see that the slackbot mounts a volume matching that claim at `/var/sqlite3`. -(In this config, the slackbot is also specified as a [Deployment](http://kubernetes.io/docs/user-guide/deployments/) rather than a Replication Controller. For the purposes of this example, the difference is not important.) - -Note: when you created your disk, if you named it something other than `slackbotstore`, you will need to edit this configuration to specify your disk name instead. - -If you already have a slackbot running, you will probably want to shut it down before you start up this new version, so that there are not two separate bots monitoring your channel. See the "Shutting down" section below. Then, start up the persistent-storage version of the bot like this: - -```bash -kubectl create -f slack-bot-dep.yaml -``` - - -## Shutting down - -To shut down your bot, we tell Kubernetes to delete the Replication Controller: - -```bash -kubectl delete -f slack-bot-rc.yaml -``` - -Or, if you are running the variant that uses a persistent disk, shut it down with: - -```bash -kubectl delete -f slack-bot-dep.yaml -``` - -This will delete the persistent disk resource and claim as well as the Deployment, but does *not* delete the disk itself, which remains part of your GCP project. If you restart later using the same config file, your existing sqlite3 db will be preserved. - -## Cleanup - -If you have created a container cluster, you may still get charged for the -Google Compute Engine resources it is using, even if they are idle. To delete -the cluster, run: - -```bash -gcloud container clusters delete slackbot-cluster -``` - -(If you used a different name for your cluster, substitute that name for `slackbot-cluster`.) -This deletes the Google Compute Engine instances that are running the cluster. - -If you created a persistent disk for your db, you may want to [delete that as well](https://cloud.google.com/sdk/gcloud/reference/compute/disks/delete). diff --git a/language/slackbot/demo_bot.js b/language/slackbot/demo_bot.js deleted file mode 100755 index 6f8c5242e1..0000000000 --- a/language/slackbot/demo_bot.js +++ /dev/null @@ -1,267 +0,0 @@ -/* ***************************************************************************** -Copyright 2016 Google Inc. All Rights Reserved. - -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 - - http://www.apache.org/licenses/LICENSE-2.0 - -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. -******************************************************************************** - -This is a Slack bot built using the Botkit library (http://howdy.ai/botkit). It -runs on a Kubernetes cluster, and uses one of the Google Cloud Platform's ML -APIs, the Natural Language API, to interact in a Slack channel. It does this in -two respects. - -First, it uses the NL API to assess the "sentiment" of any message posted to -the channel, and if the positive or negative magnitude of the statement is -sufficiently large, it sends a 'thumbs up' or 'thumbs down' in reaction. - -Second, it uses the NL API to identify the 'entities' in each posted message, -and tracks them in a database (using sqlite3). Then, at any time you can -query the NL slackbot to ask it for the top N entities used in the channel. - -The README walks through how to run the NL slackbot as an app on a -Google Container Engine/Kubernetes cluster, but you can also just run the bot -locally if you like. -To do this, create a file containing your Slack token (as described in -the README), then point 'SLACK_TOKEN_PATH' to that file when you run the script: - - echo my-slack-token > slack-token - SLACK_TOKEN_PATH=./slack-token node demo_bot.js - -See the README.md in this directory for more information about setup and usage. - -*/ - -'use strict'; - -const Botkit = require('botkit'); -const fs = require('fs'); -const Language = require('@google-cloud/language'); -const path = require('path'); -const sqlite3 = require('sqlite3').verbose(); - -const controller = Botkit.slackbot({ debug: false }); - -// create our database if it does not already exist. -const db = new sqlite3.cached.Database(path.join(__dirname, './slackDB.db')); -// comment out the line above, and instead uncomment the following, to store -// the db on a persistent disk mounted at /var/sqlite3. See the README -// section on 'using a persistent disk' for this config. -// const db = new sqlite3.cached.Database('/var/sqlite3/slackDB.db'); - -// the number of most frequent entities to retrieve from the db on request. -const NUM_ENTITIES = 20; -// The threshold of sentiment score of a posted text, above which the bot will -// respond. This threshold is rather arbitrary; you may want to play with this -// value. -const SENTIMENT_THRESHOLD = 0.3; -const SEVEN_DAYS_AGO = 60 * 60 * 24 * 7; - -const ENTITIES_BASE_SQL = `SELECT name, type, count(name) as wc -FROM entities`; - -const ENTITIES_SQL = ` GROUP BY name ORDER BY wc DESC -LIMIT ${NUM_ENTITIES};`; - -const TABLE_SQL = `CREATE TABLE if not exists entities ( - name text, - type text, - salience real, - wiki_url text, - ts integer -);`; - -function startController () { - if (!process.env.SLACK_TOKEN_PATH) { - throw new Error('Please set the SLACK_TOKEN_PATH environment variable!'); - } - - let token = fs.readFileSync(process.env.SLACK_TOKEN_PATH, { encoding: 'utf8' }); - token = token.replace(/\s/g, ''); - - // Create the table that will store entity information if it does not already - // exist. - db.run(TABLE_SQL); - - controller - .spawn({ token: token }) - .startRTM((err) => { - if (err) { - console.error('Failed to start controller!'); - console.error(err); - process.exit(1); - } - }); - - return controller - // If the bot gets a DM or mention with 'hello' or 'hi', it will reply. You - // can use this to sanity-check your app without needing to use the NL API. - .hears( - ['hello', 'hi'], - ['direct_message', 'direct_mention', 'mention'], - handleSimpleReply - ) - // If the bot gets a DM or mention including "top entities", it will reply with - // a list of the top N most frequent entities used in this channel, as derived - // by the NL API. - .hears( - ['top entities'], - ['direct_message', 'direct_mention', 'mention'], - handleEntitiesReply - ) - // For any posted message, the bot will send the text to the NL API for - // analysis. - .on('ambient', handleAmbientMessage) - .on('rtm_close', startBot); -} - -function startBot (bot, cerr) { - console.error('RTM closed'); - let token = fs.readFileSync(process.env.SLACK_TOKEN_PATH, { encoding: 'utf8' }); - token = token.replace(/\s/g, ''); - - bot - .spawn({ token: token }) - .startRTM((err) => { - if (err) { - console.error('Failed to start controller!'); - console.error(err); - process.exit(1); - } - }); -} - -function handleSimpleReply (bot, message) { - bot.reply(message, 'Hello.'); -} - -function handleEntitiesReply (bot, message) { - bot.reply(message, 'Top entities: '); - - // Query the database for the top N entities in the past week - const queryTs = Math.floor(Date.now() / 1000) - SEVEN_DAYS_AGO; - // const entitiesWeekSql = `select * from entities`; - const entitiesWeekSql = `${ENTITIES_BASE_SQL} WHERE ts > ${queryTs}${ENTITIES_SQL}`; - db.all(entitiesWeekSql, (err, topEntities) => { - if (err) { - throw err; - } - - let entityInfo = ''; - - // Uncomment this to see the query results logged to console: - // console.log(topEntities); - - topEntities.forEach((entity) => { - entityInfo += `entity: *${entity.name}*, type: ${entity.type}, count: ${entity.wc}\n`; - }); - - bot.reply(message, entityInfo); - }); -} - -function analyzeEntities (text, ts) { - // Instantiates a client - const language = Language(); - - // Instantiates a Document, representing the provided text - const document = { - // The document text, e.g. "Hello, world!" - content: text, - // The type of content to analyze - type: 'PLAIN_TEXT' - }; - - // Detects entities in the document - return language.analyzeEntities({ document: document }) - .then((results) => { - const entities = results[0].entities; - entities.forEach((entity) => { - const name = entity.name; - const type = entity.type; - const salience = entity.salience; - let wikiUrl = ''; - if (entity.metadata.wikipedia_url) { - wikiUrl = entity.metadata.wikipedia_url; - } - - // Uncomment this to see the entity info logged to console: - // console.log(`${name}, type: ${type}, w url: ${wikiUrl}, salience: ${salience}, ts: ${ts}`); - - db.run( - 'INSERT INTO entities VALUES (?, ?, ?, ?, ?);', - [name, type, salience, wikiUrl, Math.round(ts)] - ); - }); - - return entities; - }); -} - -function analyzeSentiment (text) { - // Instantiates a client - const language = Language(); - - // Instantiates a Document, representing the provided text - const document = { - // The document text, e.g. "Hello, world!" - content: text, - // The type of content to analyze - type: 'PLAIN_TEXT' - }; - - // Detects the 'sentiment' of some text using the NL API - return language.analyzeSentiment({ document: document }) - .then((results) => { - const sentiment = results[0]; - - // Uncomment the following lines to log the sentiment to the console: - // console.log(`Sentiment: ${sentiment}`) - // if (sentiment.score >= SENTIMENT_THRESHOLD) { - // console.log('Sentiment: positive.'); - // } else if (sentiment.score <= -SENTIMENT_THRESHOLD) { - // console.log('Sentiment: negative.'); - // } - - return sentiment; - }); -} - -function handleAmbientMessage (bot, message) { - // Note: for purposes of this example, we're making two separate calls to the - // API, one to extract the entities from the message, and one to analyze the - // 'sentiment' of the message. These could be combined into one call. - return analyzeEntities(message.text, message.ts) - .then(() => analyzeSentiment(message.text)) - .then((sentiment) => { - if (sentiment.score >= SENTIMENT_THRESHOLD) { - // We have a positive sentiment score larger than the threshold. - bot.reply(message, ':thumbsup:'); - } else if (sentiment.score <= -SENTIMENT_THRESHOLD) { - // We have a negative sentiment score of absolute value larger than - // the threshold. - bot.reply(message, ':thumbsdown:'); - } - }); -} - -exports.ENTITIES_SQL = ENTITIES_SQL; -exports.TABLE_SQL = TABLE_SQL; -exports.startController = startController; -exports.handleSimpleReply = handleSimpleReply; -exports.handleEntitiesReply = handleEntitiesReply; -exports.analyzeEntities = analyzeEntities; -exports.analyzeSentiment = analyzeSentiment; -exports.handleAmbientMessage = handleAmbientMessage; - -if (require.main === module) { - startController(); -} diff --git a/language/slackbot/generate-dep.sh b/language/slackbot/generate-dep.sh deleted file mode 100755 index 606cb22003..0000000000 --- a/language/slackbot/generate-dep.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/bin/bash -# Copyright 2016 Google Inc. All Rights Reserved. -# -# 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 -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# 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. - -if [[ $# -ne 1 ]] ; then - echo "Your project ID must be specified." - echo "Usage:" >&2 - echo " ${0} my-cloud-project" >&2 - exit 1 -fi -cloud_project=$1 - -cat < slack-bot-dep.yaml -apiVersion: v1 -kind: PersistentVolume -metadata: - name: slackbotstore -spec: - capacity: - storage: 20Gi - accessModes: - - ReadWriteOnce - gcePersistentDisk: - pdName: slackbotstore - fsType: ext4 ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: sb-pv-claim - labels: - name: slack-bot -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 20Gi ---- -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: slack-bot -spec: - replicas: 1 - template: - metadata: - labels: - name: slack-bot - spec: - containers: - - name: master - image: gcr.io/${cloud_project}/slack-bot - volumeMounts: - - name: slack-token - mountPath: /etc/slack-token - - name: slackbot-persistent-storage - mountPath: /var/sqlite3 - env: - - name: SLACK_TOKEN_PATH - value: /etc/slack-token/slack-token - - name: GCLOUD_PROJECT - value: ${cloud_project} - volumes: - - name: slack-token - secret: - secretName: slack-token - - name: slackbot-persistent-storage - persistentVolumeClaim: - claimName: sb-pv-claim -END diff --git a/language/slackbot/generate-rc.sh b/language/slackbot/generate-rc.sh deleted file mode 100755 index e367d2eb44..0000000000 --- a/language/slackbot/generate-rc.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash -# Copyright 2016 Google Inc. All Rights Reserved. -# -# 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 -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# 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. - -if [[ $# -ne 1 ]] ; then - echo "Your project ID must be specified." - echo "Usage:" >&2 - echo " ${0} my-cloud-project" >&2 - exit 1 -fi -cloud_project=$1 - -cat < slack-bot-rc.yaml -apiVersion: v1 -kind: ReplicationController -metadata: - name: slack-bot -spec: - replicas: 1 - template: - metadata: - labels: - name: slack-bot - spec: - containers: - - name: master - image: gcr.io/${cloud_project}/slack-bot - volumeMounts: - - name: slack-token - mountPath: /etc/slack-token - env: - - name: SLACK_TOKEN_PATH - value: /etc/slack-token/slack-token - - name: GCLOUD_PROJECT - value: ${cloud_project} - volumes: - - name: slack-token - secret: - secretName: slack-token -END diff --git a/language/slackbot/generate-secret.sh b/language/slackbot/generate-secret.sh deleted file mode 100755 index 157ec02513..0000000000 --- a/language/slackbot/generate-secret.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash -# Copyright 2016 Google Inc. All Rights Reserved. -# -# 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 -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# 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. - -if [[ $# -ne 1 ]] ; then - echo "Your slack token must be specified." - echo "Usage:" >&2 - echo " ${0} MY-SLACK-TOKEN" >&2 - exit 1 -fi - -token=$1 -token_base64=$(python -c "import base64; print base64.b64encode(\"${token}\")") - -cat < slack-token-secret.yaml -apiVersion: v1 -kind: Secret -metadata: - name: slack-token -type: Opaque -data: - slack-token: ${token_base64} -END diff --git a/language/slackbot/package.json b/language/slackbot/package.json deleted file mode 100644 index 90b5290a4f..0000000000 --- a/language/slackbot/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "kubernetes-slack-botkit-example", - "description": "A Slack Botkit bot, running on Kubernetes.", - "version": "0.0.1", - "private": true, - "license": "Apache-2.0", - "author": "Google Inc.", - "repository": { - "type": "git", - "url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git" - }, - "cloud-repo-tools": { - "requiresKeyFile": true, - "requiresProjectId": true - }, - "main": "demo_bot.js", - "engines": { - "node": ">=4.3.2" - }, - "scripts": { - "lint": "repo-tools lint", - "pretest": "npm run lint", - "test": "repo-tools test run --cmd ava -- -T 20s --verbose system-test/*.test.js" - }, - "dependencies": { - "@google-cloud/language": "0.11.0", - "botkit": "0.5.7", - "sqlite3": "3.1.9" - }, - "devDependencies": { - "@google-cloud/nodejs-repo-tools": "2.3.0", - "ava": "0.25.0", - "proxyquire": "1.8.0", - "semistandard": "^12.0.1", - "sinon": "3.2.0" - } -} diff --git a/language/slackbot/system-test/controller.test.js b/language/slackbot/system-test/controller.test.js deleted file mode 100644 index 8e9008ae96..0000000000 --- a/language/slackbot/system-test/controller.test.js +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright 2017, Google, Inc. - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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. - */ - -'use strict'; - -const fs = require(`fs`); -const path = require(`path`); -const proxyquire = require(`proxyquire`).noCallThru(); -const sinon = require(`sinon`); -const test = require(`ava`); - -const SLACK_TOKEN_PATH = path.join(__dirname, `../.token`); - -let controllerMock, botkitMock, program, originalToken; - -test.before((t) => { - originalToken = process.env.SLACK_TOKEN_PATH; - controllerMock = { - spawn: sinon.stub().returnsThis(), - startRTM: sinon.stub().returnsThis(), - hears: sinon.stub().returnsThis(), - on: sinon.stub().returnsThis() - }; - botkitMock = { - slackbot: sinon.stub().returns(controllerMock) - }; - program = proxyquire(`../demo_bot`, { - botkit: botkitMock, - sqlite3: { - verbose: sinon.stub().returns({ - cached: { - Database: sinon.stub().returns({ - run: sinon.stub() - }) - } - }) - } - }); -}); - -test.after.always((t) => { - process.env.SLACK_TOKEN_PATH = originalToken; - try { - fs.unlinkSync(SLACK_TOKEN_PATH); - } catch (err) { - // Ignore error - } -}); - -test(`should check SLACK_TOKEN_PATH`, (t) => { - process.env.SLACK_TOKEN_PATH = ``; - - t.throws(() => { - program.startController(); - }, Error, `Please set the SLACK_TOKEN_PATH environment variable!`); -}); - -test(`should start the controller`, (t) => { - let controller; - - fs.writeFileSync(SLACK_TOKEN_PATH, `test`, { encoding: `utf8` }); - process.env.SLACK_TOKEN_PATH = SLACK_TOKEN_PATH; - - controller = program.startController(); - - t.is(controller === controllerMock, true); - t.is(controllerMock.spawn.callCount, 1); - t.is(controllerMock.startRTM.callCount, 1); - t.is(controllerMock.hears.callCount, 2); - t.is(controllerMock.on.callCount, 2); -}); diff --git a/language/slackbot/system-test/demo_bot.test.js b/language/slackbot/system-test/demo_bot.test.js deleted file mode 100644 index 2406bcfdca..0000000000 --- a/language/slackbot/system-test/demo_bot.test.js +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Copyright 2017, Google, Inc. - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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. - */ - -'use strict'; - -const fs = require(`fs`); -const path = require(`path`); -const proxyquire = require(`proxyquire`).noCallThru(); -const sinon = require(`sinon`); -const sqlite3 = require(`sqlite3`).verbose(); -const test = require(`ava`); -const tools = require(`@google-cloud/nodejs-repo-tools`); - -const DB_PATH = path.join(__dirname, `../slackDB.db`); -const SLACK_TOKEN_PATH = path.join(__dirname, `../.token`); -const text = `President Obama is speaking at the White House. He is announcing an amazing new cookie recipe.`; - -let db, controllerMock, botkitMock, botMock, program; - -test.before(tools.checkCredentials); -test.before.cb((t) => { - fs.unlink(DB_PATH, (err) => { - if (err && err.code !== `ENOENT`) { - t.end(err); - return; - } - - db = new sqlite3.cached.Database(DB_PATH); - controllerMock = { - spawn: sinon.stub().returnsThis(), - startRTM: sinon.stub().returnsThis(), - hears: sinon.stub().returnsThis(), - on: sinon.stub().returnsThis() - }; - - botkitMock = { - slackbot: sinon.stub().returns(controllerMock) - }; - - botMock = { - reply: sinon.stub() - }; - - program = proxyquire(`../demo_bot`, { - botkit: botkitMock - }); - - db.run(program.TABLE_SQL, t.end); - }); -}); - -test.after.cb.always((t) => { - fs.unlink(DB_PATH, (err) => { - if (err) { - t.end(err); - return; - } - try { - fs.unlinkSync(SLACK_TOKEN_PATH); - } catch (err) { - // Ignore error - } - t.end(); - }); -}); - -test.serial(`should analyze sentiment in text`, async (t) => { - const results = await program.analyzeSentiment(text); - t.is(results.documentSentiment.score > 0, true); -}); - -test.serial(`should analyze entities in text`, async (t) => { - const entities = await program.analyzeEntities(text, Date.now()); - t.is(entities.some((entity) => entity.name === `Obama`), true); - t.is(entities.some((entity) => entity.name === `White House`), true); - - await new Promise((resolve, reject) => { - setTimeout(() => { - db.all(`select * from entities`, (err, entities) => { - if (err) { - reject(err); - return; - } - t.is(entities.some((entity) => entity.name === `Obama`), true); - t.is(entities.some((entity) => entity.name === `White House`), true); - resolve(); - }); - }, 1000); - }); -}); - -test.serial(`should reply to simple hello message`, (t) => { - const message = {}; - - program.handleSimpleReply(botMock, message); - - t.is(botMock.reply.callCount, 1); - t.deepEqual(botMock.reply.getCall(0).args, [message, `Hello.`]); -}); - -test.cb.serial(`should reply to entities message`, (t) => { - const message = {}; - - program.handleEntitiesReply(botMock, message); - - setTimeout(() => { - try { - t.is(botMock.reply.callCount, 3); - t.deepEqual(botMock.reply.getCall(1).args, [message, `Top entities: `]); - t.deepEqual(botMock.reply.getCall(2).args, [message, `entity: *Obama*, type: PERSON, count: 1\nentity: *White House*, type: LOCATION, count: 1\nentity: *cookie recipe*, type: WORK_OF_ART, count: 1\n`]); - t.end(); - } catch (err) { - t.end(err); - } - }, 1000); -});