{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Webex Teams ChatOps\n",
    "\n",
    "Exam Topics Covered:\n",
    "3.1 Construct API requests to implement chatops with Webex Teams API\n",
    "\n",
    "## Overview\n",
    "This section shows how to build a ChatBot in WebEx Teams. The overall work flow is the following:\n",
    "\n",
    "1. Create a bot at developer.webex.com\n",
    "2. Create a name for the bot and get a token which is good for 100 years\n",
    "3. Add the chatbot to the room\n",
    "4. Get the ID for the room\n",
    "5. Create a Webhook through the API which monitors the room for events and sends a notification to your application when the event is triggered\n",
    "6. When an event is triggered, the webhook is sent to your application\n",
    "7. The webhook indicates the event that occurred,  your application must then use the API to retrieve the content and generate a response"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The below application runs on Python Flask which must be installed with `pip install flask`. In order to run the application an environment variable must be set:  `FLASK_APP=chatbot_server.py`. It can then be run with `flask run`. In addition, in order for the application to receive the webhook from Webex teams it must be reachable over the public Internet.  I used an online proxy called NGrok (https://ngrok.com/) to receive the webook from WebEx teams and tunnel it to my local workstation.  When running NGrok it provides a URL which is used as the `targetUrl` in the webhook. Note that the URL changes each time you restart NGrok, so this is only useable for testing. NGrok also provides a static URL for a small fee.|\n",
    "\n",
    "The application programs a webhook using the API upon first startup. It then waits for a webhook,  which is triggered by a message in the Teams room with the bot mentioned.  When the webhook is received, the application connects back to the API to retrieve the message based on the message ID that was provided in the webhook.  It then parses the text of the message, and posts a reply to the room.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "from flask import Flask, request, render_template, Response\n",
    "import requests\n",
    "import os\n",
    "from datetime import datetime\n",
    "import re\n",
    "import json\n",
    "\n",
    "# Create the web application instance\n",
    "flask_app = Flask(__name__)\n",
    "\n",
    "# Grab the API token from the environment. This is the bot token. \n",
    "token = os.environ.get('TEAMS_ACCESS_TOKEN')\n",
    "\n",
    "# This is the base_url for all API calls\n",
    "base_url = \"https://webexapis.com\"\n",
    "\n",
    "# An Authorization header must be set with the bot token\n",
    "headers = {'Content-Type':'application/json',\n",
    "                   'Accept': 'application/json',\n",
    "                   'Authorization': f'Bearer {token}'}\n",
    "                   \n",
    "# Get rooms to determine RoomID\n",
    "resp = requests.get(f\"{base_url}/v1/rooms\", headers=headers)\n",
    "room_id = [item['id'] for item in resp.json()['items'] if \"Bot_Testing\" == item['title']][0]\n",
    "\n",
    "# Webhook payload\n",
    "# The resource is messages being sent to the room\n",
    "# The filter indicates the webhook should trigger when messages are sent to\n",
    "# \"me\", which indicates the bot\n",
    "\n",
    "webhook_data = dict(name=\"chatbot_webhook\",\n",
    "                    targetUrl=\"http://eb54696397db.ngrok.io/events\",\n",
    "                    resource=\"messages\",\n",
    "                    filter=f\"roomId={room_id}&mentionedPeople=me\",\n",
    "                    event=\"created\")\n",
    "\n",
    "# Check if webhook already created. Prevents a new webhook from \n",
    "# being created every time the program is run\n",
    "try:\n",
    "    resp = requests.get(f\"{base_url}/v1/webhooks\", headers=headers)\n",
    "except requests.exceptions.RequestException as e:\n",
    "    print(e)\n",
    "# Generate list of current webhook names\n",
    "webhooks = [webhook.get('name') for webhook in resp.json()['items']]\n",
    "\n",
    "# Create webhook, if not already created\n",
    "if \"chatbot_webhook\" not in webhooks:\n",
    "    try:\n",
    "        resp = requests.post(f\"{base_url}/v1/webhooks\", headers=headers, json=webhook_data)\n",
    "        resp.raise_for_status()\n",
    "        print(\"Created webhook: chatbot_webhook\")\n",
    "    except Exception as e:\n",
    "        print(e)\n",
    "else:\n",
    "    print(\"Webhook already configured,  skipping\")\n",
    "\n",
    "# This is a function that grabs current Covid cases in a specified\n",
    "# NC zip code. The user can send a message to the bot with their \n",
    "# zip code and the bot will reply with the current number of cases\n",
    "def get_cases(zip_code):\n",
    "    url = f\"https://services.arcgis.com/iFBq2AW9XO0jYYF7/\" \\\n",
    "          f\"arcgis/rest/services/Covid19byZIPnew/FeatureServer\" \\\n",
    "          f\"/0/query?where=ZIPCode={zip_code}&outFields=*&f=json\"\n",
    "    headers = {'Accept': 'application/json'}\n",
    "    resp = requests.get(url, headers=headers)\n",
    "    cases = resp.json()['features'][0]['attributes']['Cases']\n",
    "    return cases\n",
    "\n",
    "\n",
    "@flask_app.route('/', methods=['GET', 'POST'])\n",
    "def home_page():\n",
    "    return render_template('index.html')\n",
    "\n",
    "\n",
    "@flask_app.route('/events', methods=['GET', 'POST'])\n",
    "def webex_teams_webhook_events():\n",
    "    # App doesn't do anything on a GET\n",
    "    if request.method == 'GET':\n",
    "        return f\"Nothing to do here!\"\n",
    "    # The webhook issues a POST to the TargetURL \n",
    "    elif request.method == 'POST':\n",
    "        # Respond to incoming webhook from Webex Teams\n",
    "        json_data = request.get_json()\n",
    "        # This information will be displayed on the console where\n",
    "        # flask run was executed\n",
    "        print(f\"TIME: {datetime.now().strftime('%H:%M:%S')}\")\n",
    "        print(\"\\nWEBHOOK POST RECEIVED:\")\n",
    "        print(f\"{json_data}\\n\")\n",
    "        # Parse the json for the message ID and then retrieve the message\n",
    "        msg_id = json_data['data']['id']\n",
    "        # Retrieve the message\n",
    "        resp = requests.get(f\"{base_url}/v1/messages/{msg_id}\", headers=headers)\n",
    "        # Parse the response\n",
    "        msg = resp.json()\n",
    "        print(f\"Message from: {msg['personEmail']}\")\n",
    "        print(f\"Message: {msg['text']}\")\n",
    "        print(msg)\n",
    "        if 'cases' in msg['text'].lower():\n",
    "            m = re.search('[0-9]{5}', msg['text'])\n",
    "            if m:\n",
    "                zip = m.group()\n",
    "                cases = get_cases(zip)\n",
    "                msg_text = f\"The number of cases that have been reported in {zip} is {cases}\"\n",
    "            else:\n",
    "                msg_text = 'Please give me a 5-digit NC zip code'\n",
    "        else:\n",
    "            msg_text = \"Not sure what you're asking, you can ask me things like 'how many cases in zip code 27502?'\"\n",
    "\n",
    "        # Respond to the message\n",
    "        msg_data = dict(roomId=msg['roomId'],\n",
    "                        parentId=msg['id'],\n",
    "                        text=msg_text)\n",
    "        resp = requests.post(f\"{base_url}/v1/messages\", headers=headers, json=msg_data)\n",
    "        print(resp.status_code, resp.content)\n",
    "\n",
    "\n",
    "        return \"OK\"\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    # Start the flask web server\n",
    "    flask_app.run(host='0.0.0.0', port=5001)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Below is the `index.html` file that should be placed in the `templates` directory. \n",
    "\n",
    "```\n",
    "<!DOCTYPE html>\n",
    "<html lang=\"en\">\n",
    "   <head>\n",
    "       <meta charset=\"UTF-8\">\n",
    "       <title>Webex Teams Bot served via Flask</title>\n",
    "   </head>\n",
    "<body>\n",
    "<p>\n",
    "<strong>Flask web server is up and running!</strong>\n",
    "</p>\n",
    "</body>\n",
    "</html>\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For comparison, here is the exact same application written using the Webex Teams SDK.  The Webex Teams SDK can be installed by running `pip install webexteamssdk`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from flask import Flask, request, render_template\n",
    "from webexteamssdk import WebexTeamsAPI\n",
    "from datetime import datetime\n",
    "import requests\n",
    "import os\n",
    "import re\n",
    "\n",
    "# Create the web application instance\n",
    "flask_app = Flask(__name__)\n",
    "token = os.environ.get('TEAMS_ACCESS_TOKEN')\n",
    "\n",
    "api = WebexTeamsAPI(access_token=token)\n",
    "room_id = [room.id for room in api.rooms.list() if room.title == 'Bot_Testing'][0]\n",
    "\n",
    "webhooks_list = [webhook.name for webhook in api.webhooks.list()]\n",
    "if 'chatbot_webhook' not in webhooks_list:\n",
    "    api.webhooks.create(name='chatbot_webhook2',\n",
    "                        targetUrl='http://eb54696397db.ngrok.io/events',\n",
    "                        resource='messages',\n",
    "                        event='created',\n",
    "                        filter=f\"roomId={room_id}&mentionedPeople=me\")\n",
    "\n",
    "else:\n",
    "    print('chatbot_webhook already configured, skipping')\n",
    "\n",
    "\n",
    "def get_cases(zip_code):\n",
    "    url = f\"https://services.arcgis.com/iFBq2AW9XO0jYYF7/\" \\\n",
    "          f\"arcgis/rest/services/Covid19byZIPnew/FeatureServer\" \\\n",
    "          f\"/0/query?where=ZIPCode={zip_code}&outFields=*&f=json\"\n",
    "    headers = {'Accept': 'application/json'}\n",
    "    resp = requests.get(url, headers=headers)\n",
    "    cases = resp.json()['features'][0]['attributes']['Cases']\n",
    "    return cases\n",
    "\n",
    "\n",
    "@flask_app.route('/', methods=['GET', 'POST'])\n",
    "def home_page():\n",
    "    return render_template('index.html')\n",
    "\n",
    "\n",
    "@flask_app.route('/events', methods=['GET', 'POST'])\n",
    "def webex_teams_webhook_events():\n",
    "    if request.method == 'GET':\n",
    "        return f\"Nothing to do here!\"\n",
    "    elif request.method == 'POST':\n",
    "        \"\"\"Respond to incoming webhook from Webex Teams\"\"\"\n",
    "        json_data = request.get_json()\n",
    "        print(f\"TIME: {datetime.now().strftime('%H:%M:%S')}\")\n",
    "        print(\"\\nWEBHOOK POST RECEIVED:\")\n",
    "        print(f\"{json_data}\\n\")\n",
    "        # Parse the json for the message ID and then retrieve the message\n",
    "        msg_id = json_data['data']['id']\n",
    "        # Get the posted message\n",
    "        message = api.messages.get(msg_id)\n",
    "        print(message)\n",
    "        if 'cases' in message.text.lower():\n",
    "            m = re.search(\"[0-9]{5}\", message.text)\n",
    "            if m:\n",
    "                zipcode = m.group()\n",
    "                cases = get_cases(zipcode)\n",
    "                msg_text = f\"The number of cases in {zipcode} is {cases}\"\n",
    "            else:\n",
    "                msg_text = f\"Please include the 5-digit NC zip code you would like me to search in\"\n",
    "        else:\n",
    "            msg_text = f\"I didn't understand. You can ask me things like 'How many cases are in zip code 27502?'\"\n",
    "        api.messages.create(roomId=message.roomId,\n",
    "                            parentId=message.id,\n",
    "                            text=msg_text)\n",
    "        return \"OK\"\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    # Start the flask web server\n",
    "    flask_app.run(host='0.0.0.0', port=5001)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}