Created
August 17, 2012 05:34
-
-
Save turtlesoupy/3376208 to your computer and use it in GitHub Desktop.
Cosbybot / Urkelbot implementation for the Silly Face Society
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{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