hubot-freetacos
Version:
A hubot script for giving free tacos
224 lines (180 loc) • 6.65 kB
text/coffeescript
# Description:
# The taco giveth and the poop taketh away.
#
# Dependencies:
# "underscore": ">= 1.0.0"
# "clark": "0.0.6"
# "hubot-slack": ">= 4.5.1"
#
# Configuration:
#
# Commands:
# @name :taco: - add a taco (up to 5) to @name (must be user)
# @name :tacobell: - add 5 tacos to @name (must be user)
# @name :poop: - remove a taco (up to 5) from @name (must be user)
# @name :poop_fire: - minus 5 tacos to @name (must be user)
# hubot tacos @name - give current taco count for @name
# hubot top-tacos <amount> - top <amount>
# hubot bottom-tacos <amount> - bottom <amount>
# hubot erase-tacos @name
#
# Author:
# KingOfBananas
# ajacksified (for creating hubot-pluslus)
#
_ = require('underscore')
clark = require('clark')
class ScoreKeeper
constructor: (@robot) ->
storageLoaded = =>
@storage = @robot.brain.data.freeTacos ||= {
scores: {}
log: {}
last: {}
}
if typeof @storage.last == "string"
@storage.last = {}
@robot.logger.debug "Free Tacos Data Loaded: " + JSON.stringify(@storage, null, 2)
@robot.brain.on "loaded", storageLoaded
storageLoaded() # just in case storage was loaded before we got here
getUser: (user) ->
@storage.scores[user] ||= 0
user
saveUser: (user, from, room) ->
@saveScoreLog(user, from, room)
@robot.brain.save()
[@storage.scores[user]]
add: (user, from, room, amount) ->
if @validate(user, from)
user = @getUser(user)
@storage.scores[user] += amount
@saveUser(user, from, room)
else
null
subtract: (user, from, room, amount) ->
if @validate(user, from)
user = @getUser(user)
@storage.scores[user] -= amount
@saveUser(user, from, room)
else
null
erase: (user, from, room) ->
user = @getUser(user)
delete @storage.scores[user]
return true
scoreForUser: (user) ->
user = @getUser(user)
@storage.scores[user]
saveScoreLog: (user, from, room) ->
unless typeof @storage.log[from] == "object"
@storage.log[from] = {}
@storage.log[from][user] = new Date()
@storage.last[room] = {user: user}
last: (room) ->
last = @storage.last[room]
if typeof last == 'string'
[last]
else
[last.user]
isSpam: (user, from) ->
@storage.log[from] ||= {}
if !@storage.log[from][user]
return false
dateSubmitted = @storage.log[from][user]
date = new Date(dateSubmitted)
messageIsSpam = date.setSeconds(date.getSeconds() + 5) > new Date()
if !messageIsSpam
delete @storage.log[from][user] #clean it up
messageIsSpam
validate: (user, from) ->
user != from && user != "" && !@isSpam(user, from)
length: () ->
@storage.log.length
top: (amount) ->
tops = []
for name, score of @storage.scores
tops.push(name: name, score: score)
tops.sort((a,b) -> b.score - a.score).slice(0,amount)
bottom: (amount) ->
all = @top(@storage.scores.length)
all.sort((a,b) -> b.score - a.score).reverse().slice(0,amount)
normalize: (fn) ->
scores = {}
_.each(@storage.scores, (score, name) ->
scores[name] = fn(score)
delete scores[name] if scores[name] == 0
)
@storage.scores = scores
@robot.brain.save()
module.exports = (robot) ->
scoreKeeper = new ScoreKeeper(robot)
deltaMax = 5
robot.hear ///((?:\:taco\:\s{0,1}|\:poop\:\s{0,1}){1,#{deltaMax}}|\:tacobell\:|\:poop_fire\:)///i, (res) ->
operator = res.match[1]
from = res.message.user.id
room = res.message.room
# filter mentions to just user mentions
user_mentions = (mention for mention in res.message.mentions when mention.type is "user")
# when there are user mentions...
if user_mentions.length > 0
# process each mention
for { id } in user_mentions
score = if operator.includes(":tacobell:")
amount = deltaMax
scoreKeeper.add(id, from, room, amount)
else if operator.includes(":poop_fire:")
amount = deltaMax
scoreKeeper.subtract(id, from, room, amount)
else if operator.includes(":taco:")
amount = operator.split(":taco:").length - 1
scoreKeeper.add(id, from, room, amount)
else if operator.includes(":poop:")
amount = operator.split(":poop:").length - 1
scoreKeeper.subtract(id, from, room, amount)
if score?
res.send "<@#{id}> has #{score} :taco:"
robot.respond /(?:erase-tacos )/i, (res) ->
from = res.message.user.id
room = res.message.room
user = res.envelope.user
isAdmin = @robot.auth?.hasRole(user, 'freetacos-admin') or @robot.auth?.hasRole(user, 'admin')
# filter mentions to just user mentions
user_mentions = (mention for mention in res.message.mentions when mention.type is "user")
# when there are user mentions...
if user_mentions.length > 0
for { id } in user_mentions
if not @robot.auth? or isAdmin
erased = scoreKeeper.erase(id, from, room)
else
return res.reply "Sorry, you don't have authorization to do that."
if erased?
message = "Erased points for <@#{id}>"
res.send message
robot.respond /tacos (for\s)?(.*)/i, (res) ->
# filter mentions to just user mentions
user_mentions = (mention for mention in res.message.mentions when mention.type is "user")
# when there are user mentions...
if user_mentions.length > 0
for { id } in user_mentions
score = scoreKeeper.scoreForUser(id)
reasonString = "<@#{id}> has #{score} :taco:"
res.send reasonString
robot.respond /(top-tacos|bottom-tacos) (\d+)/i, (res) ->
operator = res.match[1]
amount = parseInt(res.match[2]) || 10
message = []
command = ""
if (operator == "top-tacos")
command = "top"
else
command = "bottom"
tops = scoreKeeper[command](amount)
if tops.length > 0
for i in [0..tops.length-1]
message.push("#{i+1}. <@#{tops[i].name}> : #{tops[i].score}")
else
message.push("No scores to keep track of yet!")
if(command == "top")
graphSize = Math.min(tops.length, Math.min(amount, 20))
message.splice(0, 0, clark(_.first(_.pluck(tops, "score"), graphSize)))
res.send message.join("\n")