├── .gitignore ├── README.md ├── echo_hookmanagement.lua ├── img └── uber_sf_example.png ├── uber_getLatLngFromGoogle.lua └── uber_slackNotification.lua /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .AppleDouble 3 | .LSOverride 4 | 5 | # Icon must end with two \r 6 | Icon 7 | 8 | 9 | # Thumbnails 10 | ._* 11 | 12 | # Files that might appear on external disk 13 | .Spotlight-V100 14 | .Trashes 15 | 16 | # Directories potentially created on remote AFP share 17 | .AppleDB 18 | .AppleDesktop 19 | Network Trash Folder 20 | Temporary Items 21 | .apdisk 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Uber Slack Bot 2 | 3 | Get Uber price estimates directly in Slack. 4 | 5 | # How-to 6 | Command to post in a Slack channel 7 | 8 | `uber price start=ADDRESS OF START POINT end=ADDRESS OF END POINT` 9 | 10 | ### examples 11 | `uber price start=ATT park, san francisco end=dolores park, san francisco` 12 | 13 | result: 14 | 15 | ![Screenshot Uber estime ATT Park to Dolores](http://i.imgur.com/kFbvEk8.png?1) 16 | 17 | Failed request `uber price Heaven to Hell` 18 | 19 | ![Screenshot failed request](https://i.imgur.com/SRYaBRW.png) 20 | 21 | Request in a city that does not have Uber availble 22 | 23 | ![Screenshot not available](https://i.imgur.com/8lxkiia.png) 24 | 25 | ## Installation guide 26 | 27 | ### Pre-requisite 28 | - Uber API account, [create one](https://developer.uber.com/) 29 | - Google Maps API account, [create one](https://console.developers.google.com/) 30 | - Slack organization, [create one](https://slack.com/) 31 | - APItools account, [create one](https://apitools.com?ref=uberbot) 32 | - Time :) 33 | 34 | 35 | ### Slack part 36 | #### 1. Create outgoing webhook 37 | Outgoing webhook is used to "listen" what's happening on channels. 38 | To create a new one go on `https://YOUR_ORG.slack.com/services/new` 39 | 40 | On the next page you will select on which channel of your organization you want to bot to be active. And the triggered words. Triggered words are the words that will make the bot react. In our case *breez*. 41 | 42 | At the bottom of the page you can give a cute name to your bot as well as an avatar. 43 | We will come back later to this page to fill the other fields. 44 | 45 | #### 2. Create incoming webhook 46 | Incoming webhook is used to send data to channels. 47 | Again we go on `https://YOUR_ORG.slack.com/services/new` 48 | And create an `incoming webhook`. 49 | 50 | Define on which channel you want to post, change the name of the bot and it's icon. All these could be overide later. 51 | 52 | Keep the `Webhook URL` somewhere we will need it later to send a `POST` request and send data to slack. 53 | 54 | ### APItools 55 | Using APItools is a great way to avoid maintaining servers for something as simple as webhooks. It's also free to use. 56 | 57 | #### Google API 58 | Create a new service with the *API URL* `https://maps.googleapis.com/maps/api/` 59 | This service will be used to make all the requests to Google Maps API. 60 | 61 | We don't have any middleware code for this service. 62 | 63 | #### Uber API 64 | Create a new service with the *API URL* `https://api.uber.com/v1/` 65 | This service will be used to make all the requests to Uber API. 66 | 67 | We now are going to add two middlewares. First one will be used to convert street names to latitude longitude coordinates using service previously created. 68 | Code for this middleware could be found in `uber_getLatLngFromGoogle.lua` file. 69 | 70 | In this file change: 71 | 72 | - `GOOGLE_API_KEY` by your own Google API key 73 | - `APITOOLS_GOOGLE_SERVICEURL` by the APItools service URL for the Google Maps API. 74 | - `UBER_TOKEN` by your Uber Token 75 | 76 | Then add the second middleware that will send the message to slack. Code could be found in `uber_slackNotification.lua` file. 77 | 78 | In the code change `SLACK_INCOMING_URL` to your slack incoming webhook URL created before 79 | 80 | Your pipeline should look like this: 81 | 82 | ![APItools pipeline](https://i.imgur.com/IY8uiZK.png) 83 | 84 | #### Handling webhook 85 | Anytime our triggered word is used in a channel, slack will make a request to an URL. We want to URL to be an API service. This service will handle the outgoing webhook request from slack. 86 | 87 | Use the echo-api as *API URL* `https://echo-api.herokuapp.com` 88 | 89 | Copy *APItool URL* that should look like `https://SOMETHING.my.apitools.com/`. 90 | Go back to the *Outgoing webhook* config page you previsouly created. Pasted the APItools URL in the *URLs* field. 91 | 92 | ![Add APItools URL to outgoing webhook](https://i.imgur.com/QCL5rHP.png) 93 | 94 | On APItools, on your newly created service, go in the pipeline and paste the code found in the file `echo_hookmanagement.lua` of this repo. 95 | 96 | In this snippet change placeholders: 97 | 98 | - `SLACK_INCOMING_HOOK_URL` by the Incoming Webhook URL you created in Slack. 99 | - `APITOOLS_UBER_SERVICE_URL` by APItools service URL for Uber API. 100 | -------------------------------------------------------------------------------- /echo_hookmanagement.lua: -------------------------------------------------------------------------------- 1 | -- fct to split a string by a delimeter 2 | local function split(s, delimiter) 3 | local result = {} 4 | for match in (s..delimiter):gmatch("(.-)"..delimiter) do 5 | table.insert(result, match) 6 | end 7 | return result 8 | end 9 | 10 | local hex_to_char = function(x) 11 | return string.char(tonumber(x, 16)) 12 | end 13 | 14 | local unescape = function(url) 15 | return url:gsub("%%(%x%x)", hex_to_char) 16 | end 17 | 18 | return function(request, next_middleware) 19 | local response = next_middleware() 20 | local hookURL = "SLACK_INCOMING_HOOK_URL" 21 | 22 | if(request.uri == '/hook') then 23 | local params = split(request.body,'&') 24 | local decoded_params = {} 25 | -- turn urlencoded string into an object 26 | for i=1,#params do 27 | local p = split(params[i],'=') 28 | decoded_params[p[1]] = p[2] 29 | end 30 | 31 | local query = decoded_params['text'] 32 | query = unescape(query) -- decode special chars 33 | query = string.gsub(query,"+"," ") -- turn + into spaces 34 | 35 | if(string.match(query,"^uber price start=[a-zA-Z0-9_, ]* end=[a-zA-Z0-9_, ]*")) then 36 | console.log("MATCH") 37 | local start_point = string.sub(query,string.find(query, "start=")+6,string.find(query, "end=")-2) 38 | local end_point = string.sub(query,string.find(query, "end=")+4) 39 | 40 | start_point = string.gsub(start_point," ","%%20") 41 | end_point = string.gsub(end_point," ","%%20") 42 | 43 | local url = "APITOOLS_UBER_SERVICE_URL/estimates/price?start_point="..start_point.."&end_point="..end_point.."&channel="..decoded_params.channel_name 44 | console.log(url) 45 | local r = http.get(url) 46 | console.log(r) 47 | else 48 | console.log("DONT MATCH") 49 | msg = 'Malformated request. Format is: `uber price start=START_ADDRESS end=END_ADDRESS`' 50 | local r = http.json.post(hookURL,'{"text": "'..msg..'","channel":"#'.. decoded_params.channel_name..'"}') 51 | console.log(r) 52 | end 53 | end 54 | return response 55 | end 56 | -------------------------------------------------------------------------------- /img/uber_sf_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/picsoung/uberSlackBot/0476eee9a46f125b72d009f27f39e7c15b3d388f/img/uber_sf_example.png -------------------------------------------------------------------------------- /uber_getLatLngFromGoogle.lua: -------------------------------------------------------------------------------- 1 | local start_latlng 2 | local end_latlng 3 | 4 | function getLatLng(start_point,end_point) 5 | local google_key = "GOOGLE_API_KEY" 6 | local google_url ="APITOOLS_GOOGLE_SERVICEURL/geocode/json?address=" 7 | 8 | start_point = string.gsub(start_point," ","+") 9 | end_point = string.gsub(end_point," ","+") 10 | 11 | local request_url = google_url .. start_point .. "&key="..google_key 12 | local resp_geocoding = http.get(request_url) 13 | start_latlng = json.decode(resp_geocoding.body).results[1].geometry.location 14 | 15 | request_url = google_url .. end_point .. "&key="..google_key 16 | resp_geocoding = http.get(request_url) 17 | end_latlng = json.decode(resp_geocoding.body).results[1].geometry.location 18 | end 19 | 20 | return function(request, next_middleware) 21 | local start_point = request.args.start_point 22 | local end_point = request.args.end_point 23 | 24 | getLatLng(start_point,end_point) 25 | 26 | request.args.start_latitude = start_latlng.lat 27 | request.args.start_longitude = start_latlng.lng 28 | request.args.end_latitude = end_latlng.lat 29 | request.args.end_longitude = end_latlng.lng 30 | local uber_token = "UBER_TOKEN" 31 | request.args.server_token = uber_token 32 | 33 | local response = next_middleware() 34 | return response 35 | end 36 | -------------------------------------------------------------------------------- /uber_slackNotification.lua: -------------------------------------------------------------------------------- 1 | return function(request, next_middleware) 2 | local response = next_middleware() 3 | 4 | local prices = json.decode(response.body).prices 5 | local msg ="" 6 | if(#prices >0) then 7 | emoji = {} 8 | emoji["uberX"]="\xF0\x9F\x9A\x97" 9 | emoji["uberXL"]= "\xF0\x9F\x9A\x99" 10 | emoji["UberBLACK"]= "\xF0\x9F\x9A\x93" 11 | emoji["UberSUV"]= "\xF0\x9F\x9A\x90" 12 | emoji["uberTAXI"]= "\xF0\x9F\x9A\x95" 13 | pretext = "Prices for a distance of " .. prices[1].distance .. "km in " .. math.floor(prices[1].duration/60) .. " minutes" 14 | msg ="" 15 | for i=1,#prices do 16 | msg = msg .. emoji[prices[i].display_name].." "..prices[i].display_name .." -> ".. prices[i].estimate .. " surge: ".. prices[1].surge_multiplier .."\\n" 17 | end 18 | else 19 | msg = "Uber does not seem to be available in this city" 20 | end 21 | local hookURL = "SLACK_INCOMING_URL" 22 | console.log(msg,pretext) 23 | local r = http.json.post(hookURL,'{"channel":"#'.. request.args.channel..'","attachments":[{"fallback":"'..pretext..'","pretext":"'..pretext..'","color":"#1fbad6","fields":[{"title":"Prices","value":"'..msg..'","short":false}]}]}') 24 | 25 | console.log(r) 26 | return response 27 | end 28 | --------------------------------------------------------------------------------