Merge branch 'master' into production

This commit is contained in:
Nick Winter 2015-09-09 08:59:48 -07:00
commit dd66018ece
10 changed files with 252 additions and 28 deletions

View file

@ -42,7 +42,7 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
buildAutomatically: true buildAutomatically: true
buildAsync: true buildAsync: true
resolutionFactor: SPRITE_RESOLUTION_FACTOR resolutionFactor: SPRITE_RESOLUTION_FACTOR
defaultActions: ['idle', 'die', 'move', 'move', 'attack'] defaultActions: ['idle', 'die', 'move', 'attack']
numThingsLoading: 0 numThingsLoading: 0
lanks: null lanks: null
spriteSheet: null spriteSheet: null
@ -147,9 +147,6 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
#- Adding, removing children for WebGL layers. #- Adding, removing children for WebGL layers.
addLank: (lank) -> addLank: (lank) ->
# TODO: Move this into the production DB rather than setting it dynamically.
if lank.thangType?.get('name') is 'Highlight'
lank.thangType.set('spriteType', 'segmented')
lank.options.resolutionFactor = @resolutionFactor lank.options.resolutionFactor = @resolutionFactor
lank.layer = @ lank.layer = @

View file

@ -99,13 +99,13 @@ module.exports = nativeDescription: "Português do Brasil", englishDescription:
authenticate_gplus: "Autenticar com G+" authenticate_gplus: "Autenticar com G+"
load_profile: "Carregar Perfil do G+" load_profile: "Carregar Perfil do G+"
finishing: "Terminando" finishing: "Terminando"
sign_in_with_facebook: "Conectar com Facebook" sign_in_with_facebook: "Conectar com o Facebook"
sign_in_with_gplus: "Conectar com G+" sign_in_with_gplus: "Conectar com o G+"
signup_switch: "Deseja Criar uma Conta?" signup_switch: "Deseja Criar uma Conta?"
signup: signup:
email_announcements: "Receber notícias por email." email_announcements: "Receber notícias por email."
creating: "Criando a nova conta..." creating: "Criando uma nova conta..."
sign_up: "Criar conta" sign_up: "Criar conta"
log_in: "Entre com a senha" log_in: "Entre com a senha"
social_signup: "Ou, você pode fazer login pelo Facebook ou G+:" social_signup: "Ou, você pode fazer login pelo Facebook ou G+:"
@ -137,7 +137,7 @@ module.exports = nativeDescription: "Português do Brasil", englishDescription:
publish: "Publicar" publish: "Publicar"
create: "Criar" create: "Criar"
manual: "Manual" manual: "Manual"
fork: "Fork" fork: "Fork" # When used as a verb, like "To fork a repository"
play: "Jogar" # When used as an action verb, like "Play next level" play: "Jogar" # When used as an action verb, like "Play next level"
retry: "Tente novamente" retry: "Tente novamente"
actions: "Ações" actions: "Ações"
@ -239,7 +239,7 @@ module.exports = nativeDescription: "Português do Brasil", englishDescription:
victory: "Vitória" victory: "Vitória"
victory_title_prefix: " Vitória " victory_title_prefix: " Vitória "
victory_title_suffix: " Completado!" victory_title_suffix: " Completado!"
victory_sign_up: "Assine para atualizações" victory_sign_up: "Assine para receber atualizações"
victory_sign_up_poke: "Quer receber as últimas novidades por email? Crie uma conta grátis e nós o manteremos informado!" victory_sign_up_poke: "Quer receber as últimas novidades por email? Crie uma conta grátis e nós o manteremos informado!"
victory_rate_the_level: "Avalie o estágio: " # Only in old-style levels. victory_rate_the_level: "Avalie o estágio: " # Only in old-style levels.
victory_return_to_ladder: "Retornar para a progressão" victory_return_to_ladder: "Retornar para a progressão"
@ -251,7 +251,7 @@ module.exports = nativeDescription: "Português do Brasil", englishDescription:
victory_hour_of_code_done: "Terminou?" victory_hour_of_code_done: "Terminou?"
victory_hour_of_code_done_yes: "Sim, eu terminei minha Hora da Programação!" victory_hour_of_code_done_yes: "Sim, eu terminei minha Hora da Programação!"
victory_experience_gained: "XP ganho" victory_experience_gained: "XP ganho"
victory_gems_gained: "Gems ganhas" victory_gems_gained: "Gemas ganhas"
victory_new_item: "Novo item" victory_new_item: "Novo item"
victory_viking_code_school: "Pelas barbas do profeta, esse foi um nível difícil! Se você ainda não é um desenvolvedor de software, você deveria ser. Você acaba de ser priorizado para aceitação na Viking Code School, onde você pode aprender mais e se tornar um desenvolvedor web profissional em 14 semanas." victory_viking_code_school: "Pelas barbas do profeta, esse foi um nível difícil! Se você ainda não é um desenvolvedor de software, você deveria ser. Você acaba de ser priorizado para aceitação na Viking Code School, onde você pode aprender mais e se tornar um desenvolvedor web profissional em 14 semanas."
victory_become_a_viking: "Torne-se um viking" victory_become_a_viking: "Torne-se um viking"
@ -288,7 +288,7 @@ module.exports = nativeDescription: "Português do Brasil", englishDescription:
infinite_loop_title: "Loop Infinito Detectado" infinite_loop_title: "Loop Infinito Detectado"
infinite_loop_description: "O código inicial para construir o mundo nunca parou de rodar. Talvez seja muito lento ou tenha um loop infinito. Ou talvez tenha um bug. Você pode tentar rodar este código novamente ou resetá-lo para o estado inicial. Se isto não consertá-lo, avise-nos por favor." infinite_loop_description: "O código inicial para construir o mundo nunca parou de rodar. Talvez seja muito lento ou tenha um loop infinito. Ou talvez tenha um bug. Você pode tentar rodar este código novamente ou resetá-lo para o estado inicial. Se isto não consertá-lo, avise-nos por favor."
check_dev_console: "Você também pode abrir o terminal do desenvolvedor para ver o que pode estar dando errado." check_dev_console: "Você também pode abrir o terminal do desenvolvedor para ver o que pode estar dando errado."
check_dev_console_link: "(instruções)" check_dev_console_link: "(Instruções)"
infinite_loop_try_again: "Tentar novamente" infinite_loop_try_again: "Tentar novamente"
infinite_loop_reset_level: "Resetar nível" infinite_loop_reset_level: "Resetar nível"
infinite_loop_comment_out: "Comentar Meu Código" infinite_loop_comment_out: "Comentar Meu Código"
@ -296,7 +296,7 @@ module.exports = nativeDescription: "Português do Brasil", englishDescription:
tip_scrub_shortcut: "Ctrl+[ e Ctrl+] rebobina e avança." # {change} tip_scrub_shortcut: "Ctrl+[ e Ctrl+] rebobina e avança." # {change}
tip_guide_exists: "Clique no guia no topo da página para informações úteis." tip_guide_exists: "Clique no guia no topo da página para informações úteis."
tip_open_source: "CodeCombat é 100% código aberto!" tip_open_source: "CodeCombat é 100% código aberto!"
tip_tell_friends: "Está gostando de CodeCombate? Dibulgue para os seus amigos!" tip_tell_friends: "Está gostando de CodeCombate? Divulgue para os seus amigos!"
tip_beta_launch: "CodeCombat lançou sua versão beta em outubro de 2013." tip_beta_launch: "CodeCombat lançou sua versão beta em outubro de 2013."
tip_think_solution: "Pense na solução, não no problema." tip_think_solution: "Pense na solução, não no problema."
tip_theory_practice: "Na teoria, não existe diferença entre teoria e prática. Mas, na prática, há. - Yogi Berra" tip_theory_practice: "Na teoria, não existe diferença entre teoria e prática. Mas, na prática, há. - Yogi Berra"
@ -582,15 +582,15 @@ module.exports = nativeDescription: "Português do Brasil", englishDescription:
press_paragraph_1_link: "Midia Kit" press_paragraph_1_link: "Midia Kit"
press_paragraph_1_suffix: ". Todas as logomarcas e imagens podem ser usadas sem nos contactar previamente." press_paragraph_1_suffix: ". Todas as logomarcas e imagens podem ser usadas sem nos contactar previamente."
team: "Time" team: "Time"
george_title: "CEO" # {change} george_title: "Cofundador"
george_blurb: "Administrador" george_blurb: "Administrador"
scott_title: "Programador" # {change} scott_title: "Cofundador"
scott_blurb: "O Sensato" scott_blurb: "O Sensato"
nick_title: "Programador" # {change} nick_title: "Cofundador"
nick_blurb: "Guru Motivacional" nick_blurb: "Guru Motivacional"
michael_title: "Programador" michael_title: "Programador"
michael_blurb: "Administrador de Sistemas" michael_blurb: "Administrador de Sistemas"
matt_title: "Programador" # {change} matt_title: "Cofundador"
matt_blurb: "O Ciclista" matt_blurb: "O Ciclista"
cat_title: "Chefe Artesão" cat_title: "Chefe Artesão"
cat_blurb: "Corta-vento" cat_blurb: "Corta-vento"

View file

@ -29,8 +29,8 @@ defaultTasks = [
'Release to adventurers via MailChimp.' 'Release to adventurers via MailChimp.'
'Write the description.' 'Write the description.'
'Translate the sample code comments.' 'Add i18n field for the sample code comments.'
'Add Io/Clojure/Lua/CoffeeScript.' 'Add Clojure/Lua/CoffeeScript.'
'Write the guide.' 'Write the guide.'
'Write a loading tip, if needed.' 'Write a loading tip, if needed.'
'Click the Populate i18n button.' 'Click the Populate i18n button.'
@ -40,7 +40,6 @@ defaultTasks = [
'Release to everyone via MailChimp.' 'Release to everyone via MailChimp.'
'Check completion/engagement/problem analytics.' 'Check completion/engagement/problem analytics.'
'Do any custom scripting, if needed.'
'Do thorough set decoration.' 'Do thorough set decoration.'
'Add a walkthrough video.' 'Add a walkthrough video.'
] ]

View file

@ -101,7 +101,7 @@ module.exports = class LadderPlayModal extends ModalView
{id: 'coffeescript', name: 'CoffeeScript (Experimental)'} {id: 'coffeescript', name: 'CoffeeScript (Experimental)'}
{id: 'clojure', name: 'Clojure (Experimental)'} {id: 'clojure', name: 'Clojure (Experimental)'}
{id: 'lua', name: 'Lua'} {id: 'lua', name: 'Lua'}
{id: 'io', name: 'Io (Experimental)'} #{id: 'io', name: 'Io (Experimental)'}
] ]
ctx.league = @options.league ctx.league = @options.league
teamsList = teamDataFromLevel @level teamsList = teamDataFromLevel @level

View file

@ -1,6 +1,7 @@
CocoView = require 'views/core/CocoView' CocoView = require 'views/core/CocoView'
template = require 'templates/play/common/ladder_submission' template = require 'templates/play/common/ladder_submission'
{createAetherOptions} = require 'lib/aether_utils' {createAetherOptions} = require 'lib/aether_utils'
LevelSession = require 'models/LevelSession'
module.exports = class LadderSubmissionView extends CocoView module.exports = class LadderSubmissionView extends CocoView
className: 'ladder-submission-view' className: 'ladder-submission-view'
@ -78,14 +79,20 @@ module.exports = class LadderSubmissionView extends CocoView
# Also submit the mirrorSession after the main session submits successfully. # Also submit the mirrorSession after the main session submits successfully.
mirrorAjaxData = _.clone ajaxData mirrorAjaxData = _.clone ajaxData
mirrorAjaxData.session = @mirrorSession.id mirrorAjaxData.session = @mirrorSession.id
mirrorCode = @mirrorSession.get('code')
if @session.get('team') is 'humans' if @session.get('team') is 'humans'
mirrorAjaxData.transpiledCode = 'hero-placeholder-1': transpiledCode['hero-placeholder'] mirrorAjaxData.transpiledCode = 'hero-placeholder-1': transpiledCode['hero-placeholder']
mirrorCode['hero-placeholder-1'] = @session.get('code')['hero-placeholder']
else else
mirrorAjaxData.transpiledCode = 'hero-placeholder': transpiledCode['hero-placeholder-1'] mirrorAjaxData.transpiledCode = 'hero-placeholder': transpiledCode['hero-placeholder-1']
mirrorCode['hero-placeholder'] = @session.get('code')['hero-placeholder-1']
mirrorAjaxOptions = _.clone ajaxOptions mirrorAjaxOptions = _.clone ajaxOptions
mirrorAjaxOptions.data = mirrorAjaxData mirrorAjaxOptions.data = mirrorAjaxData
ajaxOptions.success = -> ajaxOptions.success = =>
$.ajax '/queue/scoring', mirrorAjaxOptions patch = code: mirrorCode, codeLanguage: @session.get('codeLanguage'), submittedCodeLanguage: @session.get('submittedCodeLanguage')
tempSession = new LevelSession _id: @mirrorSession.id
tempSession.save patch, patch: true, type: 'PUT', success: ->
$.ajax '/queue/scoring', mirrorAjaxOptions
$.ajax '/queue/scoring', ajaxOptions $.ajax '/queue/scoring', ajaxOptions

View file

@ -101,7 +101,9 @@ module.exports = class CastButtonView extends CocoView
@casting = false @casting = false
if @hasCastOnce # Don't play this sound the first time if @hasCastOnce # Don't play this sound the first time
@playSound 'cast-end', 0.5 @playSound 'cast-end', 0.5
_.delay (=> @ladderSubmissionView?.rankSession()), 1000 if @ladderSubmissionView myHeroID = if me.team is 'ogres' then 'Hero Placeholder 1' else 'Hero Placeholder'
if @ladderSubmissionView and not e.world.thangMap[myHeroID]?.errorsOut
_.delay (=> @ladderSubmissionView?.rankSession()), 1000 if @ladderSubmissionView
@hasCastOnce = true @hasCastOnce = true
@updateCastButton() @updateCastButton()
@world = e.world @world = e.world

View file

@ -36,7 +36,7 @@ module.exports = class Spell
else else
@setLanguage 'javascript' @setLanguage 'javascript'
@useTranspiledCode = @shouldUseTranspiledCode() @useTranspiledCode = @shouldUseTranspiledCode()
console.log 'Spell', @spellKey, 'is using transpiled code (should only happen if it\'s an enemy/spectate writable method).' if @useTranspiledCode #console.log 'Spell', @spellKey, 'is using transpiled code (should only happen if it\'s an enemy/spectate writable method).' if @useTranspiledCode
@source = @originalSource @source = @originalSource
@parameters = p.parameters @parameters = p.parameters

View file

@ -113,7 +113,7 @@ module.exports = class PlayHeroesModal extends ModalView
{id: 'coffeescript', name: "CoffeeScript (#{$.i18n.t('choose_hero.experimental')})"} {id: 'coffeescript', name: "CoffeeScript (#{$.i18n.t('choose_hero.experimental')})"}
{id: 'clojure', name: "Clojure (#{$.i18n.t('choose_hero.experimental')})"} {id: 'clojure', name: "Clojure (#{$.i18n.t('choose_hero.experimental')})"}
{id: 'lua', name: 'Lua'} {id: 'lua', name: 'Lua'}
{id: 'io', name: "Io (#{$.i18n.t('choose_hero.experimental')})"} #{id: 'io', name: "Io (#{$.i18n.t('choose_hero.experimental')})"}
] ]
onHeroChanged: (e) -> onHeroChanged: (e) ->

View file

@ -0,0 +1,221 @@
// Copy sales leads from HelpScout to Close.io based on HelpScout tags
// TODO: handle leads with multiple email addresses
// TODO: some feedback email threads get broken up in Close.io
var CloseIo = function (apiKey, mailboxEmail) {
this.apiKey = apiKey;
this.mailboxEmail = mailboxEmail;
}
CloseIo.prototype.createLead = function (conversation, done) {
console.log('Close.Io - Creating lead for', conversation.customer.email);
var data = {
contacts: [{
emails: [{
email: conversation.customer.email,
type: 'office'
}],
name: conversation.customer.firstName + ' ' + conversation.customer.lastName
}]
};
var options = {
uri: 'https://' + this.apiKey+ ':X@app.close.io/api/v1/lead/',
body: JSON.stringify(data)
};
request.post(options, function (error, response, body) {
if (error) {
return done(error);
}
return done(null, JSON.parse(body));
});
}
CloseIo.prototype.getActivities = function (leadID, done) {
// console.log('Close.Io - Retrieving activities for lead', leadID);
request.get('https://' + this.apiKey + ':X@app.close.io/api/v1/activity/email/?lead_id=' + leadID, function(error, response, body) {
if (error) {
return done(error);
}
return done(null, JSON.parse(body));
});
}
CloseIo.prototype.getLead = function (conversation, done) {
// console.log('Close.Io - Retrieving contact', conversation.customer.email);
var uri = 'https://' + this.apiKey + ':X@app.close.io/api/v1/lead/?query=email_address:' + conversation.customer.email;
request.get(uri, (function(error, response, body) {
if (error) return done(error);
var leads = JSON.parse(body);
if (leads.data.length === 1) {
return done(null, leads.data[0]);
}
else if (leads.data.length > 1) {
return done('ERROR: too many leads returned for ' + conversation.customer.email + ' ' + leads.data.length);
}
this.createLead(conversation, (function(error, lead) {
if (error) return done(error);
return done(null, lead);
}).bind(this));
}).bind(this));
}
CloseIo.prototype.updateActivity = function (activities, lead, conversation, conversationThread, done) {
// console.log('Close.Io - Updating email thread', conversation.subject);
var data = {
body_html: conversationThread.body,
contact_id: lead.contacts[0].id,
date_created: conversationThread.createdAt,
lead_id: lead.id,
sender: conversationThread.createdBy.email,
_type: 'Email'
}
if (conversation.subject) {
data.subject = conversation.subject;
if (data.subject.substring(0, 4) === 'Re: ') {
data.subject = data.subject.substring(4);
}
}
if (conversationThread.createdBy.email === this.mailboxEmail) {
data.status = 'sent';
data.to = [conversationThread.customer.email];
}
else {
data.status = 'inbox';
data.to = [this.mailboxEmail];
}
for (var i = 0; i < activities.data.length; i++) {
if (activities.data[i].body_html === data.body_html
&& new Date(activities.data[i].date_created).getTime() == new Date(data.date_created).getTime()) {
// console.log('Close.Io - Found existing email', data.subject, data.date_created);
return done();
}
}
var options = {
uri: 'https://' + this.apiKey + ':X@app.close.io/api/v1/activity/email/',
body: JSON.stringify(data)
};
request.post(options, function (error, response, body) {
if (error) {
return done(error);
}
return done();
});
}
CloseIo.prototype.updateLead = function (conversation, done) {
console.log('Close.Io - Updating lead', conversation.customer.email);
this.getLead(conversation, (function(error, lead) {
if (error) return done(error);
this.updateLeadEmails(lead, conversation, (function(error) {
if (error) {
console.log(error);
return;
}
}).bind(this));
}).bind(this));
}
CloseIo.prototype.updateLeadEmails = function (lead, conversation, done) {
console.log('Close.Io - Updating lead emails', lead.display_name, conversation.subject);
if (conversation.type !== 'email') return done();
this.getActivities(lead.id, (function(error, activities) {
if (error) return done(error);
for (var i = 0; i < conversation.threads.length; i++) {
if (conversation.threads[i].type !== 'message') continue;
if (conversation.threads[i].state !== 'published') continue;
if (!conversation.threads[i].body || conversation.threads[i].body.length === 0) continue;
this.updateActivity(activities, lead, conversation, conversation.threads[i], done);
}
}).bind(this));
}
var HelpScout = function (apiKey, mailboxEmails, searchTag) {
this.apiKey = apiKey;
this.mailboxEmails = mailboxEmails;
this.searchTag = searchTag;
}
HelpScout.prototype.getConversation = function (conversationId, done) {
// console.log('HelpScout - Retrieving conversation', conversationId);
request.get('https://' + this.apiKey + ':X@api.helpscout.net/v1/conversations/' + conversationId + '.json', function (error, response, body) {
if (error) return done(error);
var conversation = JSON.parse(body);
return done(null, conversation.item);
});
}
HelpScout.prototype.getConversations = function (mailboxId, done) {
// console.log('HelpScout - Retrieving conversations for mailbox', mailboxId);
var results = [];
var fetchPage = (function (page) {
// console.error("HelpScout - Fetching conversations page", page);
var uri = 'https://' + this.apiKey + ':X@api.helpscout.net/v1/mailboxes/' + mailboxId + '/conversations.json'
uri += '?page=' + page + '&tag=' + this.searchTag;
request.get(uri, function (error, response, body) {
if (error) return done(error);
var conversations = JSON.parse(body);
results = results.concat(conversations.items);
if (conversations.page < conversations.pages) {
return fetchPage(page + 1);
}
return done(null, results);
});
}).bind(this);
fetchPage(1);
}
HelpScout.prototype.getMailboxes = function (done) {
// console.log('HelpScout - Retrieving mailboxes');
var results = [];
request.get('https://' + this.apiKey + ':X@api.helpscout.net/v1/mailboxes.json', (function (error, response, body) {
if (error) return done(error);
var mailboxes = JSON.parse(body);
for (var i = 0 ; i < mailboxes.items.length; i++) {
if (this.mailboxEmails.indexOf(mailboxes.items[i].email) >= 0) {
results.push(mailboxes.items[i]);
}
}
return done(null, results);
}).bind(this));
}
// Main program
if (process.argv.length !== 4) {
log("Usage: node <script> <HelpScout API key> <Close.io API key>");
process.exit();
}
var request = require('request');
var helpScout = new HelpScout(process.argv[2], ['support@codecombat.com', 'team@codecombat.com'], 'make the sale');
var closeIo = new CloseIo(process.argv[3], 'matt@codecombat.com');
helpScout.getMailboxes(function (error, mailboxes) {
if (error) {
console.log(error);
return;
}
for (var i = 0; i < mailboxes.length; i++) {
var mailbox = mailboxes[i];
helpScout.getConversations(mailbox.id, function(error, conversations) {
if (error) {
console.log(error);
return;
}
console.log(mailbox.email, 'mailbox has', conversations.length, 'conversations');
for (var i = 0; i < conversations.length; i++) {
if (conversations[i].type !== 'email') continue;
if (i > 8) {
console.log('TODO: process all the conversations');
break;
}
helpScout.getConversation(conversations[i].id, function(error, conversation) {
if (error) {
console.log(error);
return;
}
closeIo.updateLead(conversation, function(error, lead) {
if (error) {
console.log(error);
return;
}
});
});
}
});
}
});

View file

@ -17,7 +17,7 @@ sessionSelectionString = 'team totalScore transpiledCode submittedCodeLanguage t
sendSessionsResponse = (res) -> sendSessionsResponse = (res) ->
(err, sessions) -> (err, sessions) ->
if err then return errors.serverError res, "Couldn't get two games to simulate: #{err}" if err then return errors.serverError res, "Couldn't get two games to simulate: #{err}"
unless sessions.length is 2 unless _.filter(sessions).length is 2
console.log 'No games to score.', sessions.length console.log 'No games to score.', sessions.length
res.send 204, 'No games to score.' res.send 204, 'No games to score.'
return res.end() return res.end()
@ -110,5 +110,3 @@ findEarliestSubmission = (queryParams, callback) ->
return callback err if err return callback err if err
result = earliestSubmissionCache[cacheKey] = earliest?.submitDate result = earliestSubmissionCache[cacheKey] = earliest?.submitDate
callback null, result callback null, result