Skip to content

Instantly share code, notes, and snippets.

@turtlesoupy
Created August 17, 2012 05:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save turtlesoupy/3376208 to your computer and use it in GitHub Desktop.
Save turtlesoupy/3376208 to your computer and use it in GitHub Desktop.
Cosbybot / Urkelbot implementation for the Silly Face Society
{config, glb} = require("../lib/bootstrap")("sfs_bot")
fs = require 'fs'
http = require 'http'
winston = require 'winston'
async = require 'async'
utils = require '../lib/utils'
querystring = require 'querystring'
request = require('request').defaults
headers:
"Accept": "application/json"
"x-sfs-app-version": "0.92"
throw new Error("Need a third argument of botName") unless process.argv.length > 2
botName = process.argv[2]
botSettings = config.bots[botName]
throw new Error("Bad bot name: '#{botName}'") unless botSettings?
sfsUrl = (path, secure=false) -> "#{if secure then 'https' else 'http'}://#{config.webHost}/#{path}"
forwardError = fe = (cb, fn) -> (err) ->
return cb(err) if err?
fn Array::slice.call(arguments, 1)...
#Default error handling decorator for request module
httpDefaultError = hde = (cb, fn) -> (err, response, body) ->
return cb(err) if err?
return cb(new Error("Bad response code #{response.statusCode}: #{body}")) if response.statusCode != 200
try j = JSON.parse(body) catch e then (return callback(new Error("Unable to parse body as json: #{body}")))
if fn? then fn response, j else cb(null, j)
ACTION_STATES = {YOU_SUBMIT:0, YOU_GUESS:1, THEY_SUBMIT:2, THEY_GUESS:3}
actionState = (playerId, round) ->
if playerId == round.submitterId
if round.description? then ACTION_STATES.THEY_GUESS else ACTION_STATES.YOU_SUBMIT
else
if round.description? then ACTION_STATES.YOU_GUESS else ACTION_STATES.THEY_SUBMIT
login = (callback) ->
request.post
url: sfsUrl("login", true)
form:
loginMethod: "email"
email: botSettings.login
password: botSettings.password
, hde callback
refresh = (callback) -> request.get {url: sfsUrl "passive/refresh"}, hde callback
guessUntilClosed = (round, callback) ->
winston.info "Guessing for round #{round.id}"
roundClosed = false
async.until (-> roundClosed),
(callback) ->
guess = (utils.randomElement(cfs).id for cfs in round.description.confounders).join("!")
request.post
url: sfsUrl "passive/rounds/#{round.id}/guesses"
form: {guess}
, hde callback, (response, json) ->
roundClosed = json.roundClosed
callback()
callback
submitPhoto = (round, callback) ->
winston.info "Submitting for round #{round.id}"
photoPath = utils.randomElement(botSettings.images)
request.get {url: sfsUrl "passive/rounds/#{round.id}/send_options"}, hde callback, (response, json) ->
if Math.random() < 0.25 and json.options.models and json.options.models.length > 0
descriptionType = 1
description = (e.id for e in utils.randomElement(d.model.parts for d in json.options.models)).join("!")
else
descriptionType = 0
description = (e.id for e in utils.randomElement(d.description.parts for d in json.options.descriptions)).join("!")
fs.readFile photoPath, fe callback, (photo) ->
multipart = ({
'Content-Disposition': "form-data; name=\"#{k}\""
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
'body': querystring.escape(v)
} for k,v of {descriptionType, description})
multipart.push
'Content-Disposition': 'form-data; name="snap"; filename="snap.jpg"'
'Content-Type': 'image/jpeg'
'body': photo
request.post
url: sfsUrl "passive/rounds/#{round.id}/face_image"
headers:
'content-type' : 'multipart/form-data'
multipart: multipart
hde callback
taunt = (round, callback) ->
winston.info "Taunting for round #{round.id}"
request.post
url: sfsUrl "passive/rounds/#{round.id}/taunts"
form:
taunt: utils.randomElement botSettings.taunts
, hde callback
lastExecution = null
reexecute = ->
secondsPassed = if lastExecution? then (new Date().getTime() - lastExecution.getTime()) / 1000 else 1.0/0.0
if secondsPassed > botSettings.scanSeconds
winston.info "Scanning..."
lastExecution = new Date()
execute()
else
setTimeout reexecute, (botSettings.scanThreshold - secondsPassed) * 1000
reportError = (err) ->
winston.fatal "Error in #{botName}", {err: err, stack: err?.stack}
reexecute()
execute = ->
winston.info "#{botName} will hit #{sfsUrl('')}"
login fe reportError, ->
refresh fe reportError, (responseJson) ->
{rankName, id, monocles} = responseJson.currentPlayer
winston.info "#{botName} (#{id}) rank: '#{rankName}' - #{monocles} monocles"
async.forEachSeries responseJson.rounds,
(round, callback) ->
switch actionState(id, round)
when ACTION_STATES.YOU_SUBMIT then submitPhoto round, callback
when ACTION_STATES.YOU_GUESS
guessUntilClosed round, fe callback, ->
taunt round, callback
else callback()
fe reportError, -> reexecute()
reexecute()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment