├── .dockerignore ├── .gitignore ├── Dockerfile ├── Procfile ├── README.md ├── bin ├── hubot └── hubot.cmd ├── dev.Dockerfile ├── dev_docker-compose.yml ├── docker-compose.yml ├── external-scripts.json ├── hubot-scripts.json ├── package.json ├── scripts ├── batch-score.coffee ├── birthday.coffee ├── bye.coffee ├── detailed-score.coffee ├── events.coffee ├── fb-feed.coffee ├── fblikes.coffee ├── google-images.coffee ├── help.coffee ├── httpd.coffee ├── idlecheck.coffee ├── info.coffee ├── keys.coffee ├── leaderboard.coffee ├── maps.coffee ├── middleware.coffee ├── mirror.coffee ├── most-spoken-words.coffee ├── openclose.coffee ├── ping.coffee ├── pugme.coffee ├── random-quote.coffee ├── roles.coffee ├── rules.coffee ├── seen.coffee ├── skipped-word.coffee ├── storage.coffee ├── toss.coffee ├── translate.coffee ├── update-names.coffee ├── util.coffee ├── wail.coffee └── youtube.coffee └── start_bot.sh /.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | 3 | node_modules 4 | npm-debug.log 5 | 6 | **/*.log 7 | **/*.md 8 | **/.dockerignore 9 | **/.git/ 10 | **/.gitattributes 11 | **/.gitignore 12 | **/.gitkeep 13 | **/.gitmodules 14 | **/Dockerfile 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store* 3 | .hubot_history 4 | .env 5 | data 6 | .idea 7 | package-lock.json -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:8.10.0 2 | 3 | # Environment variables: 4 | 5 | # Forces non-interactive mode for apt commands. 6 | ENV DEBIAN_FRONTEND "noninteractive" 7 | 8 | # Slack's bot name can be changed from the settings panel of Slack 9 | ENV HUBOT_NAME "bot" 10 | ENV HUBOT_OWNER "Mobile Development Group " 11 | ENV HUBOT_SLACK_TEAM "mdgiitr" 12 | ENV HUBOT_DESCRIPTION "Slack bot for Mobile Development Group, IIT Roorkee" 13 | ENV TZ "Asia/Kolkata" 14 | ENV FB_WAIT_MINUTES "1" 15 | ENV IDLE_TIME_DURATION_HOURS "4" 16 | ENV HUBOT_YOUTUBE_HEAR "true" 17 | 18 | ENV PORT "8080" 19 | 20 | # Add user 21 | RUN useradd hubot -m 22 | COPY . /home/hubot 23 | RUN rm /home/hubot/external-scripts.json 24 | 25 | # Make sure that the files have the right owner and group. 26 | RUN chown -R hubot:hubot /home/hubot 27 | 28 | USER hubot 29 | WORKDIR /home/hubot 30 | 31 | # Set a default command to run Hubot! 32 | # hubot calls npm install internally 33 | CMD ./bin/hubot -n $HUBOT_NAME -a slack 34 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: bin/hubot -a slack -n bot 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hubot 2 | 3 | This is a version of GitHub's Campfire bot, hubot. He's pretty cool. 4 | 5 | This version is designed to be deployed on [Heroku][heroku]. This README was generated for you by hubot to help get you started. Definitely update and improve to talk about your own instance, how to use and deploy, what functionality he has, etc! 6 | 7 | [heroku]: http://www.heroku.com 8 | 9 | ### Testing Hubot Locally 10 | 11 | You can test your hubot by running the following. 12 | 13 | % bin/hubot 14 | 15 | You'll see some start up output about where your scripts come from and a 16 | prompt. 17 | 18 | [Sun, 04 Dec 2011 18:41:11 GMT] INFO Loading adapter shell 19 | [Sun, 04 Dec 2011 18:41:11 GMT] INFO Loading scripts from /home/tomb/Development/hubot/scripts 20 | [Sun, 04 Dec 2011 18:41:11 GMT] INFO Loading scripts from /home/tomb/Development/hubot/src/scripts 21 | Hubot> 22 | 23 | Then you can interact with hubot by typing `hubot help`. 24 | 25 | Hubot> hubot help 26 | 27 | Hubot> animate me - The same thing as `image me`, except adds a few 28 | convert me to - Convert expression to given units. 29 | help - Displays all of the help commands that Hubot knows about. 30 | ... 31 | 32 | 33 | ### Scripting 34 | 35 | Take a look at the scripts in the `./scripts` folder for examples. 36 | Delete any scripts you think are useless or boring. Add whatever functionality you 37 | want hubot to have. Read up on what you can do with hubot in the [Scripting Guide](https://github.com/github/hubot/blob/master/docs/scripting.md). 38 | 39 | ### Redis Persistence 40 | 41 | If you are going to use the `redis-brain.coffee` script from `hubot-scripts` 42 | (strongly suggested), you will need to add the Redis to Go addon on Heroku which requires a verified 43 | account or you can create an account at [Redis to Go][redistogo] and manually 44 | set the `REDISTOGO_URL` variable. 45 | 46 | % heroku config:set REDISTOGO_URL="..." 47 | 48 | If you don't require any persistence feel free to remove the 49 | `redis-brain.coffee` from `hubot-scripts.json` and you don't need to worry 50 | about redis at all. 51 | 52 | [redistogo]: https://redistogo.com/ 53 | 54 | ## Adapters 55 | 56 | Adapters are the interface to the service you want your hubot to run on. This 57 | can be something like Campfire or IRC. There are a number of third party 58 | adapters that the community have contributed. Check 59 | [Hubot Adapters][hubot-adapters] for the available ones. 60 | 61 | If you would like to run a non-Campfire or shell adapter you will need to add 62 | the adapter package as a dependency to the `package.json` file in the 63 | `dependencies` section. 64 | 65 | Once you've added the dependency and run `npm install` to install it you can 66 | then run hubot with the adapter. 67 | 68 | % bin/hubot -a 69 | 70 | Where `` is the name of your adapter without the `hubot-` prefix. 71 | 72 | [hubot-adapters]: https://github.com/github/hubot/blob/master/docs/adapters.md 73 | 74 | ## hubot-scripts 75 | 76 | There will inevitably be functionality that everyone will want. Instead 77 | of adding it to hubot itself, you can submit pull requests to 78 | [hubot-scripts][hubot-scripts]. 79 | 80 | To enable scripts from the hubot-scripts package, add the script name with 81 | extension as a double quoted string to the `hubot-scripts.json` file in this 82 | repo. 83 | 84 | [hubot-scripts]: https://github.com/github/hubot-scripts 85 | 86 | ## external-scripts 87 | 88 | Tired of waiting for your script to be merged into `hubot-scripts`? Want to 89 | maintain the repository and package yourself? Then this added functionality 90 | maybe for you! 91 | 92 | Hubot is now able to load scripts from third-party `npm` packages! To enable 93 | this functionality you can follow the following steps. 94 | 95 | 1. Add the packages as dependencies into your `package.json` 96 | 2. `npm install` to make sure those packages are installed 97 | 98 | To enable third-party scripts that you've added you will need to add the package 99 | name as a double quoted string to the `external-scripts.json` file in this repo. 100 | 101 | ## Deployment 102 | 103 | % heroku create --stack cedar 104 | % git push heroku master 105 | % heroku ps:scale app=1 106 | 107 | If your Heroku account has been verified you can run the following to enable 108 | and add the Redis to Go addon to your app. 109 | 110 | % heroku addons:add redistogo:nano 111 | 112 | If you run into any problems, checkout Heroku's [docs][heroku-node-docs]. 113 | 114 | You'll need to edit the `Procfile` to set the name of your hubot. 115 | 116 | More detailed documentation can be found on the 117 | [deploying hubot onto Heroku][deploy-heroku] wiki page. 118 | 119 | ### Deploying to UNIX or Windows 120 | 121 | If you would like to deploy to either a UNIX operating system or Windows. 122 | Please check out the [deploying hubot onto UNIX][deploy-unix] and 123 | [deploying hubot onto Windows][deploy-windows] wiki pages. 124 | 125 | [heroku-node-docs]: http://devcenter.heroku.com/articles/node-js 126 | [deploy-heroku]: https://github.com/github/hubot/blob/master/docs/deploying/heroku.md 127 | [deploy-unix]: https://github.com/github/hubot/blob/master/docs/deploying/unix.md 128 | [deploy-windows]: https://github.com/github/hubot/blob/master/docs/deploying/unix.md 129 | 130 | ## Campfire Variables 131 | 132 | If you are using the Campfire adapter you will need to set some environment 133 | variables. Refer to the documentation for other adapters and the configuraiton 134 | of those, links to the adapters can be found on [Hubot Adapters][hubot-adapters]. 135 | 136 | Create a separate Campfire user for your bot and get their token from the web 137 | UI. 138 | 139 | % heroku config:set HUBOT_CAMPFIRE_TOKEN="..." 140 | 141 | Get the numeric IDs of the rooms you want the bot to join, comma delimited. If 142 | you want the bot to connect to `https://mysubdomain.campfirenow.com/room/42` 143 | and `https://mysubdomain.campfirenow.com/room/1024` then you'd add it like this: 144 | 145 | % heroku config:set HUBOT_CAMPFIRE_ROOMS="42,1024" 146 | 147 | Add the subdomain hubot should connect to. If you web URL looks like 148 | `http://mysubdomain.campfirenow.com` then you'd add it like this: 149 | 150 | % heroku config:set HUBOT_CAMPFIRE_ACCOUNT="mysubdomain" 151 | 152 | [hubot-adapters]: https://github.com/github/hubot/blob/master/docs/adapters.md 153 | 154 | ## Restart the bot 155 | 156 | You may want to get comfortable with `heroku logs` and `heroku restart` 157 | if you're having issues. 158 | 159 | --- 160 | 161 | ## Installation using Docker 162 | - Build the image using `docker build . -t bot:latest` 163 | - Add the environment variables in a .env file 164 | - Create a container to run the image `docker run -d --env-file .env --name bot_cont -p 127.0.0.1::8080 bot` 165 | 166 | ## Development setup using Docker 167 | You can use the `dev_docker-compose.yml` file to spin up containers with Redis services easily. 168 | Use this env variables for the same. 169 | ``` 170 | REDIS_URL=redis://redis:6379 171 | ``` 172 | Run this command to run the containers 173 | ```shell 174 | $ docker-compose -f dev_docker-compose.yml up 175 | ``` 176 | To interact with hubot using shell, 177 | ```shell 178 | $ docker exec -it mdg-bot bash 179 | $ ./bin/hubot 180 | ``` 181 | -------------------------------------------------------------------------------- /bin/hubot: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | npm install 6 | export PATH="node_modules/.bin:node_modules/hubot/node_modules/.bin:$PATH" 7 | 8 | exec node_modules/.bin/hubot "$@" 9 | -------------------------------------------------------------------------------- /bin/hubot.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | npm install && node_modules\.bin\hubot.cmd %* -------------------------------------------------------------------------------- /dev.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:8.10.0 2 | 3 | # Environment variables: 4 | 5 | # Forces non-interactive mode for apt commands. 6 | ENV DEBIAN_FRONTEND "noninteractive" 7 | 8 | # Slack's bot name can be changed from the settings panel of Slack 9 | ENV HUBOT_NAME "bot" 10 | ENV HUBOT_OWNER "Mobile Development Group " 11 | ENV HUBOT_SLACK_TEAM "mdgiitr" 12 | ENV HUBOT_DESCRIPTION "Slack bot for Mobile Development Group, IIT Roorkee" 13 | ENV TZ "Asia/Kolkata" 14 | ENV FB_WAIT_MINUTES "1" 15 | ENV IDLE_TIME_DURATION_HOURS "4" 16 | ENV HUBOT_YOUTUBE_HEAR "true" 17 | 18 | ENV PORT "8080" 19 | 20 | # Add user 21 | RUN useradd hubot -m 22 | COPY . /home/hubot 23 | # Make sure that the files have the right owner and group. 24 | RUN chown -R hubot:hubot /home/hubot 25 | 26 | USER hubot 27 | WORKDIR /home/hubot 28 | 29 | # Set a default command to run Hubot! 30 | # hubot calls npm install internally 31 | CMD ./bin/hubot -n $HUBOT_NAME -a slack 32 | -------------------------------------------------------------------------------- /dev_docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | redis: 4 | image: redis 5 | volumes: 6 | - ./data/redis:/data 7 | bot: 8 | container_name: mdg-bot 9 | build: 10 | context: . 11 | dockerfile: dev.Dockerfile 12 | ports: 13 | - 127.0.0.1:9998:8080 14 | env_file: 15 | - .env 16 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | bot: 4 | container_name: mdg-bot 5 | build: . 6 | ports: 7 | - 127.0.0.1:9998:8080 8 | env_file: 9 | - .env 10 | restart: always -------------------------------------------------------------------------------- /external-scripts.json: -------------------------------------------------------------------------------- 1 | ["hubot-env"] -------------------------------------------------------------------------------- /hubot-scripts.json: -------------------------------------------------------------------------------- 1 | ["redis-brain.coffee", "shipit.coffee"] 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bot", 3 | "version": "2.7.1", 4 | "private": true, 5 | "author": "sdsmdg", 6 | "description": "Slack bot for Mobile Development Group, IIT Roorkee", 7 | "licenses": [ 8 | { 9 | "type": "MIT", 10 | "url": "https://github.com/github/hubot/raw/master/LICENSE" 11 | } 12 | ], 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/sdsmdg/hubot.git" 16 | }, 17 | "dependencies": { 18 | "follow-redirects": "^1.0.0", 19 | "hubot": "^2.19.0", 20 | "hubot-env": "0.0.2", 21 | "hubot-redis-brain": "0.0.3", 22 | "hubot-scripts": "^2.17.2", 23 | "hubot-slack": "^4.3.4", 24 | "libxmljs": "^0.19.10", 25 | "moment": "^2.18.1", 26 | "natural": "^0.5.0", 27 | "node-cron": "^1.1.2", 28 | "node-gyp": "^9.3.1", 29 | "node-time-ago": "^1.0.0", 30 | "nodemon": "2.0.19", 31 | "soupselect": "^0.2.0" 32 | }, 33 | "engines": { 34 | "node": "^8.10.0", 35 | "npm": "^3.5.2" 36 | }, 37 | "scripts": { 38 | "start": "nodemon -e coffee --exec './bin/hubot -n $HUBOT_NAME -a slack'" 39 | }, 40 | "devDependencies": { 41 | "cross-env": "^7.0.3", 42 | "nodemon": "^2.0.19" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /scripts/batch-score.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # Script for batch wise score. 3 | # 4 | # Configuration: 5 | # INFO_SPREADSHEET_URL 6 | # 7 | # Commands: 8 | # hubot score fxx -> for tabular representation 9 | # hubot score fxx -b -> for bar graph 10 | # hubot score fxx -p -> for pie graph 11 | # 12 | # Author: 13 | # aseem09, aman-singh7 14 | 15 | util = require('./util') 16 | 17 | module.exports = (robot) -> 18 | 19 | scorefield = () -> 20 | Field = robot.brain.get("scorefield") or {} 21 | robot.brain.set("scorefield", Field) 22 | Field 23 | 24 | stringLength = (str) -> 25 | return String(str).split('').length 26 | 27 | # makes all the elements in the array equal in width by padding at right 28 | padright = (array) -> 29 | padding = 0 30 | max_length = 0 31 | for name in array 32 | max_length = if max_length >= stringLength(name) 33 | then max_length 34 | else stringLength(name) 35 | padding = max_length 36 | for i in [0..array.length - 1] 37 | max = padding - stringLength(array[i]) 38 | if i == 0 39 | max = max + 3 40 | if max > 0 41 | for j in [0..max - 1] 42 | array[i] += " " 43 | return array 44 | 45 | # makes all the elements in the array equal in width by padding at left 46 | padleft = (array) -> 47 | padding = 5 48 | for i in [0..array.length - 1] 49 | max = padding - stringLength(array[i]) 50 | name = "" 51 | if max > 0 52 | for j in [0..max - 1] 53 | name += " " 54 | name += array[i] 55 | array[i] = name 56 | return array 57 | 58 | parse = (json) -> 59 | result = [] 60 | for line in json.toString().split '\n' 61 | result.push line.split(',').map Function.prototype.call, 62 | String.prototype.trim 63 | if result != "" 64 | result 65 | else 66 | false 67 | 68 | member = (members, year, callback) -> 69 | user_name = [] 70 | slackId = [] 71 | for user in members 72 | if (user.length >= 13) 73 | user_year = user[4].split('') 74 | year_info = parseInt(user_year[0], 10 ) 75 | if year == year_info 76 | if user[10] 77 | slackId.push [user[10]] 78 | user_name.push user[0] 79 | callback [user_name, slackId] 80 | 81 | robot.respond /score f(\d\d)( \-\w)?/i, (msg) -> 82 | 83 | ScoreField = scorefield() 84 | 85 | # obtaining the current date to calculate relative_year 86 | today = new Date 87 | mm = today.getMonth() + 1 88 | yyyy = today.getFullYear() 89 | yy = yyyy % 100 90 | if mm < 7 91 | relative_year = yy 92 | else 93 | relative_year = yy + 1 94 | 95 | # whose score is to be shown 96 | batch = msg.match[1] 97 | year = relative_year - batch 98 | 99 | util.info (body) -> 100 | result = [] 101 | result = parse body 102 | member result, year, ([user_name, slackId]) -> 103 | 104 | user_score = [] 105 | 106 | if not msg.match[2]? 107 | user_name = ["```Name", user_name...] 108 | slackId = ["Score", slackId...] 109 | for i in [1..slackId.length - 1] 110 | user_score[i] = ScoreField[slackId[i]] or 0 111 | 112 | user_name = padright user_name 113 | user_score = padleft user_score 114 | 115 | sorted = [] 116 | sorted.push [user_name[0], slackId[0]] 117 | for i in [1..user_name.length - 1] 118 | sorted.push [user_name[i], user_score[i]] 119 | 120 | if sorted.length 121 | sorted.sort (a, b) -> 122 | b[1] - a[1] 123 | 124 | sorted = sorted.map (val) -> "#{val[0]} : #{val[1]}" 125 | response = [] 126 | response += sorted.join '\n' 127 | response += "```" 128 | msg.send response 129 | 130 | else 131 | lastChar = msg.match[2] 132 | if lastChar == ' -p' 133 | graph_type = "pie" 134 | 135 | else if lastChar == ' -b' 136 | graph_type = "bar" 137 | 138 | else 139 | return 140 | 141 | for i in [0..slackId.length - 1] 142 | user_score[i] = ScoreField[slackId[i]] or 0 143 | 144 | chart = { 145 | type: graph_type, 146 | data: { 147 | labels: user_name, 148 | datasets: [{ 149 | label: "Score", 150 | data: user_score 151 | }] 152 | }, 153 | options: { 154 | plugins: { 155 | datalabels: { 156 | display: true, 157 | color: '#fff' 158 | } 159 | } 160 | } 161 | } 162 | data = encodeURIComponent(JSON.stringify(chart)) 163 | text = "Batch#{batch} score" 164 | alt = "Chart showing score of batch#{batch}" 165 | util.graph data, text, alt, (reply) -> 166 | msg.send attachments: JSON.stringify(reply) 167 | -------------------------------------------------------------------------------- /scripts/birthday.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # Script for birthdays!. 3 | # 4 | # Configuration: 5 | # INFO_SPREADSHEET_URL 6 | # 7 | # Commands: 8 | # hubot birthday 9 | # 10 | # Author: 11 | # dev-ritik 12 | 13 | moment = require 'moment' 14 | cron = require('node-cron') 15 | util = require('./util') 16 | 17 | module.exports = (robot) -> 18 | robot.respond /(birthday) (.+)$/i, (msg) -> 19 | query = msg.match[2].toLowerCase() 20 | util.info (body) -> 21 | result = parse body, query 22 | if not result 23 | msg.send "I could not find a user matching `"+query.toString()+"`" 24 | else 25 | msg.send result.length+" user(s) found matching `"+query.toString()+"`" 26 | for user in result 27 | if user[3] == '' 28 | msg.send "#{user[0]} bro add karde yrr!" 29 | else 30 | msg.send "Wish #{user[0]} on #{moment(user[3], 'DD/MM/YYYY').format("MMM Do, YYYY")}!" 31 | 32 | #This will run every day at 00:00:05 33 | cron.schedule '5 0 0 * * *', ()-> 34 | util.info (body) -> 35 | result = parse body, '/' 36 | for member in result 37 | try 38 | dob = moment(member[3], 'DD/MM/YYYY') 39 | today = moment() 40 | if(dob.format("D") == today.format("D") && dob.format("M") == today.format("M") && member[6] > 15000000 ) 41 | robot.send room: 'general', "Happy Birthday #{member[0]}:birthday::birthday:!!" 42 | catch error 43 | robot.send room: 'general', "Help!! These dates are breaking me!" 44 | 45 | parse = (json, query) -> 46 | result = [] 47 | for line in json.toString().split '\n' 48 | y = line.toLowerCase().indexOf query 49 | if y != -1 50 | result.push line.split(',').map Function.prototype.call, String.prototype.trim 51 | if result != "" 52 | result 53 | else 54 | false -------------------------------------------------------------------------------- /scripts/bye.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # GOOD Night 3 | # 4 | # Dependencies: 5 | # None 6 | # 7 | # Configuration: 8 | # None 9 | # 10 | # Commands: 11 | # good night 12 | # 13 | # Author: 14 | # noahhendrix 15 | 16 | ways_to_say_good_night = [ 17 | "Good night, baby.", 18 | "Night hot stuff.", 19 | "Like I'm going to let you get any sleep", 20 | "LOOK...the moon is calling you, SEE...the stars are shining for you, HEAR... my heart saying good night.", 21 | "Sleep tight, don't let the bed bugs bite", 22 | "I'll be here, all alone, waiting" 23 | "So long, and thanks for all the fish.", 24 | "Finally", 25 | "À voir!", 26 | "Don't let the back door hit ya where the good Lord split ya", 27 | "May your feet never fall off and grow back as cactuses", 28 | "TTYL", 29 | "C U L8R", 30 | "Fine, then go!", 31 | "Cheers", 32 | "Uh, I can hear the voices calling me...see ya", 33 | "In a while, crocodile", 34 | "SHOO! SHOO!", 35 | "No more of you.", 36 | "Avada Kedavra", 37 | "Ok. Tata. Byebye", 38 | "Connection: close", 39 | "End Of Line", 40 | "Farewell! God knows when we shall meet again.", 41 | "You are the weakest link - goodbye!", 42 | "आप इस राउंड के कमज़ोर कड़ी हैं. आप जा सकते हैं. नमस्ते", 43 | "If I leave here tomorrow, will you still remember me?", 44 | "Don't leave me here!", 45 | "If you say so.", 46 | "Connection closed by remote host", 47 | "You are fired." 48 | ] 49 | 50 | module.exports = (robot) -> 51 | robot.hear /(good night|goodnight|cya|bye|nighty night)/i, (msg) -> 52 | msg.send msg.random ways_to_say_good_night 53 | -------------------------------------------------------------------------------- /scripts/detailed-score.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # Enlists all people who have given ++ or -- to a particular person 3 | 4 | # Commands: 5 | # bot detailed score name 6 | # bot detailed score name -b (for bar graph) 7 | 8 | # Author: 9 | # Pulkit Karira, aman-singh7 10 | 11 | util = require('./util') 12 | 13 | module.exports = (robot) -> 14 | class Person 15 | constructor: (@name, @plus = 0, @minus = 0) -> 16 | 17 | plusFn: (val) -> @plus += val 18 | minusFn: (val) -> @minus -= val 19 | 20 | isExist = (list, name) -> 21 | idx = 0 22 | for person in list 23 | if person.name == name 24 | index = idx 25 | idx++ 26 | 27 | return index || -1 28 | 29 | 30 | robot.respond /detailed score ([\w\-_]+)( \-\w)?/i, (msg) -> 31 | # whose score is to be shown 32 | if not msg.match[2]? 33 | name = msg.match[1] 34 | name = name.toLowerCase() 35 | plusField = [] 36 | minusField = [] 37 | detailedfield = robot.brain.get("detailedfield") 38 | response = "" 39 | if detailedfield[name]? 40 | if detailedfield[name]["plus"]? 41 | response += "Appreciations\n" 42 | for key , value of detailedfield[name]["plus"] 43 | plusField.push [key , value] 44 | plusField = plusField.map (val) -> "#{val[0]} : #{val[1]}\n" 45 | response += plusField.join '\n' 46 | if detailedfield[name]["minus"]? 47 | response += "\nDepreciations\n" 48 | for key , value of detailedfield[name]["minus"] 49 | minusField.push [key , value] 50 | minusField = minusField.map (val) -> "#{val[0]} : #{val[1]}\n" 51 | response += minusField.join '\n' 52 | else 53 | response += "Sorry ! No such user" 54 | msg.send response 55 | else 56 | if msg.match[2] == ' -b' 57 | name = msg.match[1] 58 | name = name.toLowerCase() 59 | detailedfield = robot.brain.get('detailedfield') 60 | list = [] 61 | if detailedfield[name]? 62 | if detailedfield[name]['plus']? 63 | for key, value of detailedfield[name]['plus'] 64 | list.push new Person key, value, 0 65 | 66 | if detailedfield[name]['minus']? 67 | for key, value of detailedfield[name]['minus'] 68 | idx = isExist list, key 69 | if idx != -1 then list[idx].minusFn(value) 70 | else list.push new Person key, 0, -value 71 | else 72 | msg.send 'No such user found!' 73 | return 74 | 75 | nameList = [] 76 | plus = [] 77 | minus = [] 78 | 79 | list.map (p) -> 80 | nameList.push p.name 81 | plus.push p.plus 82 | minus.push p.minus 83 | 84 | graph = { 85 | type: "bar" 86 | data: { 87 | labels: nameList, 88 | datasets: [ 89 | { 90 | label: "++", 91 | backgroundColor: "rgba(54, 162, 235, 0.5)", 92 | borderColor: "rgba(54, 162, 235)", 93 | borderWidth: 1, 94 | data: plus, 95 | }, 96 | { 97 | label: "--" 98 | backgroundColor: "rgba(255, 99, 132, 0.5)", 99 | borderColor: "rgba(255, 99, 132)" 100 | borderWidth: 1 101 | data: minus 102 | }, 103 | ], 104 | options: { 105 | title: { 106 | display: true, 107 | text: "Detailed Score of #{name}", 108 | }, 109 | plugins: { 110 | datalabels: { 111 | anchor: "center", 112 | align: "center", 113 | color: "#666", 114 | font: { 115 | weight: 'normal', 116 | }, 117 | }, 118 | }, 119 | }, 120 | } 121 | } 122 | chart = encodeURIComponent(JSON.stringify(graph)) 123 | text = "Detailed Score of #{name}" 124 | util.graph chart, text, "Graph Showing Detailed Score", (reply) -> 125 | msg.send attachments: JSON.stringify(reply) 126 | 127 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /scripts/events.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # Event system related utilities 3 | # 4 | # Commands: 5 | # hubot fake event - Triggers the event for debugging reasons 6 | # 7 | # Events: 8 | # debug - {user: } 9 | 10 | util = require 'util' 11 | 12 | module.exports = (robot) -> 13 | 14 | robot.respond /FAKE EVENT (.*)/i, (msg) -> 15 | msg.send "fake event '#{msg.match[1]}' triggered" 16 | robot.emit msg.match[1], {user: msg.message.user} 17 | 18 | robot.on 'debug', (event) -> 19 | robot.send event.user, util.inspect event -------------------------------------------------------------------------------- /scripts/fb-feed.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # Fetch random posts from any facebook page 3 | # 4 | # Dependencies: 5 | # https 6 | # 7 | # Configuration: 8 | # FB_APP_ACCESS_TOKEN 9 | # 10 | # Commands: 11 | # hubot fb feed 12 | # 13 | # Author: 14 | # csoni111 15 | 16 | https = require 'https' 17 | access_token = "access_token=#{process.env.FB_APP_ACCESS_TOKEN}" 18 | options = {host: 'graph.facebook.com'} 19 | 20 | module.exports = (robot) -> 21 | 22 | robot.on 'send:fb-feed', (page_name) -> 23 | getRandomPost page_name, (data) -> 24 | robot.send room: 'general', attachments: [data] 25 | 26 | robot.respond /fb feed (.+)$/i, (msg) -> 27 | if access_token.includes 'undefined' 28 | msg.send "Looks like `FB_APP_ACCESS_TOKEN` is missing :thinking_face:" 29 | else 30 | page_name = msg.match[1].trim() 31 | getRandomPost page_name, (data) -> 32 | msg.send( 33 | attachments: [ 34 | data 35 | ] 36 | ) 37 | 38 | 39 | #returns a random post from a given fb page 40 | getRandomPost = (page_name, callback) -> 41 | options.path = "/#{page_name}/feed?#{access_token}" 42 | https.get options, (res) -> 43 | data = '' 44 | res.on 'data', (chunk) -> 45 | data += chunk.toString() 46 | res.on 'end', () -> 47 | data = JSON.parse(data).data 48 | post = data[Math.floor data.length * Math.random()] 49 | fetchPageDetails page_name, (page) -> 50 | output = 51 | "fallback": post.message 52 | "color": "#fc554d" 53 | "author_name": page.name 54 | "author_link": page.link 55 | "author_icon": page.picture.data.url 56 | "title": post.message 57 | "title_link": "https://facebook.com/#{post.id}" 58 | "ts": new Date(post.created_time).getTime() / 1000 59 | fetchPostAttachments post.id, (likes, image_url) -> 60 | output.footer = "#{likes} Likes" 61 | if image_url? 62 | output.image_url = image_url 63 | callback(output) 64 | 65 | 66 | #returns page details 67 | fetchPageDetails = (page_name, callback) -> 68 | options.path = "/#{page_name}?#{access_token}&fields=name,link,about,picture{url}" 69 | https.get options, (res) -> 70 | data = '' 71 | res.on 'data', (chunk) -> 72 | data += chunk.toString() 73 | res.on 'end', () -> 74 | data = JSON.parse(data) 75 | callback data 76 | 77 | 78 | fetchPostAttachments = (post_id, callback) -> 79 | options.path = "/#{post_id}/?#{access_token}&fields=attachments{media},likes.limit(0).summary(true)" 80 | https.get options, (res) -> 81 | post_data = '' 82 | res.on 'data', (chunk) -> 83 | post_data += chunk.toString() 84 | res.on 'end', () -> 85 | post_data = JSON.parse(post_data) 86 | likes = post_data.likes.summary.total_count 87 | if post_data.attachments? 88 | image_url = post_data.attachments.data[0].media.image.src 89 | callback likes, image_url -------------------------------------------------------------------------------- /scripts/fblikes.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # Returns the number of likes on MDG page on FB 3 | # 4 | # Dependencies: 5 | # None 6 | # 7 | # Configuration: 8 | # FB_APP_ACCESS_TOKEN 9 | # 10 | # Commands: 11 | # hubot fb likes 12 | # 13 | # Author: 14 | # abhshkdz, csoni111 15 | 16 | module.exports = (robot) -> 17 | 18 | robot.respond /fb(\s*)likes/i, (msg) -> 19 | https = require 'https' 20 | https.get {host: 'graph.facebook.com', path: "/mdgiitr?access_token=#{process.env.FB_APP_ACCESS_TOKEN}&fields=fan_count"}, (res) -> 21 | data = '' 22 | res.on 'data', (chunk) -> 23 | data += chunk.toString() 24 | res.on 'end', () -> 25 | console.log data 26 | data = JSON.parse(data) 27 | msg.send "#{data['fan_count']}" -------------------------------------------------------------------------------- /scripts/google-images.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # A way to interact with the Google Images API. 3 | # 4 | # Configuration 5 | # HUBOT_GOOGLE_CSE_KEY - Your Google developer API key 6 | # HUBOT_GOOGLE_CSE_ID - The ID of your Custom Search Engine 7 | # HUBOT_MUSTACHIFY_URL - Optional. Allow you to use your own mustachify instance. 8 | # HUBOT_GOOGLE_IMAGES_HEAR - Optional. If set, bot will respond to any line that begins with "image me" or "animate me" without needing to address the bot directly 9 | # HUBOT_GOOGLE_SAFE_SEARCH - Optional. Search safety level. 10 | # HUBOT_GOOGLE_IMAGES_FALLBACK - The URL to use when API fails. `{q}` will be replaced with the query string. 11 | # 12 | # Commands: 13 | # hubot image me - The Original. Queries Google Images for and returns a random top result. 14 | # hubot animate me - The same thing as `image me`, except adds a few parameters to try to return an animated GIF instead. 15 | # hubot mustache me - Adds a mustache to the specified URL or query result. 16 | 17 | module.exports = (robot) -> 18 | 19 | robot.respond /(image|img)( me)? (.+)/i, (msg) -> 20 | imageMe msg, msg.match[3], (url) -> 21 | msg.send url 22 | 23 | robot.respond /animate( me)? (.+)/i, (msg) -> 24 | imageMe msg, msg.match[2], true, (url) -> 25 | msg.send url 26 | 27 | # pro feature, not added to docs since you can't conditionally document commands 28 | if process.env.HUBOT_GOOGLE_IMAGES_HEAR? 29 | robot.hear /^(image|img) me (.+)/i, (msg) -> 30 | imageMe msg, msg.match[2], (url) -> 31 | msg.send url 32 | 33 | robot.hear /^animate me (.+)/i, (msg) -> 34 | imageMe msg, msg.match[1], true, (url) -> 35 | msg.send url 36 | 37 | robot.respond /(?:mo?u)?sta(?:s|c)h(?:e|ify)?(?: me)? (.+)/i, (msg) -> 38 | if not process.env.HUBOT_MUSTACHIFY_URL? 39 | msg.send "Sorry, the Mustachify server is not configured." 40 | , "http://i.imgur.com/BXbGJ1N.png" 41 | return 42 | mustacheBaseUrl = 43 | process.env.HUBOT_MUSTACHIFY_URL?.replace(/\/$/, '') 44 | mustachify = "#{mustacheBaseUrl}/rand?src=" 45 | imagery = msg.match[1] 46 | 47 | if imagery.match /^https?:\/\//i 48 | encodedUrl = encodeURIComponent imagery 49 | msg.send "#{mustachify}#{encodedUrl}" 50 | else 51 | imageMe msg, imagery, false, true, (url) -> 52 | encodedUrl = encodeURIComponent url 53 | msg.send "#{mustachify}#{encodedUrl}" 54 | 55 | imageMe = (msg, query, animated, faces, cb) -> 56 | cb = animated if typeof animated == 'function' 57 | cb = faces if typeof faces == 'function' 58 | googleCseId = process.env.HUBOT_GOOGLE_CSE_ID 59 | if googleCseId 60 | # Using Google Custom Search API 61 | googleApiKey = process.env.HUBOT_GOOGLE_CSE_KEY 62 | if !googleApiKey 63 | msg.robot.logger.error "Missing environment variable HUBOT_GOOGLE_CSE_KEY" 64 | msg.send "Missing server environment variable HUBOT_GOOGLE_CSE_KEY." 65 | return 66 | q = 67 | q: query, 68 | searchType:'image', 69 | safe: process.env.HUBOT_GOOGLE_SAFE_SEARCH || 'high', 70 | fields:'items(link)', 71 | cx: googleCseId, 72 | key: googleApiKey 73 | if animated is true 74 | q.fileType = 'gif' 75 | q.hq = 'animated' 76 | q.tbs = 'itp:animated' 77 | if faces is true 78 | q.imgType = 'face' 79 | url = 'https://www.googleapis.com/customsearch/v1' 80 | msg.http(url) 81 | .query(q) 82 | .get() (err, res, body) -> 83 | if err 84 | if res.statusCode is 403 85 | msg.send "Daily image quota exceeded, using alternate source." 86 | deprecatedImage(msg, query, animated, faces, cb) 87 | else 88 | msg.send "Encountered an error :( #{err}" 89 | return 90 | if res.statusCode isnt 200 91 | msg.send "Bad HTTP response :( #{res.statusCode}" 92 | return 93 | response = JSON.parse(body) 94 | if response?.items 95 | image = msg.random response.items 96 | cb ensureResult(image.link, animated) 97 | else 98 | msg.send "Oops. I had trouble searching '#{query}'. Try later." 99 | ((error) -> 100 | msg.robot.logger.error error.message 101 | msg.robot.logger 102 | .error "(see #{error.extendedHelp})" if error.extendedHelp 103 | ) error for error in response.error.errors if response.error?.errors 104 | else 105 | msg.send "Google Image Search API is no longer available. " + 106 | "Please [setup up Custom Search Engine API](https://github.com/hubot-scripts/hubot-google-images#cse-setup-details)." 107 | deprecatedImage(msg, query, animated, faces, cb) 108 | 109 | deprecatedImage = (msg, query, animated, faces, cb) -> 110 | # Show a fallback image 111 | imgUrl = process.env.HUBOT_GOOGLE_IMAGES_FALLBACK || 112 | 'http://i.imgur.com/CzFTOkI.png' 113 | imgUrl = imgUrl.replace(/\{q\}/, encodeURIComponent(query)) 114 | cb ensureResult(imgUrl, animated) 115 | 116 | # Forces giphy result to use animated version 117 | ensureResult = (url, animated) -> 118 | if animated is true 119 | ensureImageExtension url.replace( 120 | /(giphy\.com\/.*)\/.+_s.gif$/, 121 | '$1/giphy.gif') 122 | else 123 | ensureImageExtension url 124 | 125 | # Forces the URL look like an image URL by adding `#.png` 126 | ensureImageExtension = (url) -> 127 | if /(png|jpe?g|gif)$/i.test(url) 128 | url 129 | else 130 | "#{url}#.png" -------------------------------------------------------------------------------- /scripts/help.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # Generates help commands for Hubot. 3 | # 4 | # Commands: 5 | # hubot help - Displays all of the help commands that Hubot knows about. 6 | # hubot help - Displays all help commands that match . 7 | # 8 | # URLS: 9 | # /hubot/help 10 | # 11 | # Notes: 12 | # These commands are grabbed from comment blocks at the top of each file. 13 | 14 | helpContents = (name, commands) -> 15 | 16 | """ 17 | 18 | 19 | 20 | 21 | #{name} Help 22 | 45 | 46 | 47 |

#{name} Help

48 |
49 | #{commands} 50 |
51 | 52 | 53 | """ 54 | 55 | module.exports = (robot) -> 56 | robot.respond /help\s*(.*)?$/i, (msg) -> 57 | cmds = robot.helpCommands() 58 | filter = msg.match[1] 59 | 60 | if filter 61 | cmds = cmds.filter (cmd) -> 62 | cmd.match new RegExp(filter, 'i') 63 | if cmds.length == 0 64 | msg.send "No available commands match #{filter}" 65 | return 66 | 67 | prefix = robot.alias or robot.name 68 | cmds = cmds.map (cmd) -> 69 | cmd = cmd.replace /hubot/ig, robot.name 70 | cmd.replace new RegExp("^#{robot.name}"), prefix 71 | 72 | emit = cmds.join "\n" 73 | 74 | msg.send emit 75 | 76 | robot.router.get "/#{robot.name}/help", (req, res) -> 77 | cmds = robot.helpCommands().map (cmd) -> 78 | cmd.replace(/&/g,'&').replace(//g,'>') 79 | 80 | emit = "

#{cmds.join '

'}

" 81 | 82 | emit = emit.replace /hubot/ig, "#{robot.name}" 83 | 84 | res.setHeader 'content-type', 'text/html' 85 | res.end helpContents robot.name, emit 86 | -------------------------------------------------------------------------------- /scripts/httpd.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # A simple interaction with the built in HTTP Daemon 3 | # 4 | # Dependencies: 5 | # None 6 | # 7 | # Configuration: 8 | # None 9 | # 10 | # Commands: 11 | # None 12 | # 13 | # URLS: 14 | # /hubot/version 15 | # /hubot/ping 16 | # /hubot/time 17 | # /hubot/info 18 | # /hubot/ip 19 | 20 | 21 | spawn = require('child_process').spawn 22 | util = require('./util') 23 | 24 | module.exports = (robot) -> 25 | 26 | robot.router.get "/hubot/version", (req, res) -> 27 | res.end robot.version 28 | 29 | robot.router.post "/hubot/ping", (req, res) -> 30 | res.end "PONG" 31 | 32 | robot.router.get "/hubot/time", (req, res) -> 33 | res.end "Server time is: #{new Date()}" 34 | 35 | robot.router.get "/hubot/info", (req, res) -> 36 | child = spawn('/bin/sh', ['-c', "echo I\\'m $LOGNAME@$(hostname):$(pwd) \\($(git rev-parse HEAD)\\)"]) 37 | 38 | child.stdout.on 'data', (data) -> 39 | res.end "#{data.toString().trim()} running node #{process.version} [pid: #{process.pid}]" 40 | child.stdin.end() 41 | 42 | robot.router.get "/hubot/ip", (req, res) -> 43 | robot.http('http://ifconfig.me/ip').get() (err, r, body) -> 44 | res.end body 45 | 46 | robot.router.post "/hubot/slack", (request, response) -> 47 | check = process.env.HUBOT_ENV_AUTH_TOKEN 48 | 49 | if request.headers.authorization == check 50 | data = request.body 51 | responseobj = {} 52 | if data.queryResult.parameters.name == "" 53 | console.log(data.queryResult.parameters.any) 54 | robot.send room: 'general', "Announcement : '#{data.queryResult.parameters.any}'" 55 | else 56 | query = data.queryResult.parameters.name.toLowerCase() 57 | util.info (body) -> 58 | result = parse body, query 59 | console.log(result.length) 60 | if result.length==0 61 | responseobj = { 62 | "fulfillmentText": "This is a text response", 63 | "fulfillmentMessages": [ 64 | ], 65 | "source": " ", 66 | "payload": { 67 | "google": { 68 | "expectUserResponse": true, 69 | "richResponse": { 70 | "items": [ 71 | { 72 | "simpleResponse": { 73 | "textToSpeech": "No user found" 74 | } 75 | }, 76 | ] 77 | } 78 | }, 79 | } 80 | } 81 | else 82 | if result.length==1 83 | responseobj = { 84 | "fulfillmentText": "This is a text response", 85 | "fulfillmentMessages": [ 86 | ], 87 | "source": " ", 88 | "payload": { 89 | "google": { 90 | "expectUserResponse": true, 91 | "richResponse": { 92 | "items": [ 93 | { 94 | "simpleResponse": { 95 | "textToSpeech": "Here it is" 96 | } 97 | }, 98 | { 99 | "basicCard":{ 100 | "formattedText": "Github : "+result[0][8]+"\n \nMobile : "+result[0][1]+"\n \nEmail : "+result[0][2]+"\n \n"+result[0][4]+" "+result[0][5]+" ("+result[0][6]+")", 101 | "title":result[0][0] 102 | } 103 | } 104 | ] 105 | } 106 | }, 107 | } 108 | } 109 | else 110 | basicCardArray = [] 111 | for user in result 112 | basicCardArray1 = { 113 | "description":"Github : "+user[8]+"\nMobile : "+user[1]+"\nEmail : "+user[2]+"\n"+user[4]+" "+user[5]+" ("+user[6]+")" , 114 | "title":user[0], 115 | "openUrlAction": { 116 | "url": "https://github.com/"+user[8] 117 | }, 118 | 119 | } 120 | basicCardArray.push basicCardArray1 121 | responseobj = { 122 | "fulfillmentText": "This is a text response", 123 | "fulfillmentMessages": [ 124 | ], 125 | "source": " ", 126 | "payload": { 127 | "google": { 128 | "expectUserResponse": true, 129 | "richResponse": { 130 | "items": [ 131 | { 132 | "simpleResponse": { 133 | "textToSpeech": "Here it is" 134 | } 135 | }, 136 | { 137 | "carouselBrowse": { 138 | "items" : basicCardArray 139 | } 140 | }, 141 | ] 142 | } 143 | }, 144 | } 145 | } 146 | console.log(responseobj) 147 | response.writeHead 200, 148 | 'Content-Type': 'application/json' 149 | response.end JSON.stringify(responseobj) 150 | else 151 | console.log("unauthorized request") 152 | response.writeHead 404 153 | 154 | 155 | parse = (json, query) -> 156 | result = [] 157 | for line in json.toString().split '\n' 158 | y = line.toLowerCase().indexOf query 159 | if y != -1 160 | result.push line.split(',').map Function.prototype.call, String.prototype.trim 161 | if result != "" 162 | result 163 | else 164 | false -------------------------------------------------------------------------------- /scripts/idlecheck.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # Checks if there are no messages for a fixed time and sends random posts from ToDJ. 3 | # 4 | # Dependencies: 5 | # None 6 | # 7 | # Configuration: 8 | # IDLE_TIME_DURATION_HOURS 9 | # 10 | # Commands: 11 | # None 12 | # 13 | # Author: 14 | # csoni111 15 | 16 | set_time = parseFloat process.env.IDLE_TIME_DURATION_HOURS or "0" 17 | i=0 18 | msec_per_hour = 1000*60*60 19 | last_msg_time = null 20 | idle_msgs = [ 21 | 'Why so silent?', 22 | 'Is anyone alive :expressionless:', 23 | 'Looks like I am all alone!', 24 | ] 25 | 26 | module.exports = (robot) -> 27 | if set_time > 0 28 | robot.hear /.+/i, (msg) -> 29 | last_msg_time = new Date() 30 | if i 31 | clearInterval i 32 | i=setInterval () -> 33 | checkAndSendMsg() 34 | , msec_per_hour*set_time 35 | 36 | checkAndSendMsg = -> 37 | idle_time_hour = ((new Date()).getTime() - last_msg_time.getTime())/msec_per_hour 38 | if idle_time_hour > set_time 39 | robot.emit 'send:fb-feed', 'dardanaak' -------------------------------------------------------------------------------- /scripts/info.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # gets MDG member's info from google doc 3 | # Type a partial name to get all matches 4 | # 5 | # Configuration: 6 | # INFO_SPREADSHEET_URL 7 | # 8 | # Commands: 9 | # hubot info - Get information about a person 10 | 11 | moment = require 'moment' 12 | util = require('./util') 13 | 14 | module.exports = (robot) -> 15 | robot.respond /(info) (.+)$/i, (msg) -> 16 | query = msg.match[2].toLowerCase() 17 | util.info (body) -> 18 | result = parse body, query 19 | if not result 20 | msg.send "I could not find a user matching `" + query.toString() + "`" 21 | else 22 | msg.send result.length + " user(s) found matching `" + query.toString() + "`" 23 | for user in result 24 | msg.send( 25 | attachments: [ 26 | { 27 | "fallback": user.join ' \t ' 28 | "color": randomColor() 29 | "title": user[0] 30 | "title_link": "https://facebook.com/#{user[9]}" 31 | "text": "Github: "+ 32 | "\nRoom no: #{user[7]}" 33 | "fields": [ 34 | "title": "Mobile" 35 | "value": "" 36 | "short": true 37 | , 38 | "title": "Email" 39 | "value": "" 40 | "short": true 41 | , 42 | ] 43 | "footer": "#{user[4]} #{user[5]} (#{user[6]})" 44 | "ts": moment(user[3], 'DD/MM/YYYY').format("X") 45 | } 46 | 47 | ] 48 | ) 49 | 50 | 51 | parse = (json, query) -> 52 | result = [] 53 | for line in json.toString().split '\n' 54 | y = line.toLowerCase().indexOf query 55 | if y != -1 56 | result.push line.split(',').map Function.prototype.call, String.prototype.trim 57 | if result != "" 58 | result 59 | else 60 | false 61 | 62 | randomColor = () -> 63 | return '#'+(0x1000000+(Math.random())*0xffffff).toString(16).substr(1,6) 64 | 65 | 66 | -------------------------------------------------------------------------------- /scripts/keys.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # partychat like chat-score/leaderboard script built at 'SDSLabs' 3 | # we developed this to use in our 'Slack' team instance 4 | # 5 | # Commands: 6 | # listen for * has/have keys in chat text and displays users with the keys/updates the user having keys 7 | # bot who has keys : returns current user having lab keys 8 | # bot i have keys : set's the key-holder to the user who posted 9 | # bot i dont have keys : unsets the user who posted from key-holders 10 | # bot xyz has keys : sets xyz as the holder of keys 11 | # 12 | # Examples: 13 | # :bot who has keys 14 | # :bot i have keys 15 | # :bot i dont have keys 16 | # :bot who has keys 17 | # :bot ravi has keys 18 | # 19 | # Author: 20 | # Punit Dhoot (@pdhoot) 21 | # Developer at SDSLabs (@sdslabs) 22 | 23 | module.exports = (robot)-> 24 | 25 | key = ()-> 26 | Key = robot.brain.get("key") or [] 27 | robot.brain.set("key" ,Key) 28 | Key 29 | 30 | 31 | robot.respond /i have (a key|the key|key|keys) of (.+)/i, (msg)-> 32 | name = msg.message.user.name 33 | ownerName = msg.match[2] 34 | user = robot.brain.userForName name 35 | try 36 | kh = key() 37 | if typeof user is 'object' 38 | msg.send "Okay #{name} has keys" 39 | kh.push holder:name,owner:ownerName 40 | robot.brain.set("key",kh) 41 | catch e 42 | console.log e 43 | 44 | 45 | 46 | robot.respond /i (don\'t|dont|do not) (has|have) (the key|key|keys|a key)/i , (msg)-> 47 | name = msg.message.user.name 48 | kh = key() 49 | check =0 50 | for x in kh 51 | if x.holder is name 52 | index=kh.indexOf(x) 53 | owner = x.owner 54 | kh.splice(index,1) 55 | check=1 56 | msg.send "Okay #{name} doesn't have keys.Then, Who got the keys of #{owner}?" 57 | if typeof user is 'object' 58 | if check is 0 59 | msg.send "Yes , i know buddy" 60 | robot.brain.set("key",kh) 61 | 62 | 63 | robot.respond /(.+) (has|have) (the key|key|keys|a key) of (.+)/i , (msg)-> 64 | othername = msg.match[1] 65 | ownerName = msg.match[4] 66 | name = msg.message.user.name 67 | k = key() 68 | if ownerName is "" 69 | msg.send "okay, but whose key" 70 | else 71 | unless othername in ["who", "who all","Who", "Who all" , "i" , "I" , "i don't" , "i dont" , "i do not" , "I don't" , "I dont" , "I do not"] 72 | if othername is 'you' 73 | msg.send "How am I supposed to take those keys? #{name} is a liar!" 74 | else if othername is robot.name 75 | msg.send "How am I supposed to take those keys? #{name} is a liar!" 76 | else 77 | users = robot.brain.usersForFuzzyName othername 78 | userso = robot.brain.usersForFuzzyName ownerName 79 | if users.length is 1 80 | if userso.length is 1 81 | k.push holder:users[0].name,owner:userso[0].name 82 | robot.brain.set("key", k) 83 | msg.send "Okay, so now the key of #{userso[0].name} are with #{users[0].name}" 84 | else if users.length > 1 85 | msg.send getAmbiguousUserText users 86 | else 87 | msg.send "#{ownerName}? Never heard of 'em" 88 | else if users.length > 1 89 | msg.send getAmbiguousUserText users 90 | else 91 | msg.send "#{othername}? Never heard of 'em" 92 | 93 | robot.respond /(i|I) (have given|gave|had given) (the key|key|keys|a key|the keys) to (.+)/i , (msg)-> 94 | othername = msg.match[4] 95 | name = msg.message.user.name 96 | k = key() 97 | if othername is 'you' 98 | msg.send "That's utter lies! How can you blame a bot to have the keys?" 99 | else if othername is robot.name 100 | msg.send "That's utter lies! How can you blame a bot to have the keys?" 101 | else 102 | users = robot.brain.usersForFuzzyName othername 103 | if users is null 104 | msg.send "I don't know anyone by the name #{othername}" 105 | else 106 | check =0; 107 | for x in k 108 | if x.holder is name 109 | x.holder = users[0].name 110 | msg.send "Okay, so now the keys of #{x.owner} are with #{users[0].name}" 111 | check=1 112 | if check is 0 113 | msg.send "Liar, you don't have the keys" 114 | robot.brain.set("key",k) 115 | 116 | robot.respond /(who|who all) (has|have) (the key|key|keys|a key)/i , (msg)-> 117 | try 118 | kh = key() 119 | if kh is [] 120 | msg.send "Ah! Nobody informed me about the keys. Don't hold me responsible for this :expressionless:" 121 | else 122 | list = [] 123 | msgText=[] 124 | for x in kh 125 | list.push "#{x.owner}'s key are with #{x.holder}" 126 | msgText += list.join '\n' 127 | msgText+="" 128 | if msgText is "" 129 | msg.send "Ah! Nobody informed me about the keys. Don't hold me responsible for this :expressionless:" 130 | else 131 | msg.send msgText 132 | robot.brain.set("key" ,kh) 133 | catch e 134 | console.log e 135 | 136 | robot.respond /who (has|have) (.+'s) (key|keys)/i , (msg)-> 137 | ownerName = msg.match[2]; 138 | ownerName =ownerName.substr 0,ownerName.length-2 139 | users = robot.brain.usersForFuzzyName ownerName 140 | s = "" 141 | try 142 | kh = key() 143 | if users.length is 1 144 | s="" 145 | for x in kh 146 | if x.owner is users[0].name 147 | s = "#{x.owner} keys are with #{x.holder}" 148 | if s is "" 149 | msg.send "Ah! Nobody informed me about the keys." 150 | else 151 | msg.send s 152 | else if users.length > 1 153 | msg.send getAmbiguousUserText users 154 | else 155 | msg.send "#{ownerName}? Never heard of 'em" 156 | robot.brain.set("key" ,kh) 157 | catch e 158 | console.log e 159 | 160 | 161 | 162 | 163 | getAmbiguousUserText = (users) -> 164 | "Be more specific, I know #{users.length} people named like that: #{(user.name for user in users).join(", ")}" 165 | 166 | -------------------------------------------------------------------------------- /scripts/leaderboard.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # Script for maininting scores of different users. 3 | # 4 | # Commands: 5 | # name++ or name-- : Adds/subtracts 1 point to/from user's score 6 | # hubot score name : Shows current score of the user 7 | 8 | util = require('./util') 9 | 10 | module.exports = (robot) -> 11 | 12 | robot.listenerMiddleware (context, next, done) -> 13 | 14 | try 15 | # Check if it was called in a room. 16 | if get_channel(context.response) is '#DM' 17 | context.response.reply "This won't work here" 18 | # Skipping sending the message to general channel. 19 | # robot.send room: 'general', "@#{context.response.message.user.name} pls dont DM me. Talk here in public!" 20 | # Bypass executing the listener callback 21 | done() 22 | else 23 | next (done) 24 | catch err 25 | robot.emit('error', err, context.response) 26 | 27 | get_channel = (response) -> 28 | if response.message.room == response.message.user.name 29 | "@#{response.message.room}" 30 | else 31 | isDM = response.message.room[0] is 'D' 32 | messageType = 'unknown' 33 | if isDM 34 | messageType = 'DM' 35 | "##{messageType}" 36 | 37 | # returns list of skipped words 38 | skippedlist = () -> 39 | List = robot.brain.get("skippedlist") or [] 40 | robot.brain.set("skippedlist", List) 41 | List 42 | 43 | # return object to store data for all keywords 44 | # using this, stores the data in brain's "scorefield" key 45 | scorefield = () -> 46 | Field = robot.brain.get("scorefield") or {} 47 | robot.brain.set("scorefield", Field) 48 | Field 49 | 50 | detailedfield = () -> 51 | Field = robot.brain.get("detailedfield") or {} 52 | robot.brain.set("detailedfield", Field) 53 | Field 54 | # returns last score 55 | lastScore = (name, field) -> 56 | name = name.toLowerCase() 57 | lastscore = field[name] or 0 58 | lastscore 59 | 60 | #returns appreciation field associated to a single user 61 | userFieldMinus = (user) -> 62 | Detailedfield = detailedfield() 63 | Detailedfield[user] = Detailedfield[user] or {} 64 | Detailedfield[user]["minus"] = Detailedfield[user]["minus"] or {} 65 | Detailedfield[user]["minus"] 66 | 67 | #returns depreciation field associated to a single user 68 | userFieldPlus = (user) -> 69 | Detailedfield = detailedfield() 70 | Detailedfield[user] = Detailedfield[user] or {} 71 | Detailedfield[user]["plus"] = Detailedfield[user]["plus"] or {} 72 | Detailedfield[user]["plus"] 73 | 74 | #updates detailed field 75 | updateDetailedScore = (field, sendername, fieldtype) -> 76 | if(fieldtype == "plus") 77 | field[sendername] = field[sendername] + 1 or 1 78 | else 79 | field[sendername] = field[sendername] + 1 or 1 80 | 81 | # updates score according to ++/-- 82 | updateScore = (word, field, username, slackIds) -> 83 | posRegex = /\+\+/ 84 | negRegex = /\-\-/ 85 | 86 | # if there is to be `plus` in score 87 | if word.indexOf("++") >= 0 88 | name = word.replace posRegex, "" 89 | if username.toLowerCase() == name.toLowerCase() 90 | response = "-1" 91 | else if name in slackIds 92 | field[name.toLowerCase()] = lastScore(name, field) + 1 93 | userfield = userFieldPlus(name.toLowerCase()) 94 | updateDetailedScore(userfield, username, "plus") 95 | response = responses[Math.floor(Math.random() * 7)] 96 | else 97 | response = "0" 98 | 99 | # if there is to be `minus` in score 100 | else if word.indexOf("--") >= 0 101 | name = word.replace negRegex, "" 102 | if username.toLowerCase() == name.toLowerCase() 103 | response = "-1" 104 | else if name in slackIds 105 | field[name.toLowerCase()] = lastScore(name, field) - 1 106 | userfield = userFieldMinus(name.toLowerCase()) 107 | updateDetailedScore(userfield, username, "minus") 108 | response = "Ouch!" 109 | else 110 | response = "0" 111 | 112 | newscore = field[name.toLowerCase()] 113 | 114 | # returns 'name' and 'newscore' and 'response' 115 | New: newscore 116 | Name: name 117 | Response: response 118 | 119 | getSlackIds = (callback) -> 120 | util.info (body) -> 121 | slackIds = [] 122 | for user in parse body 123 | if user.length >= 13 and user[10]? 124 | slackIds.push user[10] 125 | callback slackIds 126 | 127 | parse = (json) -> 128 | result = [] 129 | for line in json.toString().split '\n' 130 | result.push line.split(',').map Function.prototype.call, 131 | String.prototype.trim 132 | if result != "" 133 | result 134 | else 135 | false 136 | 137 | # listen for any [word](++/--) in chat and react/update score 138 | robot.hear /[a-zA-Z0-9\-_]+(\-\-|\+\+)/gi, (msg) -> 139 | 140 | # message for score update that bot will return 141 | oldmsg = msg.message.text 142 | 143 | # data-store object 144 | ScoreField = scorefield() 145 | 146 | # skipped word list 147 | SkippedList = skippedlist() 148 | 149 | # index keeping an eye on position, where next replace will be 150 | start = 0 151 | end = 0 152 | 153 | # reply only when there exist altest one testword which 154 | # is neither skipped nor its length greater than 30 155 | reply = false 156 | finalnewmsg = "" 157 | 158 | getSlackIds (slackIds) -> 159 | # for each ++/-- 160 | for i in [0...msg.match.length] 161 | testword = msg.match[i] 162 | 163 | end = start + testword.length 164 | 165 | # check if testword is already skipped or it is too lengthy 166 | if testword.slice(0, -2) in SkippedList or testword.length > 30 167 | newmsg = "" 168 | 169 | else 170 | reply = true 171 | # updates Scoring for words, accordingly and returns result string 172 | result = updateScore(testword, ScoreField, msg.message.user.name, slackIds) 173 | 174 | # generates response message for reply 175 | if result.Response == "-1" 176 | newmsg = "#{testword} [Sorry, You can't give ++ or -- to yourself.]" 177 | else if result.Response == "0" 178 | newmsg = "#{result.Name}? Never heard of 'em " 179 | else 180 | newmsg = "#{testword} [#{result.Response} You're now at #{result.New}] " 181 | 182 | oldmsg = oldmsg.substr(0, start) + oldmsg.substr(end + 1) 183 | finalnewmsg += newmsg + "\n" 184 | 185 | if reply 186 | # reply with updated message 187 | msg.send "#{finalnewmsg}" 188 | 189 | 190 | # response for score status of any 191 | robot.respond /score ([\w\-_]+)/i, (msg) -> 192 | 193 | # we do not want to reply in case of batch score is requested 194 | fxx = /f\d\d/i 195 | if fxx.exec(msg.match[0]) != null 196 | then return 197 | 198 | # data-store object 199 | ScoreField = scorefield() 200 | 201 | # whose score is to be shown 202 | name = msg.match[1] 203 | name = name.toLowerCase() 204 | 205 | # If the key exist 206 | if ScoreField[name]? 207 | # current score for keyword 208 | currentscore = ScoreField[name] 209 | msg.send "#{name} : #{currentscore}" 210 | 211 | else 212 | msg.send "#{name}? Never heard of 'em" 213 | 214 | robot.on 'plusplus', (event) -> 215 | ScoreField = scorefield() 216 | result = updateScore("#{event.username}++", ScoreField, "MostWordsBot", [event.username]) 217 | newmsg = "#{event.username}++ [#{result.Response} You're now at #{result.New}]" 218 | robot.send room: 'general', newmsg 219 | 220 | responses = [ 221 | 'Flamboyant!' 222 | 'Baroque!' 223 | 'Impressive!' 224 | 'Lustrous!' 225 | 'Splashy!' 226 | 'Superb!' 227 | 'Splendid!' 228 | ] 229 | -------------------------------------------------------------------------------- /scripts/maps.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # Interacts with the Google Maps API. 3 | # 4 | # Commands: 5 | # hubot map me - Returns a map view of the area returned by `query`. 6 | 7 | module.exports = (robot) -> 8 | 9 | robot.respond /(?:(satellite|terrain|hybrid)[- ])?map me (.+)/i, (msg) -> 10 | mapType = msg.match[1] or "roadmap" 11 | location = msg.match[2] 12 | mapUrl = "http://maps.google.com/maps/api/staticmap?markers=" + 13 | encodeURIComponent(location) + 14 | "&size=400x400&maptype=" + 15 | mapType + 16 | "&sensor=false" + 17 | "&format=png" # So campfire knows it's an image 18 | url = "http://maps.google.com/maps?q=" + 19 | encodeURIComponent(location) + 20 | "&hl=en&sll=37.0625,-95.677068&sspn=73.579623,100.371094&vpsrc=0&hnear=" + 21 | encodeURIComponent(location) + 22 | "&t=m&z=11" 23 | 24 | msg.send mapUrl 25 | msg.send url 26 | 27 | -------------------------------------------------------------------------------- /scripts/middleware.coffee: -------------------------------------------------------------------------------- 1 | # Dependencies: 2 | # None 3 | # 4 | # Configuration: 5 | # https://github.com/github/hubot/blob/master/docs/scripting.md#middleware 6 | # 7 | # Author: 8 | # csoni111 9 | 10 | end = (msg, done) -> 11 | # Don't process this message further. 12 | msg.finish() 13 | # Don't process further middleware. 14 | done() 15 | 16 | module.exports = (robot) -> 17 | robot.receiveMiddleware (context, next, done) -> 18 | msg = context.response.message 19 | # Check if message was sent by someone other than SlackBot 20 | if msg.user.id != 'USLACKBOT' 21 | # Check if this message was sent in a private channel 22 | if msg.message?.channel?.is_private or msg.rawMessage?.channel?.is_private 23 | # Skipping sending the message to general channel. 24 | # robot.send room: 'general', "@#{msg.user.name} stop sending me messages in private channel. Talk here in public!" 25 | end(msg, done) 26 | # or a DM 27 | else if msg.rawMessage?.channel?.is_im 28 | # Skipping sending the message to general channel. 29 | # robot.send room: 'general', "@#{msg.user.name} pls dont DM me. Talk here in public!" 30 | end(msg, done) 31 | else 32 | next(done) 33 | else 34 | end(msg, done) -------------------------------------------------------------------------------- /scripts/mirror.coffee: -------------------------------------------------------------------------------- 1 | ###################################################################################### 2 | # Description: 3 | # cron jobs for managing the MDG Mirror Spreadsheet 4 | # 5 | # 1) Adds new week column in the sheet every Sunday 9 pm. 6 | # 2) Reminds those who have not filled the sheet every Tuesday and Friday at 6 am. 7 | # 8 | ###################################################################################### 9 | 10 | https = require('follow-redirects').https 11 | cron = require('node-cron') 12 | output = '' 13 | 14 | module.exports = (robot) -> 15 | 16 | if (url = process.env.MIRROR_SCRIPT_URL) 17 | 18 | #This will run every Sunday at 9 pm 19 | cron.schedule '0 0 21 * * Sunday', ()-> 20 | https.get url+"?type=3", (res) -> 21 | res.on 'data', (body) -> 22 | output += body 23 | res.on 'end', () -> 24 | robot.send room: 'general', "Added new week to Mdg Mirror. (#{output})" 25 | output = '' 26 | 27 | #This will run every Tuesday and Friday at 6 am 28 | cron.schedule '0 0 6 * * Tuesday,Friday', ()-> 29 | https.get url+"?type=2", (res) -> 30 | res.on 'data', (body) -> 31 | output += body 32 | res.on 'end', () -> 33 | namesArray = JSON.parse output 34 | unless namesArray.length 35 | return 36 | namesArray = namesArray.map (el) -> return '@'+el 37 | names = namesArray.join " " 38 | robot.send room: 'general', names+"\nPlease fill up the activities sheet." 39 | output = '' 40 | 41 | # else 42 | # console.log "MIRROR_SCRIPT_URL not found in environment variables" 43 | -------------------------------------------------------------------------------- /scripts/most-spoken-words.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # Listen all the words spoken by a user. 3 | # Builds a dictionary of words along with the number of times it was spoken. 4 | # Display the words spoken by a particular user in desc order. 5 | # Show message stats 6 | # 7 | # Dependencies: 8 | # natural - https://www.npmjs.com/package/natural 9 | # 10 | # Configuration: 11 | # None 12 | # 13 | # Commands: 14 | # bot show me words spoken by me 15 | # bot stats 16 | # 17 | # Author: 18 | # csoni111 19 | 20 | cron = require('node-cron') 21 | natural = require 'natural' 22 | tokenizer = new natural.WordTokenizer() 23 | util = require('./util') 24 | 25 | module.exports = (robot) -> 26 | robot.hear /^(.+)/i, (msg) -> 27 | if msg.match[0].toLowerCase().startsWith robot.name.toLowerCase() 28 | return 29 | regex = /:([^ :]+):/g 30 | msg.match[0] = msg.match[0].replace(regex,"") 31 | words = tokenizer.tokenize msg.match[0] 32 | pronouns = ['i', 'he', 'she', 'it', 'we', 33 | 'me', 'mine', 'his', 'her', 'something', 34 | 'they', 'their', 'our', 'those', 35 | 'this', 'that', 'these', 'anything', 36 | 'anybody', 'anyone', 'everyone', 37 | 'each', 'either', 'everybody', 'none', 38 | 'everything', 'neither', 'nobody', 39 | 'nothing', 'one', 'somebody', 'someone'] 40 | 41 | conjunctions = ['and', 'yet', 'but', 'for', 'so', 'or', 'nor'] 42 | 43 | otherWords = ['a', 'at', 'in', 'for', 'is', 'am', 'are'] 44 | 45 | words = words.filter (val) -> 46 | val.toLowerCase() not in otherWords.concat(pronouns, conjunctions) 47 | if words.length > 0 48 | name = msg.message.user.name 49 | user = robot.brain.userForName name 50 | user.msgcount = ++user.msgcount or 1 51 | if typeof user is 'object' 52 | user.words = user.words or {} 53 | if Object.keys(user.words).length > 25 54 | removalCount=i=0 55 | while removalCount is 0 56 | i++ 57 | for word, spokenCount of user.words 58 | if spokenCount <= i 59 | delete user.words[word] 60 | removalCount++ 61 | for word in words 62 | user.words[word] = ++user.words[word] or 1 63 | 64 | 65 | robot.respond /.*show.*words.*/i, (msg) -> 66 | name = msg.message.user.name 67 | user = robot.brain.userForName name 68 | if typeof user is 'object' 69 | sorted = [] 70 | user.words = user.words or {} 71 | for word, spokenCount of user.words 72 | sorted.push [word, spokenCount] 73 | if sorted.length 74 | sorted.sort (a, b) -> 75 | b[1] - a[1] 76 | sorted = sorted.map (val) -> "#{val[0]}(#{val[1]})" 77 | msg.send sorted.join ', ' 78 | else 79 | msg.send msg.random responses 80 | 81 | 82 | robot.respond /stats( \-\w)?/i, (msg) -> 83 | sorted = listOfUsersWithCount() 84 | if not msg.match[1]? 85 | name = msg.message.user.name 86 | sender = robot.brain.userForName name 87 | isSenderInList = false 88 | response = "```Name : Message Count\n" 89 | if sorted.length 90 | for user in sorted 91 | if sender.name is user[0] 92 | isSenderInList = true 93 | break 94 | sorted = sorted.map (val) -> "#{val[0]} : #{val[1]}" 95 | response += sorted.join '\n' 96 | response += "```" 97 | if not isSenderInList 98 | response += "\nCan't find your name?\n" + msg.random responses 99 | msg.send response 100 | else 101 | if msg.match[1] == ' -b' 102 | name = (val[0] for val in sorted) 103 | msgcount = (val[1] for val in sorted) 104 | chart = { 105 | type: "bar", 106 | data: { 107 | labels: name, 108 | datasets: [{ 109 | label: "Message Count", 110 | data: msgcount 111 | }] 112 | }, 113 | options: { 114 | plugins: { 115 | datalabels: { 116 | display: true, 117 | color: '#fff' 118 | } 119 | } 120 | } 121 | } 122 | data = encodeURIComponent(JSON.stringify(chart)) 123 | text = "Message Count" 124 | alt = "Chart showing message count" 125 | util.graph data, text, alt, (reply) -> 126 | msg.send attachments: JSON.stringify(reply) 127 | 128 | #This will run every Saturday at 9 pm 129 | cron.schedule '0 0 21 * * Saturday', ()-> 130 | sorted = listOfUsersWithCount() 131 | name = sorted[0][0] 132 | currMsgRecord = sorted[0][1] 133 | msg = "This week's top poster is @#{name}" 134 | msg += " with #{currMsgRecord} messages" 135 | robot.send room: 'general', msg 136 | if currMsgRecord >= 50 137 | robot.emit "plusplus", {username: name} 138 | for own key, user of robot.brain.data.users 139 | if user.msgcount>0 140 | user.msgcount = 0 141 | 142 | 143 | listOfUsersWithCount = () -> 144 | sorted = [] 145 | for own key, user of robot.brain.data.users 146 | if user.msgcount > 0 147 | sorted.push [user.name, user.msgcount] 148 | if sorted.length 149 | sorted.sort (a, b) -> 150 | b[1] - a[1] 151 | return sorted 152 | 153 | 154 | responses = [ 155 | 'Looks like you are more of a silent man' 156 | 'There ain\'t anything for you!' 157 | 'Be more active next time!' 158 | ] 159 | -------------------------------------------------------------------------------- /scripts/openclose.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # To tell bot if the lab is open or closed. 3 | # 4 | # Commands: 5 | # bot is lab open/close 6 | # bot lab is open/close 7 | # 8 | # Author: 9 | # kt 10 | # 11 | 12 | module.exports = (robot)-> 13 | status = '' 14 | 15 | robot.hear /lab is (open|closed|close)/i, (msg)-> 16 | status = msg.match[1] 17 | msg.send "Okay lab is #{status}" 18 | 19 | 20 | robot.hear /(is|was) (lab|labs) (open|close|closed)/i , (msg)-> 21 | if status.length > 0 22 | msg.send "lab is #{status}" 23 | else 24 | msg.send "Ah! Nobody informed me about the lab status. Don't hold me responsible for this :expressionless:" 25 | 26 | -------------------------------------------------------------------------------- /scripts/ping.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # Utility commands surrounding Hubot uptime. 3 | # 4 | # Commands: 5 | # hubot ping - Reply with pong 6 | # hubot echo - Reply back with 7 | # hubot time - Reply with current time 8 | # hubot die - End hubot process 9 | 10 | module.exports = (robot) -> 11 | robot.respond /PING$/i, (msg) -> 12 | msg.send "PONG" 13 | 14 | robot.respond /ADAPTER$/i, (msg) -> 15 | msg.send robot.adapterName 16 | 17 | robot.respond /ECHO (.*)$/i, (msg) -> 18 | msg.send msg.match[1] 19 | 20 | robot.respond /TIME$/i, (msg) -> 21 | msg.send "Server time is: #{new Date()}" 22 | 23 | robot.respond /DIE$/i, (msg) -> 24 | msg.send "Goodbye, cruel world." 25 | process.exit 0 26 | 27 | -------------------------------------------------------------------------------- /scripts/pugme.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # Pugme is the most important thing in life 3 | # 4 | # Dependencies: 5 | # None 6 | # 7 | # Configuration: 8 | # None 9 | # 10 | # Commands: 11 | # hubot pug me - Receive a pug 12 | # hubot pug bomb N - get N pugs 13 | 14 | module.exports = (robot) -> 15 | 16 | robot.respond /pug me/i, (msg) -> 17 | msg.http("http://pugme.herokuapp.com/random") 18 | .get() (err, res, body) -> 19 | msg.send JSON.parse(body).pug 20 | 21 | robot.respond /pug bomb( (\d+))?/i, (msg) -> 22 | count = msg.match[2] || 5 23 | msg.http("http://pugme.herokuapp.com/bomb?count=" + count) 24 | .get() (err, res, body) -> 25 | msg.send pug for pug in JSON.parse(body).pugs 26 | 27 | robot.respond /how many pugs are there/i, (msg) -> 28 | msg.http("http://pugme.herokuapp.com/count") 29 | .get() (err, res, body) -> 30 | msg.send "There are #{JSON.parse(body).pug_count} pugs." 31 | 32 | -------------------------------------------------------------------------------- /scripts/random-quote.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # Fetches and sends a random quote from the internet. 3 | # 4 | # Dependencies: 5 | # node-soupselect 6 | # node-htmlparser 7 | # 8 | # Configuration: 9 | # NONE 10 | # 11 | # Commands: 12 | # random quote 13 | # 14 | # Author: 15 | # csoni111 16 | 17 | select = require('soupselect').select 18 | htmlparser = require 'htmlparser' 19 | 20 | module.exports = (robot) -> 21 | fetchRandomQuote = (callback) -> 22 | robot.http("http://inspirationalshit.com/endlessquotesrotator.php") 23 | .get() (err, res, body) -> 24 | if err 25 | callback false #, "error: #{err}" 26 | return 27 | if res.statusCode isnt 200 28 | callback false #, "statusCode: #{res.statusCode}" 29 | return 30 | 31 | handler = new htmlparser.DefaultHandler (err, dom) -> 32 | if err 33 | callback false 34 | else 35 | quote = select dom, 'blockquote p' 36 | author = select dom, 'blockquote footer cite' 37 | callback true, quote[0].children[0].raw, author[0].children[0].raw 38 | 39 | parser = new htmlparser.Parser handler 40 | parser.parseComplete body 41 | 42 | robot.respond /.*random.*quote.*/i , (msg) -> 43 | fetchRandomQuote (success, quote, author) -> 44 | if success 45 | msg.send "_#{quote}_ - #{author}" 46 | else 47 | msg.send '_error_' 48 | 49 | robot.on 'send:quote', (randomMsg) -> 50 | fetchRandomQuote (success, quote, author) -> 51 | if success 52 | randomMsg = "_#{quote}_ - #{author}" 53 | robot.send room: 'general', randomMsg -------------------------------------------------------------------------------- /scripts/roles.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # Assign roles to people you're chatting with 3 | # 4 | # Commands: 5 | # hubot is a badass guitarist - assign a role to a user 6 | # hubot is not a badass guitarist - remove a role from a user 7 | # hubot who is - see what roles a user has 8 | # 9 | # Examples: 10 | # hubot holman is an ego surfer 11 | # hubot holman is not an ego surfer 12 | 13 | module.exports = (robot) -> 14 | 15 | if process.env.HUBOT_AUTH_ADMIN? 16 | robot.logger.warning 'The HUBOT_AUTH_ADMIN environment variable is set not going to load roles.coffee, you should delete it' 17 | return 18 | 19 | getAmbiguousUserText = (users) -> 20 | "Be more specific, I know #{users.length} people named like that: #{(user.name for user in users).join(", ")}" 21 | 22 | robot.respond /who is @?([\w .\-]+)\?*$/i, (msg) -> 23 | joiner = ', ' 24 | name = msg.match[1].trim() 25 | 26 | if name is "you" 27 | msg.send "Who ain't I?" 28 | else if name is robot.name 29 | msg.send "The best." 30 | else 31 | users = robot.brain.usersForFuzzyName(name) 32 | if users.length is 1 33 | user = users[0] 34 | user.roles = user.roles or [ ] 35 | if user.roles.length > 0 36 | if user.roles.join('').search(',') > -1 37 | joiner = '; ' 38 | msg.send "#{name} is #{user.roles.join(joiner)}." 39 | else 40 | msg.send "#{name} is nothing to me." 41 | else if users.length > 1 42 | msg.send getAmbiguousUserText users 43 | else 44 | msg.send "#{name}? Never heard of 'em" 45 | 46 | robot.respond /@?([\w .\-_]+) is (["'\w: \-_]+)[.!]*$/i, (msg) -> 47 | name = msg.match[1].trim() 48 | newRole = msg.match[2].trim() 49 | 50 | unless name in ['', 'who', 'what', 'where', 'when', 'why', 'lab'] 51 | unless newRole.match(/^not\s+/i) 52 | users = robot.brain.usersForFuzzyName(name) 53 | if users.length is 1 54 | user = users[0] 55 | user.roles = user.roles or [ ] 56 | 57 | if newRole in user.roles 58 | msg.send "I know" 59 | else 60 | user.roles.push(newRole) 61 | if name.toLowerCase() is robot.name.toLowerCase() 62 | msg.send "Ok, I am #{newRole}." 63 | else 64 | msg.send "Ok, #{name} is #{newRole}." 65 | else if users.length > 1 66 | msg.send getAmbiguousUserText users 67 | else 68 | msg.send "I don't know anything about #{name}." 69 | 70 | robot.respond /@?([\w .\-_]+) is not (["'\w: \-_]+)[.!]*$/i, (msg) -> 71 | name = msg.match[1].trim() 72 | newRole = msg.match[2].trim() 73 | 74 | unless name in ['', 'who', 'what', 'where', 'when', 'why', 'lab'] 75 | users = robot.brain.usersForFuzzyName(name) 76 | if users.length is 1 77 | user = users[0] 78 | user.roles = user.roles or [ ] 79 | if msg.envelope.user.name == user.name 80 | msg.send "Nice try, dumbass!" 81 | else if newRole not in user.roles 82 | msg.send "I know." 83 | else 84 | user.roles = (role for role in user.roles when role isnt newRole) 85 | msg.send "Ok, #{name} is no longer #{newRole}." 86 | else if users.length > 1 87 | msg.send getAmbiguousUserText users 88 | else 89 | msg.send "I don't know anything about #{name}." 90 | 91 | -------------------------------------------------------------------------------- /scripts/rules.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # Make sure that hubot knows the rules. 3 | # 4 | # Commands: 5 | # hubot the rules - Make sure hubot still knows the rules. 6 | # 7 | # Notes: 8 | # DON'T DELETE THIS SCRIPT! ALL ROBAWTS MUST KNOW THE RULES 9 | 10 | rules = [ 11 | "1. A robot may not injure a human being or, through inaction, allow a human being to come to harm.", 12 | "2. A robot must obey any orders given to it by human beings, except where such orders would conflict with the First Law.", 13 | "3. A robot must protect its own existence as long as such protection does not conflict with the First or Second Law." 14 | ] 15 | 16 | otherRules = [ 17 | "A developer may not injure Apple or, through inaction, allow Apple to come to harm.", 18 | "A developer must obey any orders given to it by Apple, except where such orders would conflict with the First Law.", 19 | "A developer must protect its own existence as long as such protection does not conflict with the First or Second Law." 20 | ] 21 | 22 | module.exports = (robot) -> 23 | robot.respond /(what are )?the (three |3 )?(rules|laws)/i, (msg) -> 24 | text = msg.message.text 25 | if text.match(/apple/i) or text.match(/dev/i) 26 | msg.send otherRules.join('\n') 27 | else 28 | msg.send rules.join('\n') 29 | 30 | -------------------------------------------------------------------------------- /scripts/seen.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # A hubot script that tracks when/where users were last seen. 3 | # 4 | # Commands: 5 | # hubot seen - show when and where user was last seen 6 | # hubot seen in last 24h - list users seen in last 24 hours 7 | # 8 | # Configuration: 9 | # HUBOT_SEEN_TIMEAGO - If set to `false` (defaults to `true`), last seen times will be absolute dates instead of relative 10 | # 11 | # Author: 12 | # wiredfool, patcon@gittip 13 | 14 | config = 15 | use_timeago: process.env.HUBOT_SEEN_TIMEAGO isnt 'false' 16 | 17 | clean = (thing) -> 18 | (thing || '').toLowerCase().trim() 19 | 20 | is_pm = (msg) -> 21 | try 22 | msg.message.user.pm 23 | catch error 24 | false 25 | 26 | ircname = (msg) -> 27 | try 28 | msg.message.user.name 29 | catch error 30 | false 31 | 32 | ircchan = (msg) -> 33 | try 34 | msg.message.user.room 35 | catch error 36 | false 37 | 38 | class Seen 39 | constructor: (@robot) -> 40 | @cache= {} 41 | 42 | @robot.brain.on 'loaded', => @cache = @robot.brain.data.seen || {} 43 | 44 | save: => 45 | # TODO: should we try to only write changes to the db, instead of the entire map? 46 | @robot.brain.data.seen = @cache 47 | 48 | add: (user, channel, msg) -> 49 | @robot.logger.debug "seen.add #{clean user} on #{channel}" 50 | @cache[clean user] = 51 | chan: channel 52 | date: new Date() - 0 53 | @save() 54 | 55 | last: (user) -> 56 | @cache[clean user] ? {} 57 | 58 | usersSince: (hoursAgo) -> 59 | HOUR_MILLISECONDS = 60*60*1000 60 | seenSinceTime = new Date(Date.now() - hoursAgo*HOUR_MILLISECONDS) 61 | users = (nick for nick, data of @cache when data.date > seenSinceTime) 62 | return users 63 | 64 | module.exports = (robot) -> 65 | seen = new Seen robot 66 | 67 | # Keep track of last msg heard 68 | robot.hear /.*/, (msg) -> 69 | unless is_pm msg 70 | seen.add (ircname msg), (ircchan msg), msg.message.text 71 | 72 | robot.respond /seen @?([-\w.\\^|{}`\[\]]+):? ?(.*)/, (msg) -> 73 | if msg.match[1] == "in" and msg.match[2] == "last 24h" 74 | users = seen.usersSince(24) 75 | msg.send "Active in #{msg.match[2]}: #{users.join(', ')}" 76 | else 77 | robot.logger.debug "seen check #{clean msg.match[1]}" 78 | nick = msg.match[1] 79 | last = seen.last nick 80 | if last.date 81 | date_string = if config.use_timeago 82 | timeago = require 'node-time-ago' 83 | timeago(new Date(last.date)) 84 | else 85 | "at #{new Date(last.date)}" 86 | 87 | msg.send "#{nick} was last seen #{date_string} in ##{last.chan}" 88 | 89 | else 90 | msg.send "I haven't seen #{nick} around lately" 91 | -------------------------------------------------------------------------------- /scripts/skipped-word.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # Script to maintain a list of words to be skipped 3 | # during the execution of some commands. 4 | # 5 | # Commands: 6 | # hubot skip word 7 | # hubot unskip word 8 | # hubot show skipped 9 | # 10 | # Author: 11 | # aman-singh7 12 | 13 | module.exports = (robot) -> 14 | 15 | # returns list of skipped words 16 | skippedlist = () -> 17 | List = robot.brain.get("skippedlist") or [] 18 | robot.brain.set("skippedlist", List) 19 | List 20 | 21 | robot.respond /skip ([\w\-_]+)/i, (msg) -> 22 | SkippedList = skippedlist() 23 | 24 | word = msg.match[1] 25 | # check if that word already skipped 26 | if word in SkippedList 27 | msg.send "#{word} is already skipped" 28 | else 29 | SkippedList.push(word) 30 | msg.send "#{word} is skipped" 31 | 32 | robot.respond /unskip ([\w\-_]+)/i, (msg) -> 33 | SkippedList = skippedlist() 34 | 35 | word = msg.match[1] 36 | # check if the word is skipped or not 37 | if word in SkippedList 38 | SkippedList.splice(SkippedList.indexOf(word), 1) 39 | msg.send "#{word} is unskipped" 40 | else 41 | msg.send "#{word} is never skipped" 42 | 43 | robot.respond /show skipped/i, (msg) -> 44 | SkippedList = skippedlist() 45 | 46 | # return the words if the list is not empty 47 | if SkippedList.length 48 | msg.send "#{SkippedList.join(', ')}" 49 | 50 | else 51 | msg.send "Nothing is skipped!!" 52 | -------------------------------------------------------------------------------- /scripts/storage.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # Inspect the data in redis easily 3 | # 4 | # Commands: 5 | # hubot show users - Display all users that hubot knows about 6 | # hubot show storage - Display the contents that are persisted in the brain 7 | 8 | 9 | Util = require "util" 10 | 11 | module.exports = (robot) -> 12 | robot.respond /show storage$/i, (msg) -> 13 | output = Util.inspect(robot.brain.data, false, 4) 14 | msg.send output 15 | 16 | robot.respond /show users$/i, (msg) -> 17 | response = "" 18 | 19 | for own key, user of robot.brain.data.users 20 | response += "#{user.id} #{user.name}" 21 | response += " <#{user.email_address}>" if user.email_address 22 | response += "\n" 23 | 24 | msg.send response 25 | 26 | -------------------------------------------------------------------------------- /scripts/toss.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # Simple toss and roll a dice script 3 | # 4 | # Commands: 5 | # hubot toss 6 | # hubot roll dice 7 | # hubot roll n dices 8 | 9 | module.exports = (robot) -> 10 | toss = [':head:\nHeads', ':tail:\nTails'] 11 | dice = [':one:', ':two:', ':three:', ':four:', ':five:', ':six:'] 12 | robot.respond /toss$/i, (msg) -> 13 | msg.send msg.random toss 14 | 15 | robot.respond /roll( \d)?( a)? dices?$/i, (msg) -> 16 | i=1 17 | numbers = [] 18 | if msg.match[1] 19 | i=parseInt msg.match[1].trim() 20 | while i>0 21 | numbers.push dice[Math.floor Math.random()*6] 22 | i-- 23 | msg.send numbers.join ' ' 24 | -------------------------------------------------------------------------------- /scripts/translate.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # Allows Hubot to know many languages. 3 | # 4 | # Commands: 5 | # hubot translate me - Searches for a translation for the and then prints that bad boy out. 6 | # hubot translate me from into - Translates from into . Both and are optional 7 | 8 | API_KEY = process.env.HUBOT_GOOGLE_TRANSLATE_API_KEY 9 | 10 | languages = 11 | "af": "Afrikaans", 12 | "sq": "Albanian", 13 | "ar": "Arabic", 14 | "az": "Azerbaijani", 15 | "eu": "Basque", 16 | "bn": "Bengali", 17 | "be": "Belarusian", 18 | "bg": "Bulgarian", 19 | "ca": "Catalan", 20 | "zh-CN": "Simplified Chinese", 21 | "zh-TW": "Traditional Chinese", 22 | "hr": "Croatian", 23 | "cs": "Czech", 24 | "da": "Danish", 25 | "nl": "Dutch", 26 | "en": "English", 27 | "eo": "Esperanto", 28 | "et": "Estonian", 29 | "tl": "Filipino", 30 | "fi": "Finnish", 31 | "fr": "French", 32 | "gl": "Galician", 33 | "ka": "Georgian", 34 | "de": "German", 35 | "el": "Greek", 36 | "gu": "Gujarati", 37 | "ht": "Haitian Creole", 38 | "iw": "Hebrew", 39 | "hi": "Hindi", 40 | "hu": "Hungarian", 41 | "is": "Icelandic", 42 | "id": "Indonesian", 43 | "ga": "Irish", 44 | "it": "Italian", 45 | "ja": "Japanese", 46 | "kn": "Kannada", 47 | "ko": "Korean", 48 | "la": "Latin", 49 | "lv": "Latvian", 50 | "lt": "Lithuanian", 51 | "mk": "Macedonian", 52 | "ms": "Malay", 53 | "mt": "Maltese", 54 | "no": "Norwegian", 55 | "fa": "Persian", 56 | "pl": "Polish", 57 | "pt": "Portuguese", 58 | "ro": "Romanian", 59 | "ru": "Russian", 60 | "sr": "Serbian", 61 | "sk": "Slovak", 62 | "sl": "Slovenian", 63 | "es": "Spanish", 64 | "sw": "Swahili", 65 | "sv": "Swedish", 66 | "ta": "Tamil", 67 | "te": "Telugu", 68 | "th": "Thai", 69 | "tr": "Turkish", 70 | "uk": "Ukrainian", 71 | "ur": "Urdu", 72 | "vi": "Vietnamese", 73 | "cy": "Welsh", 74 | "yi": "Yiddish" 75 | 76 | getCode = (language,languages) -> 77 | for code, lang of languages 78 | return code if lang.toLowerCase() is language.toLowerCase() 79 | 80 | module.exports = (robot) -> 81 | language_choices = (language for _, language of languages).sort().join('|') 82 | pattern = new RegExp('translate(?: me)?' + 83 | "(?: from (#{language_choices}))?" + 84 | "(?: (?:in)?to (#{language_choices}))?" + 85 | '(.*)', 'i') 86 | robot.respond pattern, (msg) -> 87 | term = "\"#{msg.match[3]?.trim()}\"" 88 | origin = if msg.match[1] isnt undefined then getCode(msg.match[1], languages) else 'auto' 89 | target = if msg.match[2] isnt undefined then getCode(msg.match[2], languages) else 'en' 90 | 91 | msg.http("https://www.googleapis.com/language/translate/v2") 92 | .query({ 93 | key: API_KEY 94 | source: origin 95 | target: target 96 | q: term 97 | }) 98 | .get() (err, res, body) -> 99 | if err 100 | msg.send "Failed to connect to GAPI" 101 | robot.emit 'error', err, res 102 | return 103 | 104 | try 105 | parsed = JSON.parse(body) 106 | parsed = parsed.data.translations[0] 107 | if parsed 108 | language = languages[parsed.detectedSourceLanguage] 109 | translated = parsed.translatedText 110 | if msg.match[2] is undefined 111 | msg.send "#{term} is #{language} for #{translated}" 112 | else 113 | msg.send "The #{language} #{term} translates as #{translated} in #{languages[target]}" 114 | 115 | catch err 116 | msg.send "Failed to parse GAPI response" 117 | robot.emit 'error', err -------------------------------------------------------------------------------- /scripts/update-names.coffee: -------------------------------------------------------------------------------- 1 | # Desciption: 2 | # Script for updating names database 3 | # 4 | # Commands: 5 | # hubot update db 6 | https = require 'https' 7 | token = process.env.SLACK_API_TOKEN 8 | parsedUsers=0 9 | updatedUsers=0 10 | totalUsers=0 11 | currentRoom=null 12 | 13 | module.exports = (robot) -> 14 | if token 15 | robot.respond /update db/i, (msg) -> 16 | msg.send "Updating names in database" 17 | currentRoom = msg.message.user.room 18 | parsedUsers=0 19 | updatedUsers=0 20 | totalUsers=Object.keys(robot.brain.data.users).length 21 | for own key, user of robot.brain.data.users 22 | updateName user.id 23 | 24 | updateName = (uid) -> 25 | pre = '/api/users.info?token=' 26 | post = '&user=' 27 | url = pre+token+post+uid 28 | output = '' 29 | req = https.get { host: 'slack.com', path: url }, (res) -> 30 | res.on 'data', (chunk) -> 31 | output += chunk 32 | res.on 'end', () -> 33 | parsedUsers++ 34 | data = JSON.parse('' + output) 35 | if data.ok 36 | user = robot.brain.userForId data.user.id 37 | if user.name isnt data.user.name 38 | user.name = data.user.name 39 | updatedUsers++ 40 | if parsedUsers==totalUsers 41 | robot.send room: currentRoom, "Updated names for #{updatedUsers} out of #{totalUsers} users" 42 | 43 | # else 44 | # console.log "No slack Api token found" 45 | -------------------------------------------------------------------------------- /scripts/util.coffee: -------------------------------------------------------------------------------- 1 | https = require('follow-redirects').https 2 | 3 | # Get the user details 4 | exports.info = (callback) -> 5 | output = '' 6 | https.get process.env.INFO_SPREADSHEET_URL + "?output=csv", (res) -> 7 | res.on 'data', (body) -> 8 | output += body 9 | res.on 'end', () -> 10 | callback output 11 | res.on 'error', (err) -> 12 | callback err 13 | 14 | 15 | # Graph Attachment 16 | exports.graph = (enc_url, text, alt_text, callback) -> 17 | attachments = [ 18 | { 19 | color: "#f2c744", 20 | blocks: [ 21 | { 22 | type: "image", 23 | title: { 24 | type: "plain_text", 25 | text: text 26 | }, 27 | image_url: "https://quickchart.io/chart?c=#{enc_url}", 28 | alt_text: alt_text 29 | } 30 | ] 31 | } 32 | ] 33 | callback attachments -------------------------------------------------------------------------------- /scripts/wail.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # Returns the names of people in lab 3 | # 4 | # Dependencies: 5 | # None 6 | # 7 | # Configuration: 8 | # WAIL_PIC_URL 9 | # 10 | # Commands: 11 | # hubot who all in lab 12 | # 13 | # Author: 14 | # csoni111 15 | 16 | module.exports = (robot) -> 17 | robot.respond /who.*lab/i, (msg) -> 18 | waliUrl = process.env.WAIL_PIC_URL + '?t=' + new Date().getTime() 19 | msg.send( 20 | "attachments": [ 21 | "fallback": "Here's a pic: #{waliUrl}" 22 | "color": "#36a64f" 23 | "pretext": "Here's a pic:" 24 | "image_url": waliUrl 25 | "ts": new Date().getTime() / 1000 26 | ] 27 | ) 28 | -------------------------------------------------------------------------------- /scripts/youtube.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # YouTube video search 3 | # 4 | # Configuration: 5 | # HUBOT_YOUTUBE_API_KEY - Obtained from https://console.developers.google.com 6 | # HUBOT_YOUTUBE_DETERMINISTIC_RESULTS - Optional boolean flag to only fetch 7 | # the top result from the YouTube search 8 | # HUBOT_YOUTUBE_HEAR - Optional boolean flag to globally hear from channels 9 | # Commands: 10 | # hubot youtube me - Searches YouTube for the query and returns the video embed link. 11 | module.exports = (robot) -> 12 | resType = "respond" 13 | trigger = /(?:youtube|yt)(?: me)? (.*)/i 14 | if process.env.HUBOT_YOUTUBE_HEAR == 'true' 15 | resType = "hear" 16 | trigger = /^(?:youtube|yt)(?: me)? (.*)/i 17 | 18 | robot[resType] trigger, (msg) -> 19 | unless process.env.HUBOT_YOUTUBE_API_KEY 20 | robot.logger.error 'HUBOT_YOUTUBE_API_KEY is not set.' 21 | return msg.send "You must configure the HUBOT_YOUTUBE_API_KEY environment variable" 22 | query = msg.match[1] 23 | maxResults = if process.env.HUBOT_YOUTUBE_DETERMINISTIC_RESULTS == 'true' then 1 else 15 24 | robot.logger.debug "Query: #{query}\n Max Results: #{maxResults}" 25 | robot.http("https://www.googleapis.com/youtube/v3/search") 26 | .query({ 27 | order: 'relevance' 28 | part: 'snippet' 29 | type: 'video' 30 | maxResults: maxResults 31 | q: query 32 | key: process.env.HUBOT_YOUTUBE_API_KEY 33 | }) 34 | .get() (err, res, body) -> 35 | robot.logger.debug body 36 | if err 37 | robot.logger.error err 38 | return robot.emit 'error', err, msg 39 | try 40 | if res.statusCode is 200 41 | videos = JSON.parse(body) 42 | robot.logger.debug "Videos: #{JSON.stringify(videos)}" 43 | else 44 | return robot.emit 'error', "#{res.statusCode}: #{body}", msg 45 | catch error 46 | robot.logger.error error 47 | return msg.send "Error! #{body}" 48 | if videos.error 49 | robot.logger.error videos.error 50 | return msg.send "Error! #{JSON.stringify(videos.error)}" 51 | videos = videos.items 52 | unless videos? && videos.length > 0 53 | return msg.send "No video results for \"#{query}\"" 54 | video = msg.random videos 55 | msg.send "https://www.youtube.com/watch?v=#{video.id.videoId}" -------------------------------------------------------------------------------- /start_bot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . ~/.nvm/nvm.sh 4 | nvm install 8.10.0 5 | nvm use 8.10.0 6 | export HUBOT_NAME="bot" 7 | export PORT=8080 8 | set -a; source .env; set +a; 9 | npm run start --------------------------------------------------------------------------------