From bd21933cea2dbd3b047aa848b427dc816c36e151 Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Sat, 17 Feb 2018 17:15:14 +0900 Subject: [PATCH 01/31] Quest started translation support --- website/common/locales/en/groups.json | 3 ++- website/server/models/group.js | 26 +++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/website/common/locales/en/groups.json b/website/common/locales/en/groups.json index e98f66076a..7255682437 100644 --- a/website/common/locales/en/groups.json +++ b/website/common/locales/en/groups.json @@ -429,5 +429,6 @@ "worldBossBullet1": "Complete tasks to damage the World Boss", "worldBossBullet2": "The World Boss won’t damage you for missed tasks, but its Rage meter will go up. If the bar fills up, the Boss will attack one of Habitica’s shopkeepers!", "worldBossBullet3": "You can continue with normal Quest Bosses, damage will apply to both", - "worldBossBullet4": "Check the Tavern regularly to see World Boss progress and Rage attacks" + "worldBossBullet4": "Check the Tavern regularly to see World Boss progress and Rage attacks", + "chatQuestStarted": "Your quest, <%= questName %>, has started." } diff --git a/website/server/models/group.js b/website/server/models/group.js index 49006ac0e3..d0dab7eef7 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -317,11 +317,27 @@ schema.statics.getGroups = async function getGroups (options = {}) { return groupsArray; }; +function translateSystemMessages(group, user) { + for (let i = 0; i < group.chat.length; i++) { + if (_.isEmpty(group.chat[i].info)) { + continue; + } + switch (group.chat[i].info.type) { + case 'quest_start': + group.chat[i].text = '`' + shared.i18n.t('chatQuestStarted', {'questName': questScrolls[group.chat[i].info.quest].text(user.preferences.language)}, user.preferences.language) + '`'; + break; + } + } + return group; +} + // When converting to json remove chat messages with more than 1 flag and remove all flags info // unless the user is an admin or said chat is posted by that user // Not putting into toJSON because there we can't access user // It also removes the _meta field that can be stored inside a chat message schema.statics.toJSONCleanChat = function groupToJSONCleanChat (group, user) { + group = translateSystemMessages(group, user); + let toJSON = group.toJSON(); if (!user.contributor.admin) { @@ -442,10 +458,11 @@ schema.methods.isMember = function isGroupMember (user) { } }; -export function chatDefaults (msg, user) { +export function chatDefaults (msg, user, info={}) { let message = { id: shared.uuid(), text: msg, + info: info, timestamp: Number(new Date()), likes: {}, flags: {}, @@ -509,8 +526,8 @@ function setUserStyles (newMessage, user) { newMessage.userStyles = userStyles; } -schema.methods.sendChat = function sendChat (message, user, metaData) { - let newMessage = chatDefaults(message, user); +schema.methods.sendChat = function sendChat (message, user, metaData, info={}) { + let newMessage = chatDefaults(message, user, info); if (user) setUserStyles(newMessage, user); @@ -696,6 +713,9 @@ schema.methods.startQuest = async function startQuest (user) { }); this.sendChat(`\`Your quest, ${quest.text('en')}, has started.\``, null, { participatingMembers: this.getParticipatingQuestMembers().join(', '), + }, { + 'type': 'quest_start', + 'quest': quest.key, }); }; From 34d37cefcca85341929ca03b8130d3418a13785d Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Sat, 17 Feb 2018 19:41:15 +0900 Subject: [PATCH 02/31] Boss damage messages translation support --- website/common/locales/en/groups.json | 10 +++- website/server/controllers/api-v3/chat.js | 6 +-- website/server/controllers/api-v3/groups.js | 6 +-- website/server/models/group.js | 55 +++++++++++++++++++-- 4 files changed, 65 insertions(+), 12 deletions(-) diff --git a/website/common/locales/en/groups.json b/website/common/locales/en/groups.json index 7255682437..b33eaa64bd 100644 --- a/website/common/locales/en/groups.json +++ b/website/common/locales/en/groups.json @@ -430,5 +430,13 @@ "worldBossBullet2": "The World Boss won’t damage you for missed tasks, but its Rage meter will go up. If the bar fills up, the Boss will attack one of Habitica’s shopkeepers!", "worldBossBullet3": "You can continue with normal Quest Bosses, damage will apply to both", "worldBossBullet4": "Check the Tavern regularly to see World Boss progress and Rage attacks", - "chatQuestStarted": "Your quest, <%= questName %>, has started." + "chatQuestStarted": "Your quest, <%= questName %>, has started.", + "chatBossDamage": "<%= username %> attacks <%= bossName %> for <%= userDamage %> damage. <%= bossName %> attacks party for <%= bossDamage %> damage.", + "chatBossDontAttack": "<%= bossDamage %> does not attack, because it respects the fact that there are some bugs post-maintenance and it doesn't want to hurt anyone unfairly. It will continue its rampage soon!", + "chatBossDefeated": "You defeated <%= bossName %>! Questing party members receive the rewards of victory.", + "chatFindItems": "<%= username %> found <%= itemName %>.", + "chatItemQuestFinish": "All items found! Party has received their rewards.", + "chatCastSpell": "<%= username %> casts <%= spellName %> <%= targetText %>.", + "chatQuestAborted": "<%= username %> aborted the party quest <%= questName %>.", + "tavernBossTired": "<%= bossName %> tries to unleash ${quest.boss.rage.title('en')} but is too tired." } diff --git a/website/server/controllers/api-v3/chat.js b/website/server/controllers/api-v3/chat.js index fba5277d49..19cf4a53ce 100644 --- a/website/server/controllers/api-v3/chat.js +++ b/website/server/controllers/api-v3/chat.js @@ -74,7 +74,7 @@ api.getChat = { let group = await Group.getGroup({user, groupId: req.params.groupId, fields: 'chat'}); if (!group) throw new NotFound(res.t('groupNotFound')); - res.respond(200, Group.toJSONCleanChat(group, user).chat); + res.respond(200, await Group.toJSONCleanChat(group, user).chat); }, }; @@ -194,7 +194,7 @@ api.postChat = { } if (chatUpdated) { - res.respond(200, {chat: Group.toJSONCleanChat(savedGroup, user).chat}); + res.respond(200, {chat: await Group.toJSONCleanChat(savedGroup, user).chat}); } else { res.respond(200, {message: savedGroup.chat[0]}); } @@ -486,7 +486,7 @@ api.deleteChat = { ).exec(); if (chatUpdated) { - let chatRes = Group.toJSONCleanChat(group, user).chat; + let chatRes = await Group.toJSONCleanChat(group, user).chat; removeFromArray(chatRes, {id: chatId}); res.respond(200, chatRes); } else { diff --git a/website/server/controllers/api-v3/groups.js b/website/server/controllers/api-v3/groups.js index ae7c8c1234..59cd767e85 100644 --- a/website/server/controllers/api-v3/groups.js +++ b/website/server/controllers/api-v3/groups.js @@ -391,7 +391,7 @@ api.getGroup = { throw new NotFound(res.t('groupNotFound')); } - let groupJson = Group.toJSONCleanChat(group, user); + let groupJson = await Group.toJSONCleanChat(group, user); if (groupJson.leader === user._id) { groupJson.purchased.plan = group.purchased.plan.toObject(); @@ -455,7 +455,7 @@ api.updateGroup = { _.assign(group, _.merge(group.toObject(), Group.sanitizeUpdate(req.body))); let savedGroup = await group.save(); - let response = Group.toJSONCleanChat(savedGroup, user); + let response = await Group.toJSONCleanChat(savedGroup, user); // If the leader changed fetch new data, otherwise use authenticated user if (response.leader !== user._id) { @@ -619,7 +619,7 @@ api.joinGroup = { promises = await Bluebird.all(promises); - let response = Group.toJSONCleanChat(promises[0], user); + let response = await Group.toJSONCleanChat(promises[0], user); let leader = await User.findById(response.leader).select(nameFields).exec(); if (leader) { response.leader = leader.toJSON({minimize: true}); diff --git a/website/server/models/group.js b/website/server/models/group.js index d0dab7eef7..38dd1e3d1d 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -317,7 +317,8 @@ schema.statics.getGroups = async function getGroups (options = {}) { return groupsArray; }; -function translateSystemMessages(group, user) { +async function translateSystemMessages(group, user) { + let usernames = {}; for (let i = 0; i < group.chat.length; i++) { if (_.isEmpty(group.chat[i].info)) { continue; @@ -326,6 +327,42 @@ function translateSystemMessages(group, user) { case 'quest_start': group.chat[i].text = '`' + shared.i18n.t('chatQuestStarted', {'questName': questScrolls[group.chat[i].info.quest].text(user.preferences.language)}, user.preferences.language) + '`'; break; + case 'boss_damage': + if (!_.has(usernames, group.chat[i].info.user)) { + let user = await User + .findById(group.chat[i].info.user) + .select(nameFields) + .exec(); + usernames[group.chat[i].info.user] = user.profile.name; + } + group.chat[i].text = '`' + shared.i18n.t('chatBossDamage', {'username': usernames[group.chat[i].info.user], 'bossName': questScrolls[group.chat[i].info.quest].boss.name(user.preferences.language), 'userDamage': group.chat[i].info.userDamage, 'bossDamage': group.chat[i].info.bossDamage}, user.preferences.language) + '`'; + break; + case 'boss_dont_attack': + break; + case 'boss_rage': + group.chat[i].text = '`' + questScrolls[group.chat[i].info.quest].boss.rage.effect(user.preferences.language) + '`'; + break; + case 'boss_defeated': + break; + case 'user_found_items': + break; + case 'all_items_found': + break; + case 'spell_cast': + break; + case 'quest_abort': + break; + case 'tavern_quest_completed': + group.chat[i].text = '`' + questScrolls[group.chat[i].info.quest].completionChat(user.preferences.language) + '`'; + break; + case 'tavern_boss_rage_tired': + break; + case 'tavern_boss_rage': + group.chat[i].text = '`' + questScrolls[group.chat[i].info.quest].boss.rage[group.chat[i].info.scene](user.preferences.language) + '`'; + break; + case 'tavern_boss_desperation': + group.chat[i].text = '`' + questScrolls[group.chat[i].info.quest].boss.desperation.text(user.preferences.language) + '`'; + break; } } return group; @@ -335,8 +372,8 @@ function translateSystemMessages(group, user) { // unless the user is an admin or said chat is posted by that user // Not putting into toJSON because there we can't access user // It also removes the _meta field that can be stored inside a chat message -schema.statics.toJSONCleanChat = function groupToJSONCleanChat (group, user) { - group = translateSystemMessages(group, user); +schema.statics.toJSONCleanChat = async function groupToJSONCleanChat (group, user) { + group = await translateSystemMessages(group, user); let toJSON = group.toJSON(); @@ -907,8 +944,16 @@ schema.methods._processBossQuest = async function processBossQuest (options) { // TODO Create a party preferred language option so emits like this can be localized. Suggestion: Always display the English version too. Or, if English is not displayed to the players, at least include it in a new field in the chat object that's visible in the database - essential for admins when troubleshooting quests! let playerAttack = `${user.profile.name} attacks ${quest.boss.name('en')} for ${progress.up.toFixed(1)} damage.`; let bossAttack = CRON_SAFE_MODE || CRON_SEMI_SAFE_MODE ? `${quest.boss.name('en')} does not attack, because it respects the fact that there are some bugs\` \`post-maintenance and it doesn't want to hurt anyone unfairly. It will continue its rampage soon!` : `${quest.boss.name('en')} attacks party for ${Math.abs(down).toFixed(1)} damage.`; - // TODO Consider putting the safe mode boss attack message in an ENV var - group.sendChat(`\`${playerAttack}\` \`${bossAttack}\``); + if (CRON_SAFE_MODE || CRON_SEMI_SAFE_MODE) { + } else { + group.sendChat(`\`${playerAttack}\` \`${bossAttack}\``, null, null, { + 'type': 'boss_damage', + 'user': user._id, + 'quest': group.quest.key, + 'userDamage': progress.up.toFixed(1), + 'bossDamage': Math.abs(down).toFixed(1), + }); + } // If boss has Rage, increment Rage as well if (quest.boss.rage) { From 14798ced829629b8b5554e5616b261e6b89ff2d2 Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Sat, 17 Feb 2018 22:17:08 +0900 Subject: [PATCH 03/31] Translation support for a lot of messages in party --- website/common/locales/en/groups.json | 7 +- website/server/controllers/api-v3/quests.js | 6 +- website/server/controllers/api-v3/user.js | 17 ++++- website/server/models/group.js | 71 +++++++++++++++++++-- 4 files changed, 92 insertions(+), 9 deletions(-) diff --git a/website/common/locales/en/groups.json b/website/common/locales/en/groups.json index b33eaa64bd..ef440ec914 100644 --- a/website/common/locales/en/groups.json +++ b/website/common/locales/en/groups.json @@ -432,11 +432,12 @@ "worldBossBullet4": "Check the Tavern regularly to see World Boss progress and Rage attacks", "chatQuestStarted": "Your quest, <%= questName %>, has started.", "chatBossDamage": "<%= username %> attacks <%= bossName %> for <%= userDamage %> damage. <%= bossName %> attacks party for <%= bossDamage %> damage.", - "chatBossDontAttack": "<%= bossDamage %> does not attack, because it respects the fact that there are some bugs post-maintenance and it doesn't want to hurt anyone unfairly. It will continue its rampage soon!", + "chatBossDontAttack": "<%= bossName %> does not attack, because it respects the fact that there are some bugs post-maintenance and it doesn't want to hurt anyone unfairly. It will continue its rampage soon!", "chatBossDefeated": "You defeated <%= bossName %>! Questing party members receive the rewards of victory.", - "chatFindItems": "<%= username %> found <%= itemName %>.", + "chatFindItems": "<%= username %> found <%= items %>.", "chatItemQuestFinish": "All items found! Party has received their rewards.", - "chatCastSpell": "<%= username %> casts <%= spellName %> <%= targetText %>.", + "chatCastSpellParty": "<%= username %> casts <%= spellName %> for the party.", + "chatCastSpellUser": "<%= username %> casts <%= spellName %> on <%= target %>.", "chatQuestAborted": "<%= username %> aborted the party quest <%= questName %>.", "tavernBossTired": "<%= bossName %> tries to unleash ${quest.boss.rage.title('en')} but is too tired." } diff --git a/website/server/controllers/api-v3/quests.js b/website/server/controllers/api-v3/quests.js index 8d4bd71992..678c64d55e 100644 --- a/website/server/controllers/api-v3/quests.js +++ b/website/server/controllers/api-v3/quests.js @@ -422,7 +422,11 @@ api.abortQuest = { if (user._id !== group.leader && user._id !== group.quest.leader) throw new NotAuthorized(res.t('onlyLeaderAbortQuest')); let questName = questScrolls[group.quest.key].text('en'); - group.sendChat(`\`${user.profile.name} aborted the party quest ${questName}.\``); + group.sendChat(`\`${user.profile.name} aborted the party quest ${questName}.\``, null, null, { + 'type': 'quest_abort', + 'user': user._id, + 'quest': group.quest.key, + }); let memberUpdates = User.update({ 'party._id': groupId, diff --git a/website/server/controllers/api-v3/user.js b/website/server/controllers/api-v3/user.js index 70721cfa54..1805f6c25c 100644 --- a/website/server/controllers/api-v3/user.js +++ b/website/server/controllers/api-v3/user.js @@ -735,7 +735,22 @@ api.castSpell = { if (party && !spell.silent) { let message = `\`${user.profile.name} casts ${spell.text()}${targetType === 'user' ? ` on ${partyMembers.profile.name}` : ' for the party'}.\``; - party.sendChat(message); + if (targetType === 'user') { + party.sendChat(message, null, null, { + 'type': 'spell_cast_user', + 'user': user._id, + 'class': klass, + 'spell': spellId, + 'target': partyMember._id, + }); + } else { + party.sendChat(message, null, null, { + 'type': 'spell_cast_party', + 'user': user._id, + 'class': klass, + 'spell': spellId, + }); + } await party.save(); } } diff --git a/website/server/models/group.js b/website/server/models/group.js index 38dd1e3d1d..d84dcd39b4 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -338,19 +338,68 @@ async function translateSystemMessages(group, user) { group.chat[i].text = '`' + shared.i18n.t('chatBossDamage', {'username': usernames[group.chat[i].info.user], 'bossName': questScrolls[group.chat[i].info.quest].boss.name(user.preferences.language), 'userDamage': group.chat[i].info.userDamage, 'bossDamage': group.chat[i].info.bossDamage}, user.preferences.language) + '`'; break; case 'boss_dont_attack': + group.chat[i].text = '`' + shared.i18n.t('chatBossDontAttack', {'bossName': questScrolls[group.chat[i].info.quest].boss.name(user.preferences.language)}, user.preferences.language) + '`'; break; case 'boss_rage': group.chat[i].text = '`' + questScrolls[group.chat[i].info.quest].boss.rage.effect(user.preferences.language) + '`'; break; case 'boss_defeated': + group.chat[i].text = '`' + shared.i18n.t('chatBossDefeated', {'bossName': questScrolls[group.chat[i].info.quest].boss.name(user.preferences.language)}, user.preferences.language) + '`'; break; case 'user_found_items': + if (!_.has(usernames, group.chat[i].info.user)) { + let user = await User + .findById(group.chat[i].info.user) + .select(nameFields) + .exec(); + usernames[group.chat[i].info.user] = user.profile.name; + } + let foundText = _.reduce(group.chat[i].info.items, (m, v, k) => { + m.push(`${v} ${questScrolls[group.chat[i].info.quest].collect[k].text(user.preferences.language)}`); + return m; + }, []); + foundText = foundText.join(', '); + group.chat[i].text = '`' + shared.i18n.t('chatFindItems', {'username': usernames[group.chat[i].info.user], 'items': foundText}, user.preferences.language) + '`'; break; case 'all_items_found': + group.chat[i].text = '`' + shared.i18n.t('chatItemQuestFinish', user.preferences.language) + '`'; break; - case 'spell_cast': + case 'spell_cast_party': + if (!_.has(usernames, group.chat[i].info.user)) { + let user = await User + .findById(group.chat[i].info.user) + .select(nameFields) + .exec(); + usernames[group.chat[i].info.user] = user.profile.name; + } + group.chat[i].text = '`' + shared.i18n.t('chatCastSpellParty', {'username': usernames[group.chat[i].info.user], 'spellName': shared.content.spells[group.chat[i].info.class][group.chat[i].info.spell].text(user.preferences.language)}, user.preferences.language) + '`'; + break; + case 'spell_cast_user': + if (!_.has(usernames, group.chat[i].info.user)) { + let user = await User + .findById(group.chat[i].info.user) + .select(nameFields) + .exec(); + usernames[group.chat[i].info.user] = user.profile.name; + } + if (!_.has(usernames, group.chat[i].info.target)) { + let target = await User + .findById(group.chat[i].info.target) + .select(nameFields) + .exec(); + usernames[group.chat[i].info.target] = user.profile.name; + } + group.chat[i].text = '`' + shared.i18n.t('chatCastSpellUser', {'username': usernames[group.chat[i].info.user], 'spellName': shared.content.spells[group.chat[i].info.class][group.chat[i].info.spell].text(user.preferences.language), 'target': usernames[group.chat[i].info.target]}, user.preferences.language) + '`'; break; case 'quest_abort': + if (!_.has(usernames, group.chat[i].info.user)) { + let user = await User + .findById(group.chat[i].info.user) + .select(nameFields) + .exec(); + usernames[group.chat[i].info.user] = user.profile.name; + } + group.chat[i].text = '`' + shared.i18n.t('chatQuestAborted', {'username': usernames[group.chat[i].info.user], 'questName': questScrolls[group.chat[i].info.quest].text(user.preferences.language)}, user.preferences.language) + '`'; break; case 'tavern_quest_completed': group.chat[i].text = '`' + questScrolls[group.chat[i].info.quest].completionChat(user.preferences.language) + '`'; @@ -945,6 +994,10 @@ schema.methods._processBossQuest = async function processBossQuest (options) { let playerAttack = `${user.profile.name} attacks ${quest.boss.name('en')} for ${progress.up.toFixed(1)} damage.`; let bossAttack = CRON_SAFE_MODE || CRON_SEMI_SAFE_MODE ? `${quest.boss.name('en')} does not attack, because it respects the fact that there are some bugs\` \`post-maintenance and it doesn't want to hurt anyone unfairly. It will continue its rampage soon!` : `${quest.boss.name('en')} attacks party for ${Math.abs(down).toFixed(1)} damage.`; if (CRON_SAFE_MODE || CRON_SEMI_SAFE_MODE) { + group.sendChat(`\`${playerAttack}\` \`${bossAttack}\``, null, null, { + 'type': 'boss_dont_attack', + 'quest': group.quest.key, + }); } else { group.sendChat(`\`${playerAttack}\` \`${bossAttack}\``, null, null, { 'type': 'boss_damage', @@ -986,7 +1039,10 @@ schema.methods._processBossQuest = async function processBossQuest (options) { // Boss slain, finish quest if (group.quest.progress.hp <= 0) { - group.sendChat(`\`You defeated ${quest.boss.name('en')}! Questing party members receive the rewards of victory.\``); + group.sendChat(`\`You defeated ${quest.boss.name('en')}! Questing party members receive the rewards of victory.\``, null, null, { + 'type': 'boss_defeated', + 'quest': quest.key, + }); // Participants: Grant rewards & achievements, finish quest await group.finishQuest(shared.content.quests[group.quest.key]); @@ -1037,7 +1093,12 @@ schema.methods._processCollectionQuest = async function processCollectionQuest ( }, []); foundText = foundText.join(', '); - group.sendChat(`\`${user.profile.name} found ${foundText}.\``); + group.sendChat(`\`${user.profile.name} found ${foundText}.\``, null, null, { + 'type': 'user_found_items', + 'user': user._id, + 'quest': quest.key, + 'items': itemsFound, + }); group.markModified('quest.progress.collect'); // Still needs completing @@ -1046,7 +1107,9 @@ schema.methods._processCollectionQuest = async function processCollectionQuest ( })) return await group.save(); await group.finishQuest(quest); - group.sendChat('`All items found! Party has received their rewards.`'); + group.sendChat('`All items found! Party has received their rewards.`', null, null, { + 'type': 'all_items_found', + }); return await group.save(); }; From 6477801d3e39c4ae200ae2d0298e2c9b67c46852 Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Sun, 18 Feb 2018 15:26:27 +0900 Subject: [PATCH 04/31] Translation support for missing sendChat calls --- website/server/models/group.js | 78 +++++++++++++++------------------- 1 file changed, 35 insertions(+), 43 deletions(-) diff --git a/website/server/models/group.js b/website/server/models/group.js index d84dcd39b4..71603198e5 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -323,18 +323,18 @@ async function translateSystemMessages(group, user) { if (_.isEmpty(group.chat[i].info)) { continue; } + if (_.has(group.chat[i].info, 'user') && !_.has(usernames, group.chat[i].info.user)) { + let user = await User + .findById(group.chat[i].info.user) + .select(nameFields) + .exec(); + usernames[group.chat[i].info.user] = user.profile.name; + } switch (group.chat[i].info.type) { case 'quest_start': group.chat[i].text = '`' + shared.i18n.t('chatQuestStarted', {'questName': questScrolls[group.chat[i].info.quest].text(user.preferences.language)}, user.preferences.language) + '`'; break; case 'boss_damage': - if (!_.has(usernames, group.chat[i].info.user)) { - let user = await User - .findById(group.chat[i].info.user) - .select(nameFields) - .exec(); - usernames[group.chat[i].info.user] = user.profile.name; - } group.chat[i].text = '`' + shared.i18n.t('chatBossDamage', {'username': usernames[group.chat[i].info.user], 'bossName': questScrolls[group.chat[i].info.quest].boss.name(user.preferences.language), 'userDamage': group.chat[i].info.userDamage, 'bossDamage': group.chat[i].info.bossDamage}, user.preferences.language) + '`'; break; case 'boss_dont_attack': @@ -347,13 +347,6 @@ async function translateSystemMessages(group, user) { group.chat[i].text = '`' + shared.i18n.t('chatBossDefeated', {'bossName': questScrolls[group.chat[i].info.quest].boss.name(user.preferences.language)}, user.preferences.language) + '`'; break; case 'user_found_items': - if (!_.has(usernames, group.chat[i].info.user)) { - let user = await User - .findById(group.chat[i].info.user) - .select(nameFields) - .exec(); - usernames[group.chat[i].info.user] = user.profile.name; - } let foundText = _.reduce(group.chat[i].info.items, (m, v, k) => { m.push(`${v} ${questScrolls[group.chat[i].info.quest].collect[k].text(user.preferences.language)}`); return m; @@ -365,23 +358,9 @@ async function translateSystemMessages(group, user) { group.chat[i].text = '`' + shared.i18n.t('chatItemQuestFinish', user.preferences.language) + '`'; break; case 'spell_cast_party': - if (!_.has(usernames, group.chat[i].info.user)) { - let user = await User - .findById(group.chat[i].info.user) - .select(nameFields) - .exec(); - usernames[group.chat[i].info.user] = user.profile.name; - } group.chat[i].text = '`' + shared.i18n.t('chatCastSpellParty', {'username': usernames[group.chat[i].info.user], 'spellName': shared.content.spells[group.chat[i].info.class][group.chat[i].info.spell].text(user.preferences.language)}, user.preferences.language) + '`'; break; case 'spell_cast_user': - if (!_.has(usernames, group.chat[i].info.user)) { - let user = await User - .findById(group.chat[i].info.user) - .select(nameFields) - .exec(); - usernames[group.chat[i].info.user] = user.profile.name; - } if (!_.has(usernames, group.chat[i].info.target)) { let target = await User .findById(group.chat[i].info.target) @@ -392,25 +371,22 @@ async function translateSystemMessages(group, user) { group.chat[i].text = '`' + shared.i18n.t('chatCastSpellUser', {'username': usernames[group.chat[i].info.user], 'spellName': shared.content.spells[group.chat[i].info.class][group.chat[i].info.spell].text(user.preferences.language), 'target': usernames[group.chat[i].info.target]}, user.preferences.language) + '`'; break; case 'quest_abort': - if (!_.has(usernames, group.chat[i].info.user)) { - let user = await User - .findById(group.chat[i].info.user) - .select(nameFields) - .exec(); - usernames[group.chat[i].info.user] = user.profile.name; - } group.chat[i].text = '`' + shared.i18n.t('chatQuestAborted', {'username': usernames[group.chat[i].info.user], 'questName': questScrolls[group.chat[i].info.quest].text(user.preferences.language)}, user.preferences.language) + '`'; break; case 'tavern_quest_completed': - group.chat[i].text = '`' + questScrolls[group.chat[i].info.quest].completionChat(user.preferences.language) + '`'; + group.chat[i].text = '`' + shared.content.quests[group.chat[i].info.quest].completionChat(user.preferences.language) + '`'; break; case 'tavern_boss_rage_tired': + group.chat[i].text = '`' + shared.i18n.t('tavernBossTired', {'username': usernames[group.chat[i].info.user], 'questName': shared.content.quests[group.chat[i].info.quest].text(user.preferences.language)}, user.preferences.language) + '`'; break; case 'tavern_boss_rage': - group.chat[i].text = '`' + questScrolls[group.chat[i].info.quest].boss.rage[group.chat[i].info.scene](user.preferences.language) + '`'; + group.chat[i].text = '`' + shared.content.quests[group.chat[i].info.quest].boss.rage[group.chat[i].info.scene](user.preferences.language) + '`'; + break; + case 'tavern_boss_rage_effect': + group.chat[i].text = '`' + shared.content.quests[group.chat[i].info.quest].boss.rage.effect(user.preferences.language) + '`'; break; case 'tavern_boss_desperation': - group.chat[i].text = '`' + questScrolls[group.chat[i].info.quest].boss.desperation.text(user.preferences.language) + '`'; + group.chat[i].text = '`' + shared.content.quests[group.chat[i].info.quest].boss.desperation.text(user.preferences.language) + '`'; break; } } @@ -1012,7 +988,10 @@ schema.methods._processBossQuest = async function processBossQuest (options) { if (quest.boss.rage) { group.quest.progress.rage += Math.abs(down); if (group.quest.progress.rage >= quest.boss.rage.value) { - group.sendChat(quest.boss.rage.effect('en')); + group.sendChat(quest.boss.rage.effect('en'), null, null, { + 'type': 'tavern_boss_rage_effect', + 'quest': quest.key, + }); group.quest.progress.rage = 0; // TODO To make Rage effects more expandable, let's turn these into functions in quest.boss.rage @@ -1166,7 +1145,10 @@ schema.statics.tavernBoss = async function tavernBoss (user, progress) { let quest = shared.content.quests[tavern.quest.key]; if (tavern.quest.progress.hp <= 0) { - tavern.sendChat(quest.completionChat('en')); + tavern.sendChat(quest.completionChat('en'), null, null, { + 'type': 'tavern_quest_completed', + 'quest': quest.key, + }); await tavern.finishQuest(quest); _.assign(tavernQuest, {extra: null}); return tavern.save(); @@ -1194,10 +1176,17 @@ schema.statics.tavernBoss = async function tavernBoss (user, progress) { } if (!scene) { - tavern.sendChat(`\`${quest.boss.name('en')} tries to unleash ${quest.boss.rage.title('en')} but is too tired.\``); + tavern.sendChat(`\`${quest.boss.name('en')} tries to unleash ${quest.boss.rage.title('en')} but is too tired.\``, null, null, { + 'type': 'tavern_boss_rage_tired', + 'quest': quest.key, + }); tavern.quest.progress.rage = 0; // quest.boss.rage.value; } else { - tavern.sendChat(quest.boss.rage[scene]('en')); + tavern.sendChat(quest.boss.rage[scene]('en'), null, null, { + 'type': 'tavern_boss_rage', + 'quest': quest.key, + 'scene': scene, + }); tavern.quest.extra.worldDmg[scene] = true; tavern.quest.extra.worldDmg.recent = scene; tavern.markModified('quest.extra.worldDmg'); @@ -1209,7 +1198,10 @@ schema.statics.tavernBoss = async function tavernBoss (user, progress) { } if (quest.boss.desperation && tavern.quest.progress.hp < quest.boss.desperation.threshold && !tavern.quest.extra.desperate) { - tavern.sendChat(quest.boss.desperation.text('en')); + tavern.sendChat(quest.boss.desperation.text('en'), null, null, { + 'type': 'tavern_boss_desperation', + 'quest': quest.key, + }); tavern.quest.extra.desperate = true; tavern.quest.extra.def = quest.boss.desperation.def; tavern.quest.extra.str = quest.boss.desperation.str; From 3171550de2a7363cef91b0d2b0cc774c04e06d6d Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Sun, 18 Feb 2018 17:14:18 +0900 Subject: [PATCH 05/31] Fix syntax problems found when running tests --- website/server/controllers/api-v3/quests.js | 6 +- website/server/controllers/api-v3/user.js | 18 +- website/server/models/group.js | 197 ++++++++++---------- 3 files changed, 111 insertions(+), 110 deletions(-) diff --git a/website/server/controllers/api-v3/quests.js b/website/server/controllers/api-v3/quests.js index 678c64d55e..1f003f6865 100644 --- a/website/server/controllers/api-v3/quests.js +++ b/website/server/controllers/api-v3/quests.js @@ -423,9 +423,9 @@ api.abortQuest = { let questName = questScrolls[group.quest.key].text('en'); group.sendChat(`\`${user.profile.name} aborted the party quest ${questName}.\``, null, null, { - 'type': 'quest_abort', - 'user': user._id, - 'quest': group.quest.key, + type: 'quest_abort', + user: user._id, + quest: group.quest.key, }); let memberUpdates = User.update({ diff --git a/website/server/controllers/api-v3/user.js b/website/server/controllers/api-v3/user.js index 1805f6c25c..3cdb1b9b6b 100644 --- a/website/server/controllers/api-v3/user.js +++ b/website/server/controllers/api-v3/user.js @@ -737,18 +737,18 @@ api.castSpell = { let message = `\`${user.profile.name} casts ${spell.text()}${targetType === 'user' ? ` on ${partyMembers.profile.name}` : ' for the party'}.\``; if (targetType === 'user') { party.sendChat(message, null, null, { - 'type': 'spell_cast_user', - 'user': user._id, - 'class': klass, - 'spell': spellId, - 'target': partyMember._id, + type: 'spell_cast_user', + user: user._id, + class: klass, + spell: spellId, + target: partyMembers._id, }); } else { party.sendChat(message, null, null, { - 'type': 'spell_cast_party', - 'user': user._id, - 'class': klass, - 'spell': spellId, + type: 'spell_cast_party', + user: user._id, + class: klass, + spell: spellId, }); } await party.save(); diff --git a/website/server/models/group.js b/website/server/models/group.js index 71603198e5..4cb43d2655 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -317,77 +317,78 @@ schema.statics.getGroups = async function getGroups (options = {}) { return groupsArray; }; -async function translateSystemMessages(group, user) { +async function translateSystemMessages (group, user) { let usernames = {}; + let foundText = ''; for (let i = 0; i < group.chat.length; i++) { - if (_.isEmpty(group.chat[i].info)) { - continue; + if (!_.isEmpty(group.chat[i].info)) { + if (_.has(group.chat[i].info, 'user') && !_.has(usernames, group.chat[i].info.user)) { + usernames[group.chat[i].info.user] = null; + } else if (_.has(group.chat[i].info, 'target') && !_.has(usernames, group.chat[i].info.target)) { + usernames[group.chat[i].info.target] = null; + } } - if (_.has(group.chat[i].info, 'user') && !_.has(usernames, group.chat[i].info.user)) { - let user = await User - .findById(group.chat[i].info.user) - .select(nameFields) - .exec(); - usernames[group.chat[i].info.user] = user.profile.name; - } - switch (group.chat[i].info.type) { - case 'quest_start': - group.chat[i].text = '`' + shared.i18n.t('chatQuestStarted', {'questName': questScrolls[group.chat[i].info.quest].text(user.preferences.language)}, user.preferences.language) + '`'; - break; - case 'boss_damage': - group.chat[i].text = '`' + shared.i18n.t('chatBossDamage', {'username': usernames[group.chat[i].info.user], 'bossName': questScrolls[group.chat[i].info.quest].boss.name(user.preferences.language), 'userDamage': group.chat[i].info.userDamage, 'bossDamage': group.chat[i].info.bossDamage}, user.preferences.language) + '`'; - break; - case 'boss_dont_attack': - group.chat[i].text = '`' + shared.i18n.t('chatBossDontAttack', {'bossName': questScrolls[group.chat[i].info.quest].boss.name(user.preferences.language)}, user.preferences.language) + '`'; - break; - case 'boss_rage': - group.chat[i].text = '`' + questScrolls[group.chat[i].info.quest].boss.rage.effect(user.preferences.language) + '`'; - break; - case 'boss_defeated': - group.chat[i].text = '`' + shared.i18n.t('chatBossDefeated', {'bossName': questScrolls[group.chat[i].info.quest].boss.name(user.preferences.language)}, user.preferences.language) + '`'; - break; - case 'user_found_items': - let foundText = _.reduce(group.chat[i].info.items, (m, v, k) => { - m.push(`${v} ${questScrolls[group.chat[i].info.quest].collect[k].text(user.preferences.language)}`); - return m; - }, []); - foundText = foundText.join(', '); - group.chat[i].text = '`' + shared.i18n.t('chatFindItems', {'username': usernames[group.chat[i].info.user], 'items': foundText}, user.preferences.language) + '`'; - break; - case 'all_items_found': - group.chat[i].text = '`' + shared.i18n.t('chatItemQuestFinish', user.preferences.language) + '`'; - break; - case 'spell_cast_party': - group.chat[i].text = '`' + shared.i18n.t('chatCastSpellParty', {'username': usernames[group.chat[i].info.user], 'spellName': shared.content.spells[group.chat[i].info.class][group.chat[i].info.spell].text(user.preferences.language)}, user.preferences.language) + '`'; - break; - case 'spell_cast_user': - if (!_.has(usernames, group.chat[i].info.target)) { - let target = await User - .findById(group.chat[i].info.target) - .select(nameFields) - .exec(); - usernames[group.chat[i].info.target] = user.profile.name; - } - group.chat[i].text = '`' + shared.i18n.t('chatCastSpellUser', {'username': usernames[group.chat[i].info.user], 'spellName': shared.content.spells[group.chat[i].info.class][group.chat[i].info.spell].text(user.preferences.language), 'target': usernames[group.chat[i].info.target]}, user.preferences.language) + '`'; - break; - case 'quest_abort': - group.chat[i].text = '`' + shared.i18n.t('chatQuestAborted', {'username': usernames[group.chat[i].info.user], 'questName': questScrolls[group.chat[i].info.quest].text(user.preferences.language)}, user.preferences.language) + '`'; - break; - case 'tavern_quest_completed': - group.chat[i].text = '`' + shared.content.quests[group.chat[i].info.quest].completionChat(user.preferences.language) + '`'; - break; - case 'tavern_boss_rage_tired': - group.chat[i].text = '`' + shared.i18n.t('tavernBossTired', {'username': usernames[group.chat[i].info.user], 'questName': shared.content.quests[group.chat[i].info.quest].text(user.preferences.language)}, user.preferences.language) + '`'; - break; - case 'tavern_boss_rage': - group.chat[i].text = '`' + shared.content.quests[group.chat[i].info.quest].boss.rage[group.chat[i].info.scene](user.preferences.language) + '`'; - break; - case 'tavern_boss_rage_effect': - group.chat[i].text = '`' + shared.content.quests[group.chat[i].info.quest].boss.rage.effect(user.preferences.language) + '`'; - break; - case 'tavern_boss_desperation': - group.chat[i].text = '`' + shared.content.quests[group.chat[i].info.quest].boss.desperation.text(user.preferences.language) + '`'; - break; + } + await Bluebird.map(Object.keys(usernames), async (username) => { + let usr = await User + .findById(username) + .select(nameFields) + .exec(); + usernames[username] = usr.profile.name; + }); + for (let i = 0; i < group.chat.length; i++) { + if (!_.isEmpty(group.chat[i].info)) { + switch (group.chat[i].info.type) { + case 'quest_start': + group.chat[i].text = `\`${shared.i18n.t('chatQuestStarted', {questName: questScrolls[group.chat[i].info.quest].text(user.preferences.language)}, user.preferences.language)}\``; + break; + case 'boss_damage': + group.chat[i].text = `\`${shared.i18n.t('chatBossDamage', {username: usernames[group.chat[i].info.user], bossName: questScrolls[group.chat[i].info.quest].boss.name(user.preferences.language), userDamage: group.chat[i].info.userDamage, bossDamage: group.chat[i].info.bossDamage}, user.preferences.language)}\``; + break; + case 'boss_dont_attack': + group.chat[i].text = `\`${shared.i18n.t('chatBossDontAttack', {bossName: questScrolls[group.chat[i].info.quest].boss.name(user.preferences.language)}, user.preferences.language)}\``; + break; + case 'boss_rage': + group.chat[i].text = `\`${questScrolls[group.chat[i].info.quest].boss.rage.effect(user.preferences.language)}\``; + break; + case 'boss_defeated': + group.chat[i].text = `\`${shared.i18n.t('chatBossDefeated', {bossName: questScrolls[group.chat[i].info.quest].boss.name(user.preferences.language)}, user.preferences.language)}\``; + break; + case 'user_found_items': + foundText = _.reduce(group.chat[i].info.items, (m, v, k) => { + m.push(`${v} ${questScrolls[group.chat[i].info.quest].collect[k].text(user.preferences.language)}`); + return m; + }, []).join(', '); + group.chat[i].text = `\`${shared.i18n.t('chatFindItems', {username: usernames[group.chat[i].info.user], items: foundText}, user.preferences.language)}\``; + break; + case 'all_items_found': + group.chat[i].text = `\`${shared.i18n.t('chatItemQuestFinish', user.preferences.language)}\``; + break; + case 'spell_cast_party': + group.chat[i].text = `\`${shared.i18n.t('chatCastSpellParty', {username: usernames[group.chat[i].info.user], spellName: shared.content.spells[group.chat[i].info.class][group.chat[i].info.spell].text(user.preferences.language)}, user.preferences.language)}\``; + break; + case 'spell_cast_user': + group.chat[i].text = `\`${shared.i18n.t('chatCastSpellUser', {username: usernames[group.chat[i].info.user], spellName: shared.content.spells[group.chat[i].info.class][group.chat[i].info.spell].text(user.preferences.language), target: usernames[group.chat[i].info.target]}, user.preferences.language)}\``; + break; + case 'quest_abort': + group.chat[i].text = `\`${shared.i18n.t('chatQuestAborted', {username: usernames[group.chat[i].info.user], questName: questScrolls[group.chat[i].info.quest].text(user.preferences.language)}, user.preferences.language)}\``; + break; + case 'tavern_quest_completed': + group.chat[i].text = `\`${shared.content.quests[group.chat[i].info.quest].completionChat(user.preferences.language)}\``; + break; + case 'tavern_boss_rage_tired': + group.chat[i].text = `\`${shared.i18n.t('tavernBossTired', {username: usernames[group.chat[i].info.user], questName: shared.content.quests[group.chat[i].info.quest].text(user.preferences.language)}, user.preferences.language)}\``; + break; + case 'tavern_boss_rage': + group.chat[i].text = `\`${shared.content.quests[group.chat[i].info.quest].boss.rage[group.chat[i].info.scene](user.preferences.language)}\``; + break; + case 'tavern_boss_rage_effect': + group.chat[i].text = `\`${shared.content.quests[group.chat[i].info.quest].boss.rage.effect(user.preferences.language)}\``; + break; + case 'tavern_boss_desperation': + group.chat[i].text = `\`${shared.content.quests[group.chat[i].info.quest].boss.desperation.text(user.preferences.language)}\``; + break; + } } } return group; @@ -520,11 +521,11 @@ schema.methods.isMember = function isGroupMember (user) { } }; -export function chatDefaults (msg, user, info={}) { +export function chatDefaults (msg, user, info = {}) { let message = { id: shared.uuid(), text: msg, - info: info, + info, timestamp: Number(new Date()), likes: {}, flags: {}, @@ -588,7 +589,7 @@ function setUserStyles (newMessage, user) { newMessage.userStyles = userStyles; } -schema.methods.sendChat = function sendChat (message, user, metaData, info={}) { +schema.methods.sendChat = function sendChat (message, user, metaData, info = {}) { let newMessage = chatDefaults(message, user, info); if (user) setUserStyles(newMessage, user); @@ -776,8 +777,8 @@ schema.methods.startQuest = async function startQuest (user) { this.sendChat(`\`Your quest, ${quest.text('en')}, has started.\``, null, { participatingMembers: this.getParticipatingQuestMembers().join(', '), }, { - 'type': 'quest_start', - 'quest': quest.key, + type: 'quest_start', + quest: quest.key, }); }; @@ -971,16 +972,16 @@ schema.methods._processBossQuest = async function processBossQuest (options) { let bossAttack = CRON_SAFE_MODE || CRON_SEMI_SAFE_MODE ? `${quest.boss.name('en')} does not attack, because it respects the fact that there are some bugs\` \`post-maintenance and it doesn't want to hurt anyone unfairly. It will continue its rampage soon!` : `${quest.boss.name('en')} attacks party for ${Math.abs(down).toFixed(1)} damage.`; if (CRON_SAFE_MODE || CRON_SEMI_SAFE_MODE) { group.sendChat(`\`${playerAttack}\` \`${bossAttack}\``, null, null, { - 'type': 'boss_dont_attack', - 'quest': group.quest.key, + type: 'boss_dont_attack', + quest: group.quest.key, }); } else { group.sendChat(`\`${playerAttack}\` \`${bossAttack}\``, null, null, { - 'type': 'boss_damage', - 'user': user._id, - 'quest': group.quest.key, - 'userDamage': progress.up.toFixed(1), - 'bossDamage': Math.abs(down).toFixed(1), + type: 'boss_damage', + user: user._id, + quest: group.quest.key, + userDamage: progress.up.toFixed(1), + bossDamage: Math.abs(down).toFixed(1), }); } @@ -989,8 +990,8 @@ schema.methods._processBossQuest = async function processBossQuest (options) { group.quest.progress.rage += Math.abs(down); if (group.quest.progress.rage >= quest.boss.rage.value) { group.sendChat(quest.boss.rage.effect('en'), null, null, { - 'type': 'tavern_boss_rage_effect', - 'quest': quest.key, + type: 'tavern_boss_rage_effect', + quest: quest.key, }); group.quest.progress.rage = 0; @@ -1019,8 +1020,8 @@ schema.methods._processBossQuest = async function processBossQuest (options) { // Boss slain, finish quest if (group.quest.progress.hp <= 0) { group.sendChat(`\`You defeated ${quest.boss.name('en')}! Questing party members receive the rewards of victory.\``, null, null, { - 'type': 'boss_defeated', - 'quest': quest.key, + type: 'boss_defeated', + quest: quest.key, }); // Participants: Grant rewards & achievements, finish quest @@ -1073,10 +1074,10 @@ schema.methods._processCollectionQuest = async function processCollectionQuest ( foundText = foundText.join(', '); group.sendChat(`\`${user.profile.name} found ${foundText}.\``, null, null, { - 'type': 'user_found_items', - 'user': user._id, - 'quest': quest.key, - 'items': itemsFound, + type: 'user_found_items', + user: user._id, + quest: quest.key, + items: itemsFound, }); group.markModified('quest.progress.collect'); @@ -1087,7 +1088,7 @@ schema.methods._processCollectionQuest = async function processCollectionQuest ( await group.finishQuest(quest); group.sendChat('`All items found! Party has received their rewards.`', null, null, { - 'type': 'all_items_found', + type: 'all_items_found', }); return await group.save(); @@ -1146,8 +1147,8 @@ schema.statics.tavernBoss = async function tavernBoss (user, progress) { if (tavern.quest.progress.hp <= 0) { tavern.sendChat(quest.completionChat('en'), null, null, { - 'type': 'tavern_quest_completed', - 'quest': quest.key, + type: 'tavern_quest_completed', + quest: quest.key, }); await tavern.finishQuest(quest); _.assign(tavernQuest, {extra: null}); @@ -1177,15 +1178,15 @@ schema.statics.tavernBoss = async function tavernBoss (user, progress) { if (!scene) { tavern.sendChat(`\`${quest.boss.name('en')} tries to unleash ${quest.boss.rage.title('en')} but is too tired.\``, null, null, { - 'type': 'tavern_boss_rage_tired', - 'quest': quest.key, + type: 'tavern_boss_rage_tired', + quest: quest.key, }); tavern.quest.progress.rage = 0; // quest.boss.rage.value; } else { tavern.sendChat(quest.boss.rage[scene]('en'), null, null, { - 'type': 'tavern_boss_rage', - 'quest': quest.key, - 'scene': scene, + type: 'tavern_boss_rage', + quest: quest.key, + scene, }); tavern.quest.extra.worldDmg[scene] = true; tavern.quest.extra.worldDmg.recent = scene; @@ -1199,8 +1200,8 @@ schema.statics.tavernBoss = async function tavernBoss (user, progress) { if (quest.boss.desperation && tavern.quest.progress.hp < quest.boss.desperation.threshold && !tavern.quest.extra.desperate) { tavern.sendChat(quest.boss.desperation.text('en'), null, null, { - 'type': 'tavern_boss_desperation', - 'quest': quest.key, + type: 'tavern_boss_desperation', + quest: quest.key, }); tavern.quest.extra.desperate = true; tavern.quest.extra.def = quest.boss.desperation.def; From 7af71d1457c19b874094a712a0c8f10928f4e100 Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Sun, 18 Feb 2018 19:44:17 +0900 Subject: [PATCH 06/31] Fix errors --- website/server/controllers/api-v3/chat.js | 9 ++++++--- website/server/models/group.js | 6 +++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/website/server/controllers/api-v3/chat.js b/website/server/controllers/api-v3/chat.js index 19cf4a53ce..0968a0a823 100644 --- a/website/server/controllers/api-v3/chat.js +++ b/website/server/controllers/api-v3/chat.js @@ -74,7 +74,8 @@ api.getChat = { let group = await Group.getGroup({user, groupId: req.params.groupId, fields: 'chat'}); if (!group) throw new NotFound(res.t('groupNotFound')); - res.respond(200, await Group.toJSONCleanChat(group, user).chat); + let toJSON = await Group.toJSONCleanChat(group, user); + res.respond(200, toJSON.chat); }, }; @@ -194,7 +195,8 @@ api.postChat = { } if (chatUpdated) { - res.respond(200, {chat: await Group.toJSONCleanChat(savedGroup, user).chat}); + let toJSON = await Group.toJSONCleanChat(savedGroup, user); + res.respond(200, {chat: toJSON.chat}); } else { res.respond(200, {message: savedGroup.chat[0]}); } @@ -486,7 +488,8 @@ api.deleteChat = { ).exec(); if (chatUpdated) { - let chatRes = await Group.toJSONCleanChat(group, user).chat; + let toJSON = await Group.toJSONCleanChat(group, user); + let chatRes = toJSON.chat; removeFromArray(chatRes, {id: chatId}); res.respond(200, chatRes); } else { diff --git a/website/server/models/group.js b/website/server/models/group.js index 4cb43d2655..838186a343 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -331,9 +331,9 @@ async function translateSystemMessages (group, user) { } await Bluebird.map(Object.keys(usernames), async (username) => { let usr = await User - .findById(username) - .select(nameFields) - .exec(); + .findById(username) + .select(nameFields) + .exec(); usernames[username] = usr.profile.name; }); for (let i = 0; i < group.chat.length; i++) { From 477c23dd6786681bd12c1593da3c502413e3d9e7 Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Mon, 19 Feb 2018 00:18:29 +0900 Subject: [PATCH 07/31] Save username instead of uuid (no queries necessary anymore) --- website/server/controllers/api-v3/chat.js | 9 ++--- website/server/controllers/api-v3/groups.js | 6 ++-- website/server/controllers/api-v3/quests.js | 2 +- website/server/controllers/api-v3/user.js | 4 +-- website/server/models/group.js | 39 ++++++--------------- 5 files changed, 20 insertions(+), 40 deletions(-) diff --git a/website/server/controllers/api-v3/chat.js b/website/server/controllers/api-v3/chat.js index 0968a0a823..fba5277d49 100644 --- a/website/server/controllers/api-v3/chat.js +++ b/website/server/controllers/api-v3/chat.js @@ -74,8 +74,7 @@ api.getChat = { let group = await Group.getGroup({user, groupId: req.params.groupId, fields: 'chat'}); if (!group) throw new NotFound(res.t('groupNotFound')); - let toJSON = await Group.toJSONCleanChat(group, user); - res.respond(200, toJSON.chat); + res.respond(200, Group.toJSONCleanChat(group, user).chat); }, }; @@ -195,8 +194,7 @@ api.postChat = { } if (chatUpdated) { - let toJSON = await Group.toJSONCleanChat(savedGroup, user); - res.respond(200, {chat: toJSON.chat}); + res.respond(200, {chat: Group.toJSONCleanChat(savedGroup, user).chat}); } else { res.respond(200, {message: savedGroup.chat[0]}); } @@ -488,8 +486,7 @@ api.deleteChat = { ).exec(); if (chatUpdated) { - let toJSON = await Group.toJSONCleanChat(group, user); - let chatRes = toJSON.chat; + let chatRes = Group.toJSONCleanChat(group, user).chat; removeFromArray(chatRes, {id: chatId}); res.respond(200, chatRes); } else { diff --git a/website/server/controllers/api-v3/groups.js b/website/server/controllers/api-v3/groups.js index 59cd767e85..ae7c8c1234 100644 --- a/website/server/controllers/api-v3/groups.js +++ b/website/server/controllers/api-v3/groups.js @@ -391,7 +391,7 @@ api.getGroup = { throw new NotFound(res.t('groupNotFound')); } - let groupJson = await Group.toJSONCleanChat(group, user); + let groupJson = Group.toJSONCleanChat(group, user); if (groupJson.leader === user._id) { groupJson.purchased.plan = group.purchased.plan.toObject(); @@ -455,7 +455,7 @@ api.updateGroup = { _.assign(group, _.merge(group.toObject(), Group.sanitizeUpdate(req.body))); let savedGroup = await group.save(); - let response = await Group.toJSONCleanChat(savedGroup, user); + let response = Group.toJSONCleanChat(savedGroup, user); // If the leader changed fetch new data, otherwise use authenticated user if (response.leader !== user._id) { @@ -619,7 +619,7 @@ api.joinGroup = { promises = await Bluebird.all(promises); - let response = await Group.toJSONCleanChat(promises[0], user); + let response = Group.toJSONCleanChat(promises[0], user); let leader = await User.findById(response.leader).select(nameFields).exec(); if (leader) { response.leader = leader.toJSON({minimize: true}); diff --git a/website/server/controllers/api-v3/quests.js b/website/server/controllers/api-v3/quests.js index 1f003f6865..bbc7c2497e 100644 --- a/website/server/controllers/api-v3/quests.js +++ b/website/server/controllers/api-v3/quests.js @@ -424,7 +424,7 @@ api.abortQuest = { let questName = questScrolls[group.quest.key].text('en'); group.sendChat(`\`${user.profile.name} aborted the party quest ${questName}.\``, null, null, { type: 'quest_abort', - user: user._id, + user: user.profile.name, quest: group.quest.key, }); diff --git a/website/server/controllers/api-v3/user.js b/website/server/controllers/api-v3/user.js index 3cdb1b9b6b..1adb8863f6 100644 --- a/website/server/controllers/api-v3/user.js +++ b/website/server/controllers/api-v3/user.js @@ -738,7 +738,7 @@ api.castSpell = { if (targetType === 'user') { party.sendChat(message, null, null, { type: 'spell_cast_user', - user: user._id, + user: user.profile.name, class: klass, spell: spellId, target: partyMembers._id, @@ -746,7 +746,7 @@ api.castSpell = { } else { party.sendChat(message, null, null, { type: 'spell_cast_party', - user: user._id, + user: user.profile.name, class: klass, spell: spellId, }); diff --git a/website/server/models/group.js b/website/server/models/group.js index 838186a343..81f46df7a3 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -317,25 +317,8 @@ schema.statics.getGroups = async function getGroups (options = {}) { return groupsArray; }; -async function translateSystemMessages (group, user) { - let usernames = {}; +function translateSystemMessages (group, user) { let foundText = ''; - for (let i = 0; i < group.chat.length; i++) { - if (!_.isEmpty(group.chat[i].info)) { - if (_.has(group.chat[i].info, 'user') && !_.has(usernames, group.chat[i].info.user)) { - usernames[group.chat[i].info.user] = null; - } else if (_.has(group.chat[i].info, 'target') && !_.has(usernames, group.chat[i].info.target)) { - usernames[group.chat[i].info.target] = null; - } - } - } - await Bluebird.map(Object.keys(usernames), async (username) => { - let usr = await User - .findById(username) - .select(nameFields) - .exec(); - usernames[username] = usr.profile.name; - }); for (let i = 0; i < group.chat.length; i++) { if (!_.isEmpty(group.chat[i].info)) { switch (group.chat[i].info.type) { @@ -343,7 +326,7 @@ async function translateSystemMessages (group, user) { group.chat[i].text = `\`${shared.i18n.t('chatQuestStarted', {questName: questScrolls[group.chat[i].info.quest].text(user.preferences.language)}, user.preferences.language)}\``; break; case 'boss_damage': - group.chat[i].text = `\`${shared.i18n.t('chatBossDamage', {username: usernames[group.chat[i].info.user], bossName: questScrolls[group.chat[i].info.quest].boss.name(user.preferences.language), userDamage: group.chat[i].info.userDamage, bossDamage: group.chat[i].info.bossDamage}, user.preferences.language)}\``; + group.chat[i].text = `\`${shared.i18n.t('chatBossDamage', {username: group.chat[i].info.user, bossName: questScrolls[group.chat[i].info.quest].boss.name(user.preferences.language), userDamage: group.chat[i].info.userDamage, bossDamage: group.chat[i].info.bossDamage}, user.preferences.language)}\``; break; case 'boss_dont_attack': group.chat[i].text = `\`${shared.i18n.t('chatBossDontAttack', {bossName: questScrolls[group.chat[i].info.quest].boss.name(user.preferences.language)}, user.preferences.language)}\``; @@ -359,25 +342,25 @@ async function translateSystemMessages (group, user) { m.push(`${v} ${questScrolls[group.chat[i].info.quest].collect[k].text(user.preferences.language)}`); return m; }, []).join(', '); - group.chat[i].text = `\`${shared.i18n.t('chatFindItems', {username: usernames[group.chat[i].info.user], items: foundText}, user.preferences.language)}\``; + group.chat[i].text = `\`${shared.i18n.t('chatFindItems', {username: group.chat[i].info.user, items: foundText}, user.preferences.language)}\``; break; case 'all_items_found': group.chat[i].text = `\`${shared.i18n.t('chatItemQuestFinish', user.preferences.language)}\``; break; case 'spell_cast_party': - group.chat[i].text = `\`${shared.i18n.t('chatCastSpellParty', {username: usernames[group.chat[i].info.user], spellName: shared.content.spells[group.chat[i].info.class][group.chat[i].info.spell].text(user.preferences.language)}, user.preferences.language)}\``; + group.chat[i].text = `\`${shared.i18n.t('chatCastSpellParty', {username: group.chat[i].info.user, spellName: shared.content.spells[group.chat[i].info.class][group.chat[i].info.spell].text(user.preferences.language)}, user.preferences.language)}\``; break; case 'spell_cast_user': - group.chat[i].text = `\`${shared.i18n.t('chatCastSpellUser', {username: usernames[group.chat[i].info.user], spellName: shared.content.spells[group.chat[i].info.class][group.chat[i].info.spell].text(user.preferences.language), target: usernames[group.chat[i].info.target]}, user.preferences.language)}\``; + group.chat[i].text = `\`${shared.i18n.t('chatCastSpellUser', {username: group.chat[i].info.user, spellName: shared.content.spells[group.chat[i].info.class][group.chat[i].info.spell].text(user.preferences.language), target: group.chat[i].info.target}, user.preferences.language)}\``; break; case 'quest_abort': - group.chat[i].text = `\`${shared.i18n.t('chatQuestAborted', {username: usernames[group.chat[i].info.user], questName: questScrolls[group.chat[i].info.quest].text(user.preferences.language)}, user.preferences.language)}\``; + group.chat[i].text = `\`${shared.i18n.t('chatQuestAborted', {username: group.chat[i].info.user, questName: questScrolls[group.chat[i].info.quest].text(user.preferences.language)}, user.preferences.language)}\``; break; case 'tavern_quest_completed': group.chat[i].text = `\`${shared.content.quests[group.chat[i].info.quest].completionChat(user.preferences.language)}\``; break; case 'tavern_boss_rage_tired': - group.chat[i].text = `\`${shared.i18n.t('tavernBossTired', {username: usernames[group.chat[i].info.user], questName: shared.content.quests[group.chat[i].info.quest].text(user.preferences.language)}, user.preferences.language)}\``; + group.chat[i].text = `\`${shared.i18n.t('tavernBossTired', {username: group.chat[i].info.user, questName: shared.content.quests[group.chat[i].info.quest].text(user.preferences.language)}, user.preferences.language)}\``; break; case 'tavern_boss_rage': group.chat[i].text = `\`${shared.content.quests[group.chat[i].info.quest].boss.rage[group.chat[i].info.scene](user.preferences.language)}\``; @@ -398,8 +381,8 @@ async function translateSystemMessages (group, user) { // unless the user is an admin or said chat is posted by that user // Not putting into toJSON because there we can't access user // It also removes the _meta field that can be stored inside a chat message -schema.statics.toJSONCleanChat = async function groupToJSONCleanChat (group, user) { - group = await translateSystemMessages(group, user); +schema.statics.toJSONCleanChat = function groupToJSONCleanChat (group, user) { + group = translateSystemMessages(group, user); let toJSON = group.toJSON(); @@ -978,7 +961,7 @@ schema.methods._processBossQuest = async function processBossQuest (options) { } else { group.sendChat(`\`${playerAttack}\` \`${bossAttack}\``, null, null, { type: 'boss_damage', - user: user._id, + user: user.profile.name, quest: group.quest.key, userDamage: progress.up.toFixed(1), bossDamage: Math.abs(down).toFixed(1), @@ -1075,7 +1058,7 @@ schema.methods._processCollectionQuest = async function processCollectionQuest ( foundText = foundText.join(', '); group.sendChat(`\`${user.profile.name} found ${foundText}.\``, null, null, { type: 'user_found_items', - user: user._id, + user: user.profile.name, quest: quest.key, items: itemsFound, }); From 2ce9b319a039320f8e1f1640fae659847cb55b65 Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Mon, 19 Feb 2018 00:24:30 +0900 Subject: [PATCH 08/31] Target username instead of uuid --- website/server/controllers/api-v3/user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/server/controllers/api-v3/user.js b/website/server/controllers/api-v3/user.js index 1adb8863f6..c8c04ff565 100644 --- a/website/server/controllers/api-v3/user.js +++ b/website/server/controllers/api-v3/user.js @@ -741,7 +741,7 @@ api.castSpell = { user: user.profile.name, class: klass, spell: spellId, - target: partyMembers._id, + target: partyMembers.profile.name, }); } else { party.sendChat(message, null, null, { From 2619ac37d9ea645f4c2fb4d1a770a8ec9fa3ec8b Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Mon, 19 Feb 2018 00:36:49 +0900 Subject: [PATCH 09/31] Fix mistake on tavern_boss_rage_tired --- website/common/locales/en/groups.json | 2 +- website/server/models/group.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/website/common/locales/en/groups.json b/website/common/locales/en/groups.json index ef440ec914..2600441243 100644 --- a/website/common/locales/en/groups.json +++ b/website/common/locales/en/groups.json @@ -439,5 +439,5 @@ "chatCastSpellParty": "<%= username %> casts <%= spellName %> for the party.", "chatCastSpellUser": "<%= username %> casts <%= spellName %> on <%= target %>.", "chatQuestAborted": "<%= username %> aborted the party quest <%= questName %>.", - "tavernBossTired": "<%= bossName %> tries to unleash ${quest.boss.rage.title('en')} but is too tired." + "tavernBossTired": "<%= bossName %> tries to unleash <%= rageName %> but is too tired." } diff --git a/website/server/models/group.js b/website/server/models/group.js index 81f46df7a3..e51a08daee 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -360,7 +360,7 @@ function translateSystemMessages (group, user) { group.chat[i].text = `\`${shared.content.quests[group.chat[i].info.quest].completionChat(user.preferences.language)}\``; break; case 'tavern_boss_rage_tired': - group.chat[i].text = `\`${shared.i18n.t('tavernBossTired', {username: group.chat[i].info.user, questName: shared.content.quests[group.chat[i].info.quest].text(user.preferences.language)}, user.preferences.language)}\``; + group.chat[i].text = `\`${shared.i18n.t('tavernBossTired', {rageName: shared.content.quests[group.chat[i].info.quest].boss.rage.title(user.preferences.language), bossName: shared.content.quests[group.chat[i].info.quest].boss.name(user.preferences.language)}, user.preferences.language)}\``; break; case 'tavern_boss_rage': group.chat[i].text = `\`${shared.content.quests[group.chat[i].info.quest].boss.rage[group.chat[i].info.scene](user.preferences.language)}\``; From 521a1e646d83402f93c227bd0ddfbbcc88e57a8c Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Mon, 19 Feb 2018 20:07:05 +0900 Subject: [PATCH 10/31] Missing message (user claim task) --- website/server/controllers/api-v3/tasks/groups.js | 6 +++++- website/server/models/group.js | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/website/server/controllers/api-v3/tasks/groups.js b/website/server/controllers/api-v3/tasks/groups.js index 2f1a1675d8..a340fb56ce 100644 --- a/website/server/controllers/api-v3/tasks/groups.js +++ b/website/server/controllers/api-v3/tasks/groups.js @@ -199,7 +199,11 @@ api.assignTask = { // User is claiming the task if (user._id === assignedUserId) { let message = res.t('userIsClamingTask', {username: user.profile.name, task: task.text}); - group.sendChat(message); + group.sendChat(message, null, null, { + type: 'claim_task', + user: user.profile.name, + task: task.text, + }); } let promises = []; diff --git a/website/server/models/group.js b/website/server/models/group.js index e51a08daee..ea2954b969 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -371,6 +371,9 @@ function translateSystemMessages (group, user) { case 'tavern_boss_desperation': group.chat[i].text = `\`${shared.content.quests[group.chat[i].info.quest].boss.desperation.text(user.preferences.language)}\``; break; + case 'claim_task': + group.chat[i].text = `\`${shared.i18n.t('userIsClamingTask', {username: group.chat[i].info.user, task: group.chat[i].info.task}, user.preferences.language)}\``; + break; } } } From 774db2564f81746adbabe5c606c149a3838dd001 Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Mon, 19 Feb 2018 20:17:03 +0900 Subject: [PATCH 11/31] Moving some strings around --- website/common/locales/en/character.json | 2 ++ website/common/locales/en/groups.json | 12 +----------- website/common/locales/en/quests.json | 10 +++++++++- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/website/common/locales/en/character.json b/website/common/locales/en/character.json index 162fa6e701..ee99833258 100644 --- a/website/common/locales/en/character.json +++ b/website/common/locales/en/character.json @@ -170,6 +170,8 @@ "youCast": "You cast <%= spell %>.", "youCastTarget": "You cast <%= spell %> on <%= target %>.", "youCastParty": "You cast <%= spell %> for the party.", + "chatCastSpellParty": "<%= username %> casts <%= spellName %> for the party.", + "chatCastSpellUser": "<%= username %> casts <%= spellName %> on <%= target %>.", "critBonus": "Critical Hit! Bonus: ", "gainedGold": "You gained some Gold", "gainedMana": "You gained some Mana", diff --git a/website/common/locales/en/groups.json b/website/common/locales/en/groups.json index 2600441243..e98f66076a 100644 --- a/website/common/locales/en/groups.json +++ b/website/common/locales/en/groups.json @@ -429,15 +429,5 @@ "worldBossBullet1": "Complete tasks to damage the World Boss", "worldBossBullet2": "The World Boss won’t damage you for missed tasks, but its Rage meter will go up. If the bar fills up, the Boss will attack one of Habitica’s shopkeepers!", "worldBossBullet3": "You can continue with normal Quest Bosses, damage will apply to both", - "worldBossBullet4": "Check the Tavern regularly to see World Boss progress and Rage attacks", - "chatQuestStarted": "Your quest, <%= questName %>, has started.", - "chatBossDamage": "<%= username %> attacks <%= bossName %> for <%= userDamage %> damage. <%= bossName %> attacks party for <%= bossDamage %> damage.", - "chatBossDontAttack": "<%= bossName %> does not attack, because it respects the fact that there are some bugs post-maintenance and it doesn't want to hurt anyone unfairly. It will continue its rampage soon!", - "chatBossDefeated": "You defeated <%= bossName %>! Questing party members receive the rewards of victory.", - "chatFindItems": "<%= username %> found <%= items %>.", - "chatItemQuestFinish": "All items found! Party has received their rewards.", - "chatCastSpellParty": "<%= username %> casts <%= spellName %> for the party.", - "chatCastSpellUser": "<%= username %> casts <%= spellName %> on <%= target %>.", - "chatQuestAborted": "<%= username %> aborted the party quest <%= questName %>.", - "tavernBossTired": "<%= bossName %> tries to unleash <%= rageName %> but is too tired." + "worldBossBullet4": "Check the Tavern regularly to see World Boss progress and Rage attacks" } diff --git a/website/common/locales/en/quests.json b/website/common/locales/en/quests.json index c6671cf74e..b691063b82 100644 --- a/website/common/locales/en/quests.json +++ b/website/common/locales/en/quests.json @@ -126,5 +126,13 @@ "bossHealth": "<%= currentHealth %> / <%= maxHealth %> Health", "rageAttack": "Rage Attack:", "bossRage": "<%= currentRage %> / <%= maxRage %> Rage", - "rageStrikes": "Rage Strikes" + "rageStrikes": "Rage Strikes", + "chatQuestStarted": "Your quest, <%= questName %>, has started.", + "chatBossDamage": "<%= username %> attacks <%= bossName %> for <%= userDamage %> damage. <%= bossName %> attacks party for <%= bossDamage %> damage.", + "chatBossDontAttack": "<%= bossName %> does not attack, because it respects the fact that there are some bugs post-maintenance and it doesn't want to hurt anyone unfairly. It will continue its rampage soon!", + "chatBossDefeated": "You defeated <%= bossName %>! Questing party members receive the rewards of victory.", + "chatFindItems": "<%= username %> found <%= items %>.", + "chatItemQuestFinish": "All items found! Party has received their rewards.", + "chatQuestAborted": "<%= username %> aborted the party quest <%= questName %>.", + "tavernBossTired": "<%= bossName %> tries to unleash <%= rageName %> but is too tired." } From 649404ac6accc1f7f42e3ee3c6cbabc33f396d19 Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Mon, 19 Feb 2018 20:57:31 +0900 Subject: [PATCH 12/31] Fix travis-ci error --- website/server/models/group.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/server/models/group.js b/website/server/models/group.js index ea2954b969..323a3b7b10 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -372,7 +372,7 @@ function translateSystemMessages (group, user) { group.chat[i].text = `\`${shared.content.quests[group.chat[i].info.quest].boss.desperation.text(user.preferences.language)}\``; break; case 'claim_task': - group.chat[i].text = `\`${shared.i18n.t('userIsClamingTask', {username: group.chat[i].info.user, task: group.chat[i].info.task}, user.preferences.language)}\``; + group.chat[i].text = `${shared.i18n.t('userIsClamingTask', {username: group.chat[i].info.user, task: group.chat[i].info.task}, user.preferences.language)}`; break; } } From 8304f99ecb80434f2fe19eb5bae4d3dda7e51e0f Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Mon, 26 Feb 2018 20:00:47 +0900 Subject: [PATCH 13/31] Added comment explaining the new info object --- website/server/models/group.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/server/models/group.js b/website/server/models/group.js index 6477ddb9d1..381ee08fd5 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -507,6 +507,8 @@ schema.methods.isMember = function isGroupMember (user) { } }; +// info: An object containing relevant information about a system message, +// so it can be translated to any language. export function chatDefaults (msg, user, info = {}) { let message = { id: shared.uuid(), From 68fa834946c75641c73e12402da32dda9386cb0a Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Mon, 12 Mar 2018 21:08:20 +0900 Subject: [PATCH 14/31] spellName -> spell; grammar fix --- website/common/locales/en/character.json | 4 ++-- website/common/locales/en/quests.json | 2 +- website/server/models/group.js | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/website/common/locales/en/character.json b/website/common/locales/en/character.json index ee99833258..c8626acc86 100644 --- a/website/common/locales/en/character.json +++ b/website/common/locales/en/character.json @@ -170,8 +170,8 @@ "youCast": "You cast <%= spell %>.", "youCastTarget": "You cast <%= spell %> on <%= target %>.", "youCastParty": "You cast <%= spell %> for the party.", - "chatCastSpellParty": "<%= username %> casts <%= spellName %> for the party.", - "chatCastSpellUser": "<%= username %> casts <%= spellName %> on <%= target %>.", + "chatCastSpellParty": "<%= username %> casts <%= spell %> for the party.", + "chatCastSpellUser": "<%= username %> casts <%= spell %> on <%= target %>.", "critBonus": "Critical Hit! Bonus: ", "gainedGold": "You gained some Gold", "gainedMana": "You gained some Mana", diff --git a/website/common/locales/en/quests.json b/website/common/locales/en/quests.json index b691063b82..d467f41c08 100644 --- a/website/common/locales/en/quests.json +++ b/website/common/locales/en/quests.json @@ -129,7 +129,7 @@ "rageStrikes": "Rage Strikes", "chatQuestStarted": "Your quest, <%= questName %>, has started.", "chatBossDamage": "<%= username %> attacks <%= bossName %> for <%= userDamage %> damage. <%= bossName %> attacks party for <%= bossDamage %> damage.", - "chatBossDontAttack": "<%= bossName %> does not attack, because it respects the fact that there are some bugs post-maintenance and it doesn't want to hurt anyone unfairly. It will continue its rampage soon!", + "chatBossDontAttack": "<%= bossName %> does not attack, because it respects the fact that there are some bugs post-maintenance, and it doesn't want to hurt anyone unfairly. It will continue its rampage soon!", "chatBossDefeated": "You defeated <%= bossName %>! Questing party members receive the rewards of victory.", "chatFindItems": "<%= username %> found <%= items %>.", "chatItemQuestFinish": "All items found! Party has received their rewards.", diff --git a/website/server/models/group.js b/website/server/models/group.js index 509c4e160b..23bd4828f3 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -348,10 +348,10 @@ function translateSystemMessages (group, user) { group.chat[i].text = `\`${shared.i18n.t('chatItemQuestFinish', user.preferences.language)}\``; break; case 'spell_cast_party': - group.chat[i].text = `\`${shared.i18n.t('chatCastSpellParty', {username: group.chat[i].info.user, spellName: shared.content.spells[group.chat[i].info.class][group.chat[i].info.spell].text(user.preferences.language)}, user.preferences.language)}\``; + group.chat[i].text = `\`${shared.i18n.t('chatCastSpellParty', {username: group.chat[i].info.user, spell: shared.content.spells[group.chat[i].info.class][group.chat[i].info.spell].text(user.preferences.language)}, user.preferences.language)}\``; break; case 'spell_cast_user': - group.chat[i].text = `\`${shared.i18n.t('chatCastSpellUser', {username: group.chat[i].info.user, spellName: shared.content.spells[group.chat[i].info.class][group.chat[i].info.spell].text(user.preferences.language), target: group.chat[i].info.target}, user.preferences.language)}\``; + group.chat[i].text = `\`${shared.i18n.t('chatCastSpellUser', {username: group.chat[i].info.user, spell: shared.content.spells[group.chat[i].info.class][group.chat[i].info.spell].text(user.preferences.language), target: group.chat[i].info.target}, user.preferences.language)}\``; break; case 'quest_abort': group.chat[i].text = `\`${shared.i18n.t('chatQuestAborted', {username: group.chat[i].info.user, questName: questScrolls[group.chat[i].info.quest].text(user.preferences.language)}, user.preferences.language)}\``; From 314926cc0687969b971ca2bf1ccab9494e79c1c5 Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Mon, 12 Mar 2018 21:45:41 +0900 Subject: [PATCH 15/31] Hard coded messages now using i18n strings --- website/server/controllers/api-v3/quests.js | 2 +- .../server/controllers/api-v3/user/spells.js | 5 ++--- website/server/models/group.js | 17 +++++++---------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/website/server/controllers/api-v3/quests.js b/website/server/controllers/api-v3/quests.js index c9dab902ab..8650a69858 100644 --- a/website/server/controllers/api-v3/quests.js +++ b/website/server/controllers/api-v3/quests.js @@ -422,7 +422,7 @@ api.abortQuest = { if (user._id !== group.leader && user._id !== group.quest.leader) throw new NotAuthorized(res.t('onlyLeaderAbortQuest')); let questName = questScrolls[group.quest.key].text('en'); - group.sendChat(`\`${user.profile.name} aborted the party quest ${questName}.\``, null, null, { + group.sendChat(`\`${common.i18n.t('chatQuestAborted', {username: user.profile.name, questName}, 'en')}\``, null, null, { type: 'quest_abort', user: user.profile.name, quest: group.quest.key, diff --git a/website/server/controllers/api-v3/user/spells.js b/website/server/controllers/api-v3/user/spells.js index 7a4a397ae3..509312b5a8 100644 --- a/website/server/controllers/api-v3/user/spells.js +++ b/website/server/controllers/api-v3/user/spells.js @@ -129,9 +129,8 @@ api.castSpell = { }); if (party && !spell.silent) { - let message = `\`${user.profile.name} casts ${spell.text()}${targetType === 'user' ? ` on ${partyMembers.profile.name}` : ' for the party'}.\``; if (targetType === 'user') { - party.sendChat(message, null, null, { + party.sendChat(`\`${common.i18n.t('chatCastSpellUser', {username: user.profile.name, spell: spell.text(), target: partyMembers.profile.name}, 'en')}\``, null, null, { type: 'spell_cast_user', user: user.profile.name, class: klass, @@ -139,7 +138,7 @@ api.castSpell = { target: partyMembers.profile.name, }); } else { - party.sendChat(message, null, null, { + party.sendChat(`\`${common.i18n.t('chatCastSpellParty', {username: user.profile.name, spell: spell.text()}, 'en')}\``, null, null, { type: 'spell_cast_party', user: user.profile.name, class: klass, diff --git a/website/server/models/group.js b/website/server/models/group.js index 23bd4828f3..fe19156636 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -772,7 +772,7 @@ schema.methods.startQuest = async function startQuest (user) { }); }); }); - this.sendChat(`\`Your quest, ${quest.text('en')}, has started.\``, null, { + this.sendChat(`\`${shared.i18n.t('chatQuestStarted', {questName: quest.text('en')}, 'en')}\``, null, { participatingMembers: this.getParticipatingQuestMembers().join(', '), }, { type: 'quest_start', @@ -965,16 +965,13 @@ schema.methods._processBossQuest = async function processBossQuest (options) { }; group.quest.progress.hp -= progress.up; - // TODO Create a party preferred language option so emits like this can be localized. Suggestion: Always display the English version too. Or, if English is not displayed to the players, at least include it in a new field in the chat object that's visible in the database - essential for admins when troubleshooting quests! - let playerAttack = `${user.profile.name} attacks ${quest.boss.name('en')} for ${progress.up.toFixed(1)} damage.`; - let bossAttack = CRON_SAFE_MODE || CRON_SEMI_SAFE_MODE ? `${quest.boss.name('en')} does not attack, because it respects the fact that there are some bugs\` \`post-maintenance and it doesn't want to hurt anyone unfairly. It will continue its rampage soon!` : `${quest.boss.name('en')} attacks party for ${Math.abs(down).toFixed(1)} damage.`; if (CRON_SAFE_MODE || CRON_SEMI_SAFE_MODE) { - group.sendChat(`\`${playerAttack}\` \`${bossAttack}\``, null, null, { + group.sendChat(`\`${shared.i18n.t('chatBossDontAttack', {bossName: quest.boss.name('en')}, 'en')}\``, null, null, { type: 'boss_dont_attack', quest: group.quest.key, }); } else { - group.sendChat(`\`${playerAttack}\` \`${bossAttack}\``, null, null, { + group.sendChat(`\`${shared.i18n.t('chatBossDamage', {username: user.profile.name, bossName: quest.boss.name('en'), userDamage: progress.up.toFixed(1), bossDamage: Math.abs(down).toFixed(1)}, user.preferences.language)}\``, null, null, { type: 'boss_damage', user: user.profile.name, quest: group.quest.key, @@ -1017,7 +1014,7 @@ schema.methods._processBossQuest = async function processBossQuest (options) { // Boss slain, finish quest if (group.quest.progress.hp <= 0) { - group.sendChat(`\`You defeated ${quest.boss.name('en')}! Questing party members receive the rewards of victory.\``, null, null, { + group.sendChat(`\`${shared.i18n.t('chatBossDefeated', {bossName: quest.boss.name('en')}, 'en')}\``, null, null, { type: 'boss_defeated', quest: quest.key, }); @@ -1071,7 +1068,7 @@ schema.methods._processCollectionQuest = async function processCollectionQuest ( }, []); foundText = foundText.join(', '); - group.sendChat(`\`${user.profile.name} found ${foundText}.\``, null, null, { + group.sendChat(`\`${shared.i18n.t('chatFindItems', {username: user.profile.name, items: foundText}, 'en')}\``, null, null, { type: 'user_found_items', user: user.profile.name, quest: quest.key, @@ -1085,7 +1082,7 @@ schema.methods._processCollectionQuest = async function processCollectionQuest ( })) return await group.save(); await group.finishQuest(quest); - group.sendChat('`All items found! Party has received their rewards.`', null, null, { + group.sendChat(`\`${shared.i18n.t('chatItemQuestFinish', 'en')}\``, null, null, { type: 'all_items_found', }); @@ -1175,7 +1172,7 @@ schema.statics.tavernBoss = async function tavernBoss (user, progress) { } if (!scene) { - tavern.sendChat(`\`${quest.boss.name('en')} tries to unleash ${quest.boss.rage.title('en')} but is too tired.\``, null, null, { + tavern.sendChat(`\`${shared.i18n.t('tavernBossTired', {rageName: quest.boss.rage.title('en'), bossName: quest.boss.name('en')}, 'en')}\``, null, null, { type: 'tavern_boss_rage_tired', quest: quest.key, }); From 854696728a547d7761dd971dbd0114825d6cd9d6 Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Mon, 12 Mar 2018 21:50:23 +0900 Subject: [PATCH 16/31] Fix message when boss don't attack --- website/common/locales/en/quests.json | 2 +- website/server/models/group.js | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/website/common/locales/en/quests.json b/website/common/locales/en/quests.json index d467f41c08..7e6c3c1569 100644 --- a/website/common/locales/en/quests.json +++ b/website/common/locales/en/quests.json @@ -129,7 +129,7 @@ "rageStrikes": "Rage Strikes", "chatQuestStarted": "Your quest, <%= questName %>, has started.", "chatBossDamage": "<%= username %> attacks <%= bossName %> for <%= userDamage %> damage. <%= bossName %> attacks party for <%= bossDamage %> damage.", - "chatBossDontAttack": "<%= bossName %> does not attack, because it respects the fact that there are some bugs post-maintenance, and it doesn't want to hurt anyone unfairly. It will continue its rampage soon!", + "chatBossDontAttack": "<%= username %> attacks <%= bossName %> for <%= userDamage %> damage. <%= bossName %> does not attack, because it respects the fact that there are some bugs post-maintenance, and it doesn't want to hurt anyone unfairly. It will continue its rampage soon!", "chatBossDefeated": "You defeated <%= bossName %>! Questing party members receive the rewards of victory.", "chatFindItems": "<%= username %> found <%= items %>.", "chatItemQuestFinish": "All items found! Party has received their rewards.", diff --git a/website/server/models/group.js b/website/server/models/group.js index fe19156636..03cc28efb6 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -329,7 +329,7 @@ function translateSystemMessages (group, user) { group.chat[i].text = `\`${shared.i18n.t('chatBossDamage', {username: group.chat[i].info.user, bossName: questScrolls[group.chat[i].info.quest].boss.name(user.preferences.language), userDamage: group.chat[i].info.userDamage, bossDamage: group.chat[i].info.bossDamage}, user.preferences.language)}\``; break; case 'boss_dont_attack': - group.chat[i].text = `\`${shared.i18n.t('chatBossDontAttack', {bossName: questScrolls[group.chat[i].info.quest].boss.name(user.preferences.language)}, user.preferences.language)}\``; + group.chat[i].text = `\`${shared.i18n.t('chatBossDontAttack', {username: group.chat[i].info.user, bossName: questScrolls[group.chat[i].info.quest].boss.name(user.preferences.language), userDamage: group.chat[i].info.userDamage}, user.preferences.language)}\``; break; case 'boss_rage': group.chat[i].text = `\`${questScrolls[group.chat[i].info.quest].boss.rage.effect(user.preferences.language)}\``; @@ -968,7 +968,9 @@ schema.methods._processBossQuest = async function processBossQuest (options) { if (CRON_SAFE_MODE || CRON_SEMI_SAFE_MODE) { group.sendChat(`\`${shared.i18n.t('chatBossDontAttack', {bossName: quest.boss.name('en')}, 'en')}\``, null, null, { type: 'boss_dont_attack', + user: user.profile.name, quest: group.quest.key, + userDamage: progress.up.toFixed(1), }); } else { group.sendChat(`\`${shared.i18n.t('chatBossDamage', {username: user.profile.name, bossName: quest.boss.name('en'), userDamage: progress.up.toFixed(1), bossDamage: Math.abs(down).toFixed(1)}, user.preferences.language)}\``, null, null, { From cc4772c75a47dea89930539aff20c67dc3dfbd73 Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Mon, 12 Mar 2018 23:48:09 +0900 Subject: [PATCH 17/31] Update test --- test/api/v3/unit/models/group.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/api/v3/unit/models/group.test.js b/test/api/v3/unit/models/group.test.js index 18cdb23b8b..fff28e4cc9 100644 --- a/test/api/v3/unit/models/group.test.js +++ b/test/api/v3/unit/models/group.test.js @@ -201,7 +201,7 @@ describe('Group Model', () => { party = await Group.findOne({_id: party._id}); expect(Group.prototype.sendChat).to.be.calledOnce; - expect(Group.prototype.sendChat).to.be.calledWith('`Participating Member attacks Wailing Whale for 5.0 damage.` `Wailing Whale attacks party for 7.5 damage.`'); + expect(Group.prototype.sendChat).to.be.calledWith('`Participating Member attacks Wailing Whale for 5.0 damage. Wailing Whale attacks party for 7.5 damage.`'); }); it('applies damage only to participating members of party', async () => { From 012fa2f8ef65d6abe544afa5414a92b391bb8805 Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Sun, 18 Mar 2018 12:09:03 +0900 Subject: [PATCH 18/31] Improve readability of translateSystemMessages function --- website/server/models/group.js | 37 ++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/website/server/models/group.js b/website/server/models/group.js index 6b36268dd6..a4e7e94407 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -317,62 +317,65 @@ schema.statics.getGroups = async function getGroups (options = {}) { function translateSystemMessages (group, user) { let foundText = ''; + let lang = user.preferences.language; for (let i = 0; i < group.chat.length; i++) { if (!_.isEmpty(group.chat[i].info)) { + let msg; switch (group.chat[i].info.type) { case 'quest_start': - group.chat[i].text = `\`${shared.i18n.t('chatQuestStarted', {questName: questScrolls[group.chat[i].info.quest].text(user.preferences.language)}, user.preferences.language)}\``; + msg = `\`${shared.i18n.t('chatQuestStarted', {questName: questScrolls[group.chat[i].info.quest].text(lang)}, lang)}\``; break; case 'boss_damage': - group.chat[i].text = `\`${shared.i18n.t('chatBossDamage', {username: group.chat[i].info.user, bossName: questScrolls[group.chat[i].info.quest].boss.name(user.preferences.language), userDamage: group.chat[i].info.userDamage, bossDamage: group.chat[i].info.bossDamage}, user.preferences.language)}\``; + msg = `\`${shared.i18n.t('chatBossDamage', {username: group.chat[i].info.user, bossName: questScrolls[group.chat[i].info.quest].boss.name(lang), userDamage: group.chat[i].info.userDamage, bossDamage: group.chat[i].info.bossDamage}, lang)}\``; break; case 'boss_dont_attack': - group.chat[i].text = `\`${shared.i18n.t('chatBossDontAttack', {username: group.chat[i].info.user, bossName: questScrolls[group.chat[i].info.quest].boss.name(user.preferences.language), userDamage: group.chat[i].info.userDamage}, user.preferences.language)}\``; + msg = `\`${shared.i18n.t('chatBossDontAttack', {username: group.chat[i].info.user, bossName: questScrolls[group.chat[i].info.quest].boss.name(lang), userDamage: group.chat[i].info.userDamage}, lang)}\``; break; case 'boss_rage': - group.chat[i].text = `\`${questScrolls[group.chat[i].info.quest].boss.rage.effect(user.preferences.language)}\``; + msg = `\`${questScrolls[group.chat[i].info.quest].boss.rage.effect(lang)}\``; break; case 'boss_defeated': - group.chat[i].text = `\`${shared.i18n.t('chatBossDefeated', {bossName: questScrolls[group.chat[i].info.quest].boss.name(user.preferences.language)}, user.preferences.language)}\``; + msg = `\`${shared.i18n.t('chatBossDefeated', {bossName: questScrolls[group.chat[i].info.quest].boss.name(lang)}, lang)}\``; break; case 'user_found_items': foundText = _.reduce(group.chat[i].info.items, (m, v, k) => { - m.push(`${v} ${questScrolls[group.chat[i].info.quest].collect[k].text(user.preferences.language)}`); + m.push(`${v} ${questScrolls[group.chat[i].info.quest].collect[k].text(lang)}`); return m; }, []).join(', '); - group.chat[i].text = `\`${shared.i18n.t('chatFindItems', {username: group.chat[i].info.user, items: foundText}, user.preferences.language)}\``; + msg = `\`${shared.i18n.t('chatFindItems', {username: group.chat[i].info.user, items: foundText}, lang)}\``; break; case 'all_items_found': - group.chat[i].text = `\`${shared.i18n.t('chatItemQuestFinish', user.preferences.language)}\``; + msg = `\`${shared.i18n.t('chatItemQuestFinish', lang)}\``; break; case 'spell_cast_party': - group.chat[i].text = `\`${shared.i18n.t('chatCastSpellParty', {username: group.chat[i].info.user, spell: shared.content.spells[group.chat[i].info.class][group.chat[i].info.spell].text(user.preferences.language)}, user.preferences.language)}\``; + msg = `\`${shared.i18n.t('chatCastSpellParty', {username: group.chat[i].info.user, spell: shared.content.spells[group.chat[i].info.class][group.chat[i].info.spell].text(lang)}, lang)}\``; break; case 'spell_cast_user': - group.chat[i].text = `\`${shared.i18n.t('chatCastSpellUser', {username: group.chat[i].info.user, spell: shared.content.spells[group.chat[i].info.class][group.chat[i].info.spell].text(user.preferences.language), target: group.chat[i].info.target}, user.preferences.language)}\``; + msg = `\`${shared.i18n.t('chatCastSpellUser', {username: group.chat[i].info.user, spell: shared.content.spells[group.chat[i].info.class][group.chat[i].info.spell].text(lang), target: group.chat[i].info.target}, lang)}\``; break; case 'quest_abort': - group.chat[i].text = `\`${shared.i18n.t('chatQuestAborted', {username: group.chat[i].info.user, questName: questScrolls[group.chat[i].info.quest].text(user.preferences.language)}, user.preferences.language)}\``; + msg = `\`${shared.i18n.t('chatQuestAborted', {username: group.chat[i].info.user, questName: questScrolls[group.chat[i].info.quest].text(lang)}, lang)}\``; break; case 'tavern_quest_completed': - group.chat[i].text = `\`${shared.content.quests[group.chat[i].info.quest].completionChat(user.preferences.language)}\``; + msg = `\`${shared.content.quests[group.chat[i].info.quest].completionChat(lang)}\``; break; case 'tavern_boss_rage_tired': - group.chat[i].text = `\`${shared.i18n.t('tavernBossTired', {rageName: shared.content.quests[group.chat[i].info.quest].boss.rage.title(user.preferences.language), bossName: shared.content.quests[group.chat[i].info.quest].boss.name(user.preferences.language)}, user.preferences.language)}\``; + msg = `\`${shared.i18n.t('tavernBossTired', {rageName: shared.content.quests[group.chat[i].info.quest].boss.rage.title(lang), bossName: shared.content.quests[group.chat[i].info.quest].boss.name(lang)}, lang)}\``; break; case 'tavern_boss_rage': - group.chat[i].text = `\`${shared.content.quests[group.chat[i].info.quest].boss.rage[group.chat[i].info.scene](user.preferences.language)}\``; + msg = `\`${shared.content.quests[group.chat[i].info.quest].boss.rage[group.chat[i].info.scene](lang)}\``; break; case 'tavern_boss_rage_effect': - group.chat[i].text = `\`${shared.content.quests[group.chat[i].info.quest].boss.rage.effect(user.preferences.language)}\``; + msg = `\`${shared.content.quests[group.chat[i].info.quest].boss.rage.effect(lang)}\``; break; case 'tavern_boss_desperation': - group.chat[i].text = `\`${shared.content.quests[group.chat[i].info.quest].boss.desperation.text(user.preferences.language)}\``; + msg = `\`${shared.content.quests[group.chat[i].info.quest].boss.desperation.text(lang)}\``; break; case 'claim_task': - group.chat[i].text = `${shared.i18n.t('userIsClamingTask', {username: group.chat[i].info.user, task: group.chat[i].info.task}, user.preferences.language)}`; + msg = `${shared.i18n.t('userIsClamingTask', {username: group.chat[i].info.user, task: group.chat[i].info.task}, lang)}`; break; } + group.chat[i].text = msg; } } return group; From 9d41fb02524b9913d8e1471bc4740ee48a5594f5 Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Sat, 24 Mar 2018 23:57:02 +0900 Subject: [PATCH 19/31] Add tests + some small adjusts --- test/api/v3/unit/models/group.test.js | 202 +++++++++++++++++++++++++- test/helpers/api-unit.helper.js | 1 + test/helpers/translate.js | 6 + website/server/models/group.js | 11 +- 4 files changed, 213 insertions(+), 7 deletions(-) diff --git a/test/api/v3/unit/models/group.test.js b/test/api/v3/unit/models/group.test.js index fff28e4cc9..2e8144a095 100644 --- a/test/api/v3/unit/models/group.test.js +++ b/test/api/v3/unit/models/group.test.js @@ -1,7 +1,7 @@ import moment from 'moment'; import { v4 as generateUUID } from 'uuid'; import validator from 'validator'; -import { sleep } from '../../../../helpers/api-unit.helper'; +import { sleep, translationCheck } from '../../../../helpers/api-unit.helper'; import { SPAM_MESSAGE_LIMIT, SPAM_MIN_EXEMPT_CONTRIB_LEVEL, @@ -636,6 +636,206 @@ describe('Group Model', () => { expect(res.t).to.not.be.called; }); }); + + describe('translateSystemMessages', () => { + it('translate quest_start', () => { + questLeader.preferences.language = 'en'; + party.chat = [{ + info: { + type: 'quest_start', + quest: 'basilist', + }, + }]; + party = Group.translateSystemMessages(party, questLeader); + translationCheck(party.chat[0].text); + }); + + it('translate boss_damage', () => { + questLeader.preferences.language = 'en'; + party.chat = [{ + info: { + type: 'boss_damage', + user: questLeader.profile.name, + quest: 'basilist', + userDamage: 15.3, + bossDamage: 3.7, + }, + }]; + party = Group.translateSystemMessages(party, questLeader); + translationCheck(party.chat[0].text); + }); + + it('translate boss_dont_attack', () => { + questLeader.preferences.language = 'en'; + party.chat = [{ + info: { + type: 'boss_dont_attack', + user: questLeader.profile.name, + quest: 'basilist', + userDamage: 15.3, + }, + }]; + party = Group.translateSystemMessages(party, questLeader); + translationCheck(party.chat[0].text); + }); + + it('translate boss_rage', () => { + questLeader.preferences.language = 'en'; + party.chat = [{ + info: { + type: 'boss_rage', + quest: 'lostMasterclasser3', + }, + }]; + party = Group.translateSystemMessages(party, questLeader); + translationCheck(party.chat[0].text); + }); + + it('translate boss_defeated', () => { + questLeader.preferences.language = 'en'; + party.chat = [{ + info: { + type: 'boss_defeated', + quest: 'lostMasterclasser3', + }, + }]; + party = Group.translateSystemMessages(party, questLeader); + translationCheck(party.chat[0].text); + }); + + it('translate user_found_items', () => { + questLeader.preferences.language = 'en'; + party.chat = [{ + info: { + type: 'user_found_items', + user: questLeader.profile.name, + quest: 'lostMasterclasser1', + items: { + ancientTome: 3, + forbiddenTome: 2, + hiddenTome: 1, + }, + }, + }]; + party = Group.translateSystemMessages(party, questLeader); + translationCheck(party.chat[0].text); + }); + + it('translate all_items_found', () => { + questLeader.preferences.language = 'en'; + party.chat = [{ + info: { + type: 'all_items_found', + }, + }]; + party = Group.translateSystemMessages(party, questLeader); + translationCheck(party.chat[0].text); + }); + + it('translate spell_cast_party', () => { + questLeader.preferences.language = 'en'; + party.chat = [{ + info: { + type: 'spell_cast_party', + user: questLeader.profile.name, + class: 'wizard', + spell: 'earth', + }, + }]; + party = Group.translateSystemMessages(party, questLeader); + translationCheck(party.chat[0].text); + }); + + it('translate spell_cast_user', () => { + questLeader.preferences.language = 'en'; + party.chat = [{ + info: { + type: 'spell_cast_user', + user: questLeader.profile.name, + class: 'special', + spell: 'snowball', + target: participatingMember.profile.name, + }, + }]; + party = Group.translateSystemMessages(party, questLeader); + translationCheck(party.chat[0].text); + }); + + it('translate quest_abort', () => { + questLeader.preferences.language = 'en'; + party.chat = [{ + info: { + type: 'quest_abort', + user: questLeader.profile.name, + quest: 'basilist', + }, + }]; + party = Group.translateSystemMessages(party, questLeader); + translationCheck(party.chat[0].text); + }); + + it('translate tavern_quest_completed', () => { + questLeader.preferences.language = 'en'; + party.chat = [{ + info: { + type: 'tavern_quest_completed', + quest: 'stressbeast', + }, + }]; + party = Group.translateSystemMessages(party, questLeader); + translationCheck(party.chat[0].text); + }); + + it('translate tavern_boss_rage_tired', () => { + questLeader.preferences.language = 'en'; + party.chat = [{ + info: { + type: 'tavern_boss_rage_tired', + quest: 'stressbeast', + }, + }]; + party = Group.translateSystemMessages(party, questLeader); + translationCheck(party.chat[0].text); + }); + + it('translate tavern_boss_rage', () => { + questLeader.preferences.language = 'en'; + party.chat = [{ + info: { + type: 'tavern_boss_rage', + quest: 'dysheartener', + scene: 'market', + }, + }]; + party = Group.translateSystemMessages(party, questLeader); + translationCheck(party.chat[0].text); + }); + + it('translate tavern_boss_desperation', () => { + questLeader.preferences.language = 'en'; + party.chat = [{ + info: { + type: 'tavern_boss_desperation', + quest: 'stressbeast', + }, + }]; + party = Group.translateSystemMessages(party, questLeader); + translationCheck(party.chat[0].text); + }); + + it('translate claim_task', () => { + questLeader.preferences.language = 'en'; + party.chat = [{ + info: { + type: 'claim_task', + user: questLeader.profile.name, + task: 'Feed the pet', + }, + }]; + party = Group.translateSystemMessages(party, questLeader); + translationCheck(party.chat[0].text); + }); + }); }); context('Instance Methods', () => { diff --git a/test/helpers/api-unit.helper.js b/test/helpers/api-unit.helper.js index d32423dfb9..e248d3d6f8 100644 --- a/test/helpers/api-unit.helper.js +++ b/test/helpers/api-unit.helper.js @@ -8,6 +8,7 @@ import mongo from './mongo'; // eslint-disable-line import moment from 'moment'; import i18n from '../../website/common/script/i18n'; import * as Tasks from '../../website/server/models/task'; +export { translationCheck } from './translate'; afterEach((done) => { sandbox.restore(); diff --git a/test/helpers/translate.js b/test/helpers/translate.js index e146c27a71..c12e97c147 100644 --- a/test/helpers/translate.js +++ b/test/helpers/translate.js @@ -16,3 +16,9 @@ export function translate (key, variables, language) { return translatedString; } + +export function translationCheck (translatedString) { + expect(translatedString).to.not.be.empty; + expect(translatedString).to.not.eql(STRING_ERROR_MSG); + expect(translatedString).to.not.match(STRING_DOES_NOT_EXIST_MSG); +} diff --git a/website/server/models/group.js b/website/server/models/group.js index a4e7e94407..d418d8a2da 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -315,7 +315,7 @@ schema.statics.getGroups = async function getGroups (options = {}) { return groupsArray; }; -function translateSystemMessages (group, user) { +function _translateSystemMessages (group, user) { let foundText = ''; let lang = user.preferences.language; for (let i = 0; i < group.chat.length; i++) { @@ -365,9 +365,6 @@ function translateSystemMessages (group, user) { case 'tavern_boss_rage': msg = `\`${shared.content.quests[group.chat[i].info.quest].boss.rage[group.chat[i].info.scene](lang)}\``; break; - case 'tavern_boss_rage_effect': - msg = `\`${shared.content.quests[group.chat[i].info.quest].boss.rage.effect(lang)}\``; - break; case 'tavern_boss_desperation': msg = `\`${shared.content.quests[group.chat[i].info.quest].boss.desperation.text(lang)}\``; break; @@ -381,12 +378,14 @@ function translateSystemMessages (group, user) { return group; } +schema.statics.translateSystemMessages = _translateSystemMessages; + // When converting to json remove chat messages with more than 1 flag and remove all flags info // unless the user is an admin or said chat is posted by that user // Not putting into toJSON because there we can't access user // It also removes the _meta field that can be stored inside a chat message schema.statics.toJSONCleanChat = function groupToJSONCleanChat (group, user) { - group = translateSystemMessages(group, user); + group = _translateSystemMessages(group, user); let toJSON = group.toJSON(); @@ -993,7 +992,7 @@ schema.methods._processBossQuest = async function processBossQuest (options) { group.quest.progress.rage += Math.abs(down); if (group.quest.progress.rage >= quest.boss.rage.value) { group.sendChat(quest.boss.rage.effect('en'), null, null, { - type: 'tavern_boss_rage_effect', + type: 'boss_rage', quest: quest.key, }); group.quest.progress.rage = 0; From f09274225a883e06736799231fc9cb2e5c318515 Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Wed, 25 Apr 2018 21:28:02 +0900 Subject: [PATCH 20/31] Update chat schema: add 'info' field --- website/server/models/chat.js | 1 + 1 file changed, 1 insertion(+) diff --git a/website/server/models/chat.js b/website/server/models/chat.js index cbddeb0361..c5789ecbb6 100644 --- a/website/server/models/chat.js +++ b/website/server/models/chat.js @@ -5,6 +5,7 @@ const schema = new mongoose.Schema({ timestamp: Date, user: String, text: String, + info: {type: mongoose.Schema.Types.Mixed}, contributor: {type: mongoose.Schema.Types.Mixed}, backer: {type: mongoose.Schema.Types.Mixed}, uuid: String, From 97e80e2093f8601d275698c1f57ea5447aef295e Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Fri, 18 May 2018 23:08:37 +0900 Subject: [PATCH 21/31] Implemented requested changes --- .../POST-groups_groupid_quests_abort.test.js | 9 +- test/api/v3/unit/models/group.test.js | 116 +++++--- website/server/controllers/api-v3/chat.js | 2 +- website/server/controllers/api-v3/quests.js | 11 +- .../server/controllers/api-v3/tasks/groups.js | 11 +- .../server/controllers/api-v3/user/spells.js | 28 +- website/server/models/group.js | 261 +++++++++++------- 7 files changed, 284 insertions(+), 154 deletions(-) diff --git a/test/api/v3/integration/quests/POST-groups_groupid_quests_abort.test.js b/test/api/v3/integration/quests/POST-groups_groupid_quests_abort.test.js index d90e4c7b0a..d5278ed9a8 100644 --- a/test/api/v3/integration/quests/POST-groups_groupid_quests_abort.test.js +++ b/test/api/v3/integration/quests/POST-groups_groupid_quests_abort.test.js @@ -127,7 +127,14 @@ describe('POST /groups/:groupId/quests/abort', () => { members: {}, }); expect(Group.prototype.sendChat).to.be.calledOnce; - expect(Group.prototype.sendChat).to.be.calledWithMatch(/aborted the party quest Wail of the Whale.`/); + expect(Group.prototype.sendChat).to.be.calledWithMatch({ + // message: /aborted the party quest Wail of the Whale.`/, + info: { + quest: 'whale', + type: 'quest_abort', + // user: '1526651788146ce91216' + }, + }); stub.restore(); }); diff --git a/test/api/v3/unit/models/group.test.js b/test/api/v3/unit/models/group.test.js index f4c8b5776e..700491cae4 100644 --- a/test/api/v3/unit/models/group.test.js +++ b/test/api/v3/unit/models/group.test.js @@ -205,7 +205,16 @@ describe('Group Model', () => { party = await Group.findOne({_id: party._id}); expect(Group.prototype.sendChat).to.be.calledOnce; - expect(Group.prototype.sendChat).to.be.calledWith('`Participating Member attacks Wailing Whale for 5.0 damage. Wailing Whale attacks party for 7.5 damage.`'); + expect(Group.prototype.sendChat).to.be.calledWith({ + message: '`Participating Member attacks Wailing Whale for 5.0 damage. Wailing Whale attacks party for 7.5 damage.`', + info: { + bossDamage: '7.5', + quest: 'whale', + type: 'boss_damage', + user: 'Participating Member', + userDamage: '5.0', + }, + }); }); it('applies damage only to participating members of party', async () => { @@ -271,7 +280,10 @@ describe('Group Model', () => { party = await Group.findOne({_id: party._id}); expect(Group.prototype.sendChat).to.be.calledTwice; - expect(Group.prototype.sendChat).to.be.calledWith('`You defeated Wailing Whale! Questing party members receive the rewards of victory.`'); + expect(Group.prototype.sendChat).to.be.calledWith({ + message: '`You defeated Wailing Whale! Questing party members receive the rewards of victory.`', + info: { quest: 'whale', type: 'boss_defeated' }, + }); }); it('calls finishQuest when boss has <= 0 hp', async () => { @@ -314,7 +326,10 @@ describe('Group Model', () => { party = await Group.findOne({_id: party._id}); - expect(Group.prototype.sendChat).to.be.calledWith(quest.boss.rage.effect('en')); + expect(Group.prototype.sendChat).to.be.calledWith({ + message: quest.boss.rage.effect('en'), + info: { quest: 'trex_undead', type: 'boss_rage' }, + }); expect(party.quest.progress.hp).to.eql(383.5); expect(party.quest.progress.rage).to.eql(0); }); @@ -364,7 +379,10 @@ describe('Group Model', () => { party = await Group.findOne({_id: party._id}); - expect(Group.prototype.sendChat).to.be.calledWith(quest.boss.rage.effect('en')); + expect(Group.prototype.sendChat).to.be.calledWith({ + message: quest.boss.rage.effect('en'), + info: { quest: 'lostMasterclasser4', type: 'boss_rage' }, + }); expect(party.quest.progress.rage).to.eql(0); let drainedUser = await User.findById(participatingMember._id); @@ -415,7 +433,15 @@ describe('Group Model', () => { party = await Group.findOne({_id: party._id}); expect(Group.prototype.sendChat).to.be.calledOnce; - expect(Group.prototype.sendChat).to.be.calledWith('`Participating Member found 5 Bars of Soap.`'); + expect(Group.prototype.sendChat).to.be.calledWith({ + message: '`Participating Member found 5 Bars of Soap.`', + info: { + items: { soapBars: 5 }, + quest: 'atom1', + type: 'user_found_items', + user: 'Participating Member', + }, + }); }); it('sends a chat message if no progress is made', async () => { @@ -426,7 +452,15 @@ describe('Group Model', () => { party = await Group.findOne({_id: party._id}); expect(Group.prototype.sendChat).to.be.calledOnce; - expect(Group.prototype.sendChat).to.be.calledWith('`Participating Member found 0 Bars of Soap.`'); + expect(Group.prototype.sendChat).to.be.calledWith({ + message: '`Participating Member found 0 Bars of Soap.`', + info: { + items: { soapBars: 0 }, + quest: 'atom1', + type: 'user_found_items', + user: 'Participating Member', + }, + }); }); it('sends a chat message if no progress is made on quest with multiple items', async () => { @@ -443,9 +477,15 @@ describe('Group Model', () => { party = await Group.findOne({_id: party._id}); expect(Group.prototype.sendChat).to.be.calledOnce; - expect(Group.prototype.sendChat).to.be.calledWithMatch(/`Participating Member found/); - expect(Group.prototype.sendChat).to.be.calledWithMatch(/0 Blue Fins/); - expect(Group.prototype.sendChat).to.be.calledWithMatch(/0 Fire Coral/); + expect(Group.prototype.sendChat).to.be.calledWith({ + message: '`Participating Member found 0 Fire Coral, 0 Blue Fins.`', + info: { + items: { blueFins: 0, fireCoral: 0 }, + quest: 'dilatoryDistress1', + type: 'user_found_items', + user: 'Participating Member', + }, + }); }); it('handles collection quests with multiple items', async () => { @@ -462,8 +502,15 @@ describe('Group Model', () => { party = await Group.findOne({_id: party._id}); expect(Group.prototype.sendChat).to.be.calledOnce; - expect(Group.prototype.sendChat).to.be.calledWithMatch(/`Participating Member found/); - expect(Group.prototype.sendChat).to.be.calledWithMatch(/\d* (Tracks|Broken Twigs)/); + expect(Group.prototype.sendChat).to.be.calledWithMatch({ + // message: "`Participating Member found \d* Broken Twigs, \d* Tracks.`", + info: { + // items: { branches: \d*, tracks: \d* }, + quest: 'evilsanta2', + type: 'user_found_items', + user: 'Participating Member', + }, + }); }); it('sends message about victory', async () => { @@ -474,7 +521,10 @@ describe('Group Model', () => { party = await Group.findOne({_id: party._id}); expect(Group.prototype.sendChat).to.be.calledTwice; - expect(Group.prototype.sendChat).to.be.calledWith('`All items found! Party has received their rewards.`'); + expect(Group.prototype.sendChat).to.be.calledWith({ + message: '`All items found! Party has received their rewards.`', + info: { type: 'all_items_found' }, + }); }); it('calls finishQuest when all items are found', async () => { @@ -1123,20 +1173,22 @@ describe('Group Model', () => { }); it('formats message', () => { - const chatMessage = party.sendChat('a new message', { - _id: 'user-id', - profile: { name: 'user name' }, - contributor: { - toObject () { - return 'contributor object'; + const chatMessage = party.sendChat({ + message: 'a new message', user: { + _id: 'user-id', + profile: { name: 'user name' }, + contributor: { + toObject () { + return 'contributor object'; + }, }, - }, - backer: { - toObject () { - return 'backer object'; + backer: { + toObject () { + return 'backer object'; + }, }, - }, - }); + }} + ); const chat = chatMessage; @@ -1153,7 +1205,7 @@ describe('Group Model', () => { }); it('formats message as system if no user is passed in', () => { - const chat = party.sendChat('a system message'); + const chat = party.sendChat({message: 'a system message'}); expect(chat.text).to.eql('a system message'); expect(validator.isUUID(chat.id)).to.eql(true); @@ -1174,7 +1226,7 @@ describe('Group Model', () => { expect(party.chat).to.have.a.lengthOf(220); - party.sendChat('message'); + party.sendChat({message: 'message'}); expect(party.chat).to.have.a.lengthOf(200); }); @@ -1188,13 +1240,13 @@ describe('Group Model', () => { expect(party.chat).to.have.a.lengthOf(420); - party.sendChat('message'); + party.sendChat({message: 'message'}); expect(party.chat).to.have.a.lengthOf(400); }); it('updates users about new messages in party', () => { - party.sendChat('message'); + party.sendChat({message: 'message'}); expect(User.update).to.be.calledOnce; expect(User.update).to.be.calledWithMatch({ @@ -1208,7 +1260,7 @@ describe('Group Model', () => { type: 'guild', }); - group.sendChat('message'); + group.sendChat({message: 'message'}); expect(User.update).to.be.calledOnce; expect(User.update).to.be.calledWithMatch({ @@ -1218,7 +1270,7 @@ describe('Group Model', () => { }); it('does not send update to user that sent the message', () => { - party.sendChat('message', {_id: 'user-id', profile: { name: 'user' }}); + party.sendChat({message: 'message', user: {_id: 'user-id', profile: { name: 'user' }}}); expect(User.update).to.be.calledOnce; expect(User.update).to.be.calledWithMatch({ @@ -1230,7 +1282,7 @@ describe('Group Model', () => { it('skips sending new message notification for guilds with > 5000 members', () => { party.memberCount = 5001; - party.sendChat('message'); + party.sendChat({message: 'message'}); expect(User.update).to.not.be.called; }); @@ -1238,7 +1290,7 @@ describe('Group Model', () => { it('skips sending messages to the tavern', () => { party._id = TAVERN_ID; - party.sendChat('message'); + party.sendChat({message: 'message'}); expect(User.update).to.not.be.called; }); diff --git a/website/server/controllers/api-v3/chat.js b/website/server/controllers/api-v3/chat.js index 34a3868efe..cf1635ad7e 100644 --- a/website/server/controllers/api-v3/chat.js +++ b/website/server/controllers/api-v3/chat.js @@ -179,7 +179,7 @@ api.postChat = { throw new NotAuthorized(res.t('messageGroupChatSpam')); } - const newChatMessage = group.sendChat(req.body.message, user); + const newChatMessage = group.sendChat({message: req.body.message, user}); let toSave = [newChatMessage.save()]; if (group.type === 'party') { diff --git a/website/server/controllers/api-v3/quests.js b/website/server/controllers/api-v3/quests.js index 5af563611b..dba4f5f6ad 100644 --- a/website/server/controllers/api-v3/quests.js +++ b/website/server/controllers/api-v3/quests.js @@ -434,10 +434,13 @@ api.abortQuest = { if (user._id !== group.leader && user._id !== group.quest.leader) throw new NotAuthorized(res.t('onlyLeaderAbortQuest')); let questName = questScrolls[group.quest.key].text('en'); - const newChatMessage = group.sendChat(`\`${common.i18n.t('chatQuestAborted', {username: user.profile.name, questName}, 'en')}\``, null, null, { - type: 'quest_abort', - user: user.profile.name, - quest: group.quest.key, + const newChatMessage = group.sendChat({ + message: `\`${common.i18n.t('chatQuestAborted', {username: user.profile.name, questName}, 'en')}\``, + info: { + type: 'quest_abort', + user: user.profile.name, + quest: group.quest.key, + }, }); await newChatMessage.save(); diff --git a/website/server/controllers/api-v3/tasks/groups.js b/website/server/controllers/api-v3/tasks/groups.js index a349e1250a..b632da066b 100644 --- a/website/server/controllers/api-v3/tasks/groups.js +++ b/website/server/controllers/api-v3/tasks/groups.js @@ -209,10 +209,13 @@ api.assignTask = { // User is claiming the task if (user._id === assignedUserId) { let message = res.t('userIsClamingTask', {username: user.profile.name, task: task.text}); - const newMessage = group.sendChat(message, null, null, { - type: 'claim_task', - user: user.profile.name, - task: task.text, + const newMessage = group.sendChat({ + message, + info: { + type: 'claim_task', + user: user.profile.name, + task: task.text, + }, }); promises.push(newMessage.save()); } diff --git a/website/server/controllers/api-v3/user/spells.js b/website/server/controllers/api-v3/user/spells.js index 59ac561359..c7c0492d26 100644 --- a/website/server/controllers/api-v3/user/spells.js +++ b/website/server/controllers/api-v3/user/spells.js @@ -131,20 +131,26 @@ api.castSpell = { if (party && !spell.silent) { if (targetType === 'user') { - const newChatMessage = party.sendChat(`\`${common.i18n.t('chatCastSpellUser', {username: user.profile.name, spell: spell.text(), target: partyMembers.profile.name}, 'en')}\``, null, null, { - type: 'spell_cast_user', - user: user.profile.name, - class: klass, - spell: spellId, - target: partyMembers.profile.name, + const newChatMessage = party.sendChat({ + message: `\`${common.i18n.t('chatCastSpellUser', {username: user.profile.name, spell: spell.text(), target: partyMembers.profile.name}, 'en')}\``, + info: { + type: 'spell_cast_user', + user: user.profile.name, + class: klass, + spell: spellId, + target: partyMembers.profile.name, + }, }); await newChatMessage.save(); } else { - const newChatMessage = party.sendChat(`\`${common.i18n.t('chatCastSpellParty', {username: user.profile.name, spell: spell.text()}, 'en')}\``, null, null, { - type: 'spell_cast_party', - user: user.profile.name, - class: klass, - spell: spellId, + const newChatMessage = party.sendChat({ + message: `\`${common.i18n.t('chatCastSpellParty', {username: user.profile.name, spell: spell.text()}, 'en')}\``, + info: { + type: 'spell_cast_party', + user: user.profile.name, + class: klass, + spell: spellId, + }, }); await newChatMessage.save(); } diff --git a/website/server/models/group.js b/website/server/models/group.js index 001e03f895..ffc72778ce 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -323,66 +323,90 @@ schema.statics.getGroups = async function getGroups (options = {}) { return groupsArray; }; -function _translateSystemMessages (group, user) { +function _translateMessage (lang, info) { + let msg; let foundText = ''; - let lang = user.preferences.language; - for (let i = 0; i < group.chat.length; i++) { - if (!_.isEmpty(group.chat[i].info)) { - let msg; - switch (group.chat[i].info.type) { - case 'quest_start': - msg = `\`${shared.i18n.t('chatQuestStarted', {questName: questScrolls[group.chat[i].info.quest].text(lang)}, lang)}\``; - break; - case 'boss_damage': - msg = `\`${shared.i18n.t('chatBossDamage', {username: group.chat[i].info.user, bossName: questScrolls[group.chat[i].info.quest].boss.name(lang), userDamage: group.chat[i].info.userDamage, bossDamage: group.chat[i].info.bossDamage}, lang)}\``; - break; - case 'boss_dont_attack': - msg = `\`${shared.i18n.t('chatBossDontAttack', {username: group.chat[i].info.user, bossName: questScrolls[group.chat[i].info.quest].boss.name(lang), userDamage: group.chat[i].info.userDamage}, lang)}\``; - break; - case 'boss_rage': - msg = `\`${questScrolls[group.chat[i].info.quest].boss.rage.effect(lang)}\``; - break; - case 'boss_defeated': - msg = `\`${shared.i18n.t('chatBossDefeated', {bossName: questScrolls[group.chat[i].info.quest].boss.name(lang)}, lang)}\``; - break; - case 'user_found_items': - foundText = _.reduce(group.chat[i].info.items, (m, v, k) => { - m.push(`${v} ${questScrolls[group.chat[i].info.quest].collect[k].text(lang)}`); - return m; - }, []).join(', '); - msg = `\`${shared.i18n.t('chatFindItems', {username: group.chat[i].info.user, items: foundText}, lang)}\``; - break; - case 'all_items_found': - msg = `\`${shared.i18n.t('chatItemQuestFinish', lang)}\``; - break; - case 'spell_cast_party': - msg = `\`${shared.i18n.t('chatCastSpellParty', {username: group.chat[i].info.user, spell: shared.content.spells[group.chat[i].info.class][group.chat[i].info.spell].text(lang)}, lang)}\``; - break; - case 'spell_cast_user': - msg = `\`${shared.i18n.t('chatCastSpellUser', {username: group.chat[i].info.user, spell: shared.content.spells[group.chat[i].info.class][group.chat[i].info.spell].text(lang), target: group.chat[i].info.target}, lang)}\``; - break; - case 'quest_abort': - msg = `\`${shared.i18n.t('chatQuestAborted', {username: group.chat[i].info.user, questName: questScrolls[group.chat[i].info.quest].text(lang)}, lang)}\``; - break; - case 'tavern_quest_completed': - msg = `\`${shared.content.quests[group.chat[i].info.quest].completionChat(lang)}\``; - break; - case 'tavern_boss_rage_tired': - msg = `\`${shared.i18n.t('tavernBossTired', {rageName: shared.content.quests[group.chat[i].info.quest].boss.rage.title(lang), bossName: shared.content.quests[group.chat[i].info.quest].boss.name(lang)}, lang)}\``; - break; - case 'tavern_boss_rage': - msg = `\`${shared.content.quests[group.chat[i].info.quest].boss.rage[group.chat[i].info.scene](lang)}\``; - break; - case 'tavern_boss_desperation': - msg = `\`${shared.content.quests[group.chat[i].info.quest].boss.desperation.text(lang)}\``; - break; - case 'claim_task': - msg = `${shared.i18n.t('userIsClamingTask', {username: group.chat[i].info.user, task: group.chat[i].info.task}, lang)}`; - break; - } - group.chat[i].text = msg; - } + let spells = shared.content.spells; + let quests = shared.content.quests; + + switch (info.type) { + case 'quest_start': + msg = `\`${shared.i18n.t('chatQuestStarted', {questName: questScrolls[info.quest].text(lang)}, lang)}\``; + break; + + case 'boss_damage': + msg = `\`${shared.i18n.t('chatBossDamage', {username: info.user, bossName: questScrolls[info.quest].boss.name(lang), userDamage: info.userDamage, bossDamage: info.bossDamage}, lang)}\``; + break; + + case 'boss_dont_attack': + msg = `\`${shared.i18n.t('chatBossDontAttack', {username: info.user, bossName: questScrolls[info.quest].boss.name(lang), userDamage: info.userDamage}, lang)}\``; + break; + + case 'boss_rage': + msg = `\`${questScrolls[info.quest].boss.rage.effect(lang)}\``; + break; + + case 'boss_defeated': + msg = `\`${shared.i18n.t('chatBossDefeated', {bossName: questScrolls[info.quest].boss.name(lang)}, lang)}\``; + break; + + case 'user_found_items': + foundText = _.reduce(info.items, (m, v, k) => { + m.push(`${v} ${questScrolls[info.quest].collect[k].text(lang)}`); + return m; + }, []).join(', '); + msg = `\`${shared.i18n.t('chatFindItems', {username: info.user, items: foundText}, lang)}\``; + break; + + case 'all_items_found': + msg = `\`${shared.i18n.t('chatItemQuestFinish', lang)}\``; + break; + + case 'spell_cast_party': + msg = `\`${shared.i18n.t('chatCastSpellParty', {username: info.user, spell: spells[info.class][info.spell].text(lang)}, lang)}\``; + break; + + case 'spell_cast_user': + msg = `\`${shared.i18n.t('chatCastSpellUser', {username: info.user, spell: spells[info.class][info.spell].text(lang), target: info.target}, lang)}\``; + break; + + case 'quest_abort': + msg = `\`${shared.i18n.t('chatQuestAborted', {username: info.user, questName: questScrolls[info.quest].text(lang)}, lang)}\``; + break; + + case 'tavern_quest_completed': + msg = `\`${quests[info.quest].completionChat(lang)}\``; + break; + + case 'tavern_boss_rage_tired': + msg = `\`${shared.i18n.t('tavernBossTired', {rageName: quests[info.quest].boss.rage.title(lang), bossName: quests[info.quest].boss.name(lang)}, lang)}\``; + break; + + case 'tavern_boss_rage': + msg = `\`${quests[info.quest].boss.rage[info.scene](lang)}\``; + break; + + case 'tavern_boss_desperation': + msg = `\`${quests[info.quest].boss.desperation.text(lang)}\``; + break; + + case 'claim_task': + msg = `${shared.i18n.t('userIsClamingTask', {username: info.user, task: info.task}, lang)}`; + break; } + + return msg; +} + +function _translateSystemMessages (group, user) { + let lang = user.preferences.language; + + group.chat.forEach((chat, i, chatArray) => { + if (!_.isEmpty(chat.info)) { + chatArray[i].text = _translateMessage(lang, chat.info); + } + }); + return group; } @@ -612,7 +636,8 @@ function setUserStyles (newMessage, user) { newMessage.markModified('userStyles'); } -schema.methods.sendChat = function sendChat (message, user, metaData, info = {}) { +schema.methods.sendChat = function sendChat (options = {}) { + const {message, user, metaData, info = {}} = options; let newMessage = chatDefaults(message, user, info); let newChatMessage = new Chat(); newChatMessage = Object.assign(newChatMessage, newMessage); @@ -785,11 +810,15 @@ schema.methods.startQuest = async function startQuest (user) { }, }, { multi: true }).exec(); - const newMessage = this.sendChat(`\`${shared.i18n.t('chatQuestStarted', {questName: quest.text('en')}, 'en')}\``, null, { - participatingMembers: this.getParticipatingQuestMembers().join(', '), - }, { - type: 'quest_start', - quest: quest.key, + const newMessage = this.sendChat({ + message: `\`${shared.i18n.t('chatQuestStarted', {questName: quest.text('en')}, 'en')}\``, + metaData: { + participatingMembers: this.getParticipatingQuestMembers().join(', '), + }, + info: { + type: 'quest_start', + quest: quest.key, + }, }); await newMessage.save(); @@ -1047,20 +1076,26 @@ schema.methods._processBossQuest = async function processBossQuest (options) { group.quest.progress.hp -= progress.up; if (CRON_SAFE_MODE || CRON_SEMI_SAFE_MODE) { - const groupMessage = group.sendChat(`\`${shared.i18n.t('chatBossDontAttack', {bossName: quest.boss.name('en')}, 'en')}\``, null, null, { - type: 'boss_dont_attack', - user: user.profile.name, - quest: group.quest.key, - userDamage: progress.up.toFixed(1), + const groupMessage = group.sendChat({ + message: `\`${shared.i18n.t('chatBossDontAttack', {bossName: quest.boss.name('en')}, 'en')}\``, + info: { + type: 'boss_dont_attack', + user: user.profile.name, + quest: group.quest.key, + userDamage: progress.up.toFixed(1), + }, }); promises.push(groupMessage.save()); } else { - const groupMessage = group.sendChat(`\`${shared.i18n.t('chatBossDamage', {username: user.profile.name, bossName: quest.boss.name('en'), userDamage: progress.up.toFixed(1), bossDamage: Math.abs(down).toFixed(1)}, user.preferences.language)}\``, null, null, { - type: 'boss_damage', - user: user.profile.name, - quest: group.quest.key, - userDamage: progress.up.toFixed(1), - bossDamage: Math.abs(down).toFixed(1), + const groupMessage = group.sendChat({ + message: `\`${shared.i18n.t('chatBossDamage', {username: user.profile.name, bossName: quest.boss.name('en'), userDamage: progress.up.toFixed(1), bossDamage: Math.abs(down).toFixed(1)}, user.preferences.language)}\``, + info: { + type: 'boss_damage', + user: user.profile.name, + quest: group.quest.key, + userDamage: progress.up.toFixed(1), + bossDamage: Math.abs(down).toFixed(1), + }, }); promises.push(groupMessage.save()); } @@ -1069,9 +1104,12 @@ schema.methods._processBossQuest = async function processBossQuest (options) { if (quest.boss.rage) { group.quest.progress.rage += Math.abs(down); if (group.quest.progress.rage >= quest.boss.rage.value) { - const rageMessage = group.sendChat(quest.boss.rage.effect('en'), null, null, { - type: 'boss_rage', - quest: quest.key, + const rageMessage = group.sendChat({ + message: quest.boss.rage.effect('en'), + info: { + type: 'boss_rage', + quest: quest.key, + }, }); promises.push(rageMessage.save()); group.quest.progress.rage = 0; @@ -1100,9 +1138,12 @@ schema.methods._processBossQuest = async function processBossQuest (options) { // Boss slain, finish quest if (group.quest.progress.hp <= 0) { - const questFinishChat = group.sendChat(`\`${shared.i18n.t('chatBossDefeated', {bossName: quest.boss.name('en')}, 'en')}\``, null, null, { - type: 'boss_defeated', - quest: quest.key, + const questFinishChat = group.sendChat({ + message: `\`${shared.i18n.t('chatBossDefeated', {bossName: quest.boss.name('en')}, 'en')}\``, + info: { + type: 'boss_defeated', + quest: quest.key, + }, }); promises.push(questFinishChat.save()); @@ -1156,11 +1197,14 @@ schema.methods._processCollectionQuest = async function processCollectionQuest ( }, []); foundText = foundText.join(', '); - const foundChat = group.sendChat(`\`${shared.i18n.t('chatFindItems', {username: user.profile.name, items: foundText}, 'en')}\``, null, null, { - type: 'user_found_items', - user: user.profile.name, - quest: quest.key, - items: itemsFound, + const foundChat = group.sendChat({ + message: `\`${shared.i18n.t('chatFindItems', {username: user.profile.name, items: foundText}, 'en')}\``, + info: { + type: 'user_found_items', + user: user.profile.name, + quest: quest.key, + items: itemsFound, + }, }); group.markModified('quest.progress.collect'); @@ -1174,8 +1218,11 @@ schema.methods._processCollectionQuest = async function processCollectionQuest ( } await group.finishQuest(quest); - const allItemsFoundChat = group.sendChat(`\`${shared.i18n.t('chatItemQuestFinish', 'en')}\``, null, null, { - type: 'all_items_found', + const allItemsFoundChat = group.sendChat({ + message: `\`${shared.i18n.t('chatItemQuestFinish', 'en')}\``, + info: { + type: 'all_items_found', + }, }); const promises = [group.save(), foundChat.save(), allItemsFoundChat.save()]; @@ -1237,9 +1284,12 @@ schema.statics.tavernBoss = async function tavernBoss (user, progress) { const chatPromises = []; if (tavern.quest.progress.hp <= 0) { - const completeChat = tavern.sendChat(quest.completionChat('en'), null, null, { - type: 'tavern_quest_completed', - quest: quest.key, + const completeChat = tavern.sendChat({ + message: quest.completionChat('en'), + info: { + type: 'tavern_quest_completed', + quest: quest.key, + }, }); chatPromises.push(completeChat.save()); await tavern.finishQuest(quest); @@ -1269,17 +1319,23 @@ schema.statics.tavernBoss = async function tavernBoss (user, progress) { } if (!scene) { - const tiredChat = tavern.sendChat(`\`${shared.i18n.t('tavernBossTired', {rageName: quest.boss.rage.title('en'), bossName: quest.boss.name('en')}, 'en')}\``, null, null, { - type: 'tavern_boss_rage_tired', - quest: quest.key, + const tiredChat = tavern.sendChat({ + message: `\`${shared.i18n.t('tavernBossTired', {rageName: quest.boss.rage.title('en'), bossName: quest.boss.name('en')}, 'en')}\``, + info: { + type: 'tavern_boss_rage_tired', + quest: quest.key, + }, }); chatPromises.push(tiredChat.save()); tavern.quest.progress.rage = 0; // quest.boss.rage.value; } else { - const rageChat = tavern.sendChat(quest.boss.rage[scene]('en'), null, null, { - type: 'tavern_boss_rage', - quest: quest.key, - scene, + const rageChat = tavern.sendChat({ + message: quest.boss.rage[scene]('en'), + info: { + type: 'tavern_boss_rage', + quest: quest.key, + scene, + }, }); chatPromises.push(rageChat.save()); tavern.quest.extra.worldDmg[scene] = true; @@ -1292,9 +1348,12 @@ schema.statics.tavernBoss = async function tavernBoss (user, progress) { } if (quest.boss.desperation && tavern.quest.progress.hp < quest.boss.desperation.threshold && !tavern.quest.extra.desperate) { - const progressChat = tavern.sendChat(quest.boss.desperation.text('en'), null, null, { - type: 'tavern_boss_desperation', - quest: quest.key, + const progressChat = tavern.sendChat({ + message: quest.boss.desperation.text('en'), + info: { + type: 'tavern_boss_desperation', + quest: quest.key, + }, }); chatPromises.push(progressChat.save()); tavern.quest.extra.desperate = true; From 118198b594bba8a8f302690785a1fd7702f8adb6 Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Sat, 19 May 2018 00:26:28 +0900 Subject: [PATCH 22/31] Fixed tests that contains random data --- .../quests/POST-groups_groupid_quests_abort.test.js | 3 +-- test/api/v3/unit/models/group.test.js | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/test/api/v3/integration/quests/POST-groups_groupid_quests_abort.test.js b/test/api/v3/integration/quests/POST-groups_groupid_quests_abort.test.js index d5278ed9a8..a2fc28caea 100644 --- a/test/api/v3/integration/quests/POST-groups_groupid_quests_abort.test.js +++ b/test/api/v3/integration/quests/POST-groups_groupid_quests_abort.test.js @@ -128,11 +128,10 @@ describe('POST /groups/:groupId/quests/abort', () => { }); expect(Group.prototype.sendChat).to.be.calledOnce; expect(Group.prototype.sendChat).to.be.calledWithMatch({ - // message: /aborted the party quest Wail of the Whale.`/, + message: sinon.match(/aborted the party quest Wail of the Whale.`/), info: { quest: 'whale', type: 'quest_abort', - // user: '1526651788146ce91216' }, }); diff --git a/test/api/v3/unit/models/group.test.js b/test/api/v3/unit/models/group.test.js index 700491cae4..b646c74f55 100644 --- a/test/api/v3/unit/models/group.test.js +++ b/test/api/v3/unit/models/group.test.js @@ -503,9 +503,8 @@ describe('Group Model', () => { expect(Group.prototype.sendChat).to.be.calledOnce; expect(Group.prototype.sendChat).to.be.calledWithMatch({ - // message: "`Participating Member found \d* Broken Twigs, \d* Tracks.`", + message: sinon.match(/`Participating Member found/).and(sinon.match(/\d* (Tracks|Broken Twigs)/)), info: { - // items: { branches: \d*, tracks: \d* }, quest: 'evilsanta2', type: 'user_found_items', user: 'Participating Member', From f3f215abea2443347578b93394c7ea72fda1db59 Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Wed, 15 May 2019 13:50:57 +0900 Subject: [PATCH 23/31] Fix typo on schema --- website/server/models/message.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/server/models/message.js b/website/server/models/message.js index 6d7eb8b8d3..93db62a1f9 100644 --- a/website/server/models/message.js +++ b/website/server/models/message.js @@ -7,7 +7,7 @@ const defaultSchema = () => ({ id: String, timestamp: Date, text: String, - info: {type: mongoose.Schema.Types.Mixed}, + info: {$type: mongoose.Schema.Types.Mixed}, // sender properties user: String, // profile name (unfortunately) From 0360326f41485c8a9514b586dc4d9e4ef30d587c Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Wed, 15 May 2019 15:46:32 +0900 Subject: [PATCH 24/31] Change translateSystemMessages to be a class method --- website/server/models/group.js | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/website/server/models/group.js b/website/server/models/group.js index 2dfe866828..038567d321 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -408,20 +408,6 @@ function _translateMessage (lang, info) { return msg; } -function _translateSystemMessages (group, user) { - let lang = user.preferences.language; - - group.chat.forEach((chat, i, chatArray) => { - if (!_.isEmpty(chat.info)) { - chatArray[i].text = _translateMessage(lang, chat.info); - } - }); - - return group; -} - -schema.statics.translateSystemMessages = _translateSystemMessages; - // When converting to json remove chat messages with more than 1 flag and remove all flags info // unless the user is an admin or said chat is posted by that user // Not putting into toJSON because there we can't access user @@ -433,7 +419,7 @@ schema.statics.toJSONCleanChat = async function groupToJSONCleanChat (group, use await getGroupChat(group); } - group = _translateSystemMessages(group, user); + group.translateSystemMessages(group, user); let toJSON = group.toJSON(); @@ -541,6 +527,16 @@ schema.statics.validateInvitations = async function getInvitationError (invites, } }; +schema.methods.translateSystemMessages = function translateSystemMessages (user) { + let lang = user.preferences ? user.preferences.language : 'en'; + + this.chat.forEach((chat, i, chatArray) => { + if (!_.isEmpty(chat.info)) { + chatArray[i].text = _translateMessage(lang, chat.info); + } + }); +}; + schema.methods.getParticipatingQuestMembers = function getParticipatingQuestMembers () { return Object.keys(this.quest.members).filter(member => this.quest.members[member]); }; From e1984762b5d93fda09239d599fe91629f7e84b4c Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Wed, 15 May 2019 16:18:42 +0900 Subject: [PATCH 25/31] Fix some tests --- test/api/unit/models/group.test.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/test/api/unit/models/group.test.js b/test/api/unit/models/group.test.js index 959420daf6..877a3a22fc 100644 --- a/test/api/unit/models/group.test.js +++ b/test/api/unit/models/group.test.js @@ -777,7 +777,7 @@ describe('Group Model', () => { quest: 'basilist', }, }]; - party = Group.translateSystemMessages(party, questLeader); + party.translateSystemMessages(questLeader); translationCheck(party.chat[0].text); }); @@ -792,7 +792,7 @@ describe('Group Model', () => { bossDamage: 3.7, }, }]; - party = Group.translateSystemMessages(party, questLeader); + party.translateSystemMessages(questLeader); translationCheck(party.chat[0].text); }); @@ -806,7 +806,7 @@ describe('Group Model', () => { userDamage: 15.3, }, }]; - party = Group.translateSystemMessages(party, questLeader); + party.translateSystemMessages(questLeader); translationCheck(party.chat[0].text); }); @@ -818,7 +818,7 @@ describe('Group Model', () => { quest: 'lostMasterclasser3', }, }]; - party = Group.translateSystemMessages(party, questLeader); + party.translateSystemMessages(questLeader); translationCheck(party.chat[0].text); }); @@ -830,7 +830,7 @@ describe('Group Model', () => { quest: 'lostMasterclasser3', }, }]; - party = Group.translateSystemMessages(party, questLeader); + party.translateSystemMessages(questLeader); translationCheck(party.chat[0].text); }); @@ -848,7 +848,7 @@ describe('Group Model', () => { }, }, }]; - party = Group.translateSystemMessages(party, questLeader); + party.translateSystemMessages(questLeader); translationCheck(party.chat[0].text); }); @@ -859,7 +859,7 @@ describe('Group Model', () => { type: 'all_items_found', }, }]; - party = Group.translateSystemMessages(party, questLeader); + party.translateSystemMessages(questLeader); translationCheck(party.chat[0].text); }); @@ -873,7 +873,7 @@ describe('Group Model', () => { spell: 'earth', }, }]; - party = Group.translateSystemMessages(party, questLeader); + party.translateSystemMessages(questLeader); translationCheck(party.chat[0].text); }); @@ -888,7 +888,7 @@ describe('Group Model', () => { target: participatingMember.profile.name, }, }]; - party = Group.translateSystemMessages(party, questLeader); + party.translateSystemMessages(questLeader); translationCheck(party.chat[0].text); }); @@ -901,7 +901,7 @@ describe('Group Model', () => { quest: 'basilist', }, }]; - party = Group.translateSystemMessages(party, questLeader); + party.translateSystemMessages(questLeader); translationCheck(party.chat[0].text); }); @@ -913,7 +913,7 @@ describe('Group Model', () => { quest: 'stressbeast', }, }]; - party = Group.translateSystemMessages(party, questLeader); + party.translateSystemMessages(questLeader); translationCheck(party.chat[0].text); }); @@ -925,7 +925,7 @@ describe('Group Model', () => { quest: 'stressbeast', }, }]; - party = Group.translateSystemMessages(party, questLeader); + party.translateSystemMessages(questLeader); translationCheck(party.chat[0].text); }); @@ -938,7 +938,7 @@ describe('Group Model', () => { scene: 'market', }, }]; - party = Group.translateSystemMessages(party, questLeader); + party.translateSystemMessages(questLeader); translationCheck(party.chat[0].text); }); @@ -950,7 +950,7 @@ describe('Group Model', () => { quest: 'stressbeast', }, }]; - party = Group.translateSystemMessages(party, questLeader); + party.translateSystemMessages(questLeader); translationCheck(party.chat[0].text); }); @@ -963,7 +963,7 @@ describe('Group Model', () => { task: 'Feed the pet', }, }]; - party = Group.translateSystemMessages(party, questLeader); + party.translateSystemMessages(questLeader); translationCheck(party.chat[0].text); }); }); From 8a30ac0607f72f77b95e3d32ba837aca330f34f0 Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Wed, 15 May 2019 16:41:24 +0900 Subject: [PATCH 26/31] Add translation support for quest cancel --- test/api/unit/models/group.test.js | 13 +++++++++++++ website/common/locales/en/quests.json | 1 + website/server/controllers/api-v3/quests.js | 9 ++++++++- website/server/models/group.js | 4 ++++ 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/test/api/unit/models/group.test.js b/test/api/unit/models/group.test.js index 877a3a22fc..44707d22e4 100644 --- a/test/api/unit/models/group.test.js +++ b/test/api/unit/models/group.test.js @@ -892,6 +892,19 @@ describe('Group Model', () => { translationCheck(party.chat[0].text); }); + it('translate quest_cancel', () => { + questLeader.preferences.language = 'en'; + party.chat = [{ + info: { + type: 'quest_cancel', + user: questLeader.profile.name, + quest: 'basilist', + }, + }]; + party.translateSystemMessages(questLeader); + translationCheck(party.chat[0].text); + }); + it('translate quest_abort', () => { questLeader.preferences.language = 'en'; party.chat = [{ diff --git a/website/common/locales/en/quests.json b/website/common/locales/en/quests.json index 7c0bb42387..42bb9124e2 100644 --- a/website/common/locales/en/quests.json +++ b/website/common/locales/en/quests.json @@ -134,5 +134,6 @@ "chatFindItems": "<%= username %> found <%= items %>.", "chatItemQuestFinish": "All items found! Party has received their rewards.", "chatQuestAborted": "<%= username %> aborted the party quest <%= questName %>.", + "chatQuestCancelled": "<%= username %> cancelled the party quest <%= questName %>.", "tavernBossTired": "<%= bossName %> tries to unleash <%= rageName %> but is too tired." } diff --git a/website/server/controllers/api-v3/quests.js b/website/server/controllers/api-v3/quests.js index 9857e32bd4..d0f96b8a80 100644 --- a/website/server/controllers/api-v3/quests.js +++ b/website/server/controllers/api-v3/quests.js @@ -371,7 +371,14 @@ api.cancelQuest = { if (group.quest.active) throw new NotAuthorized(res.t('cantCancelActiveQuest')); let questName = questScrolls[group.quest.key].text('en'); - const newChatMessage = group.sendChat(`\`${user.profile.name} cancelled the party quest ${questName}.\``); + const newChatMessage = group.sendChat({ + message: `\`${user.profile.name} cancelled the party quest ${questName}.\``, + info: { + type: 'quest_cancel', + user: user.profile.name, + quest: group.quest.key, + }, + }); group.quest = Group.cleanGroupQuest(); group.markModified('quest'); diff --git a/website/server/models/group.js b/website/server/models/group.js index 038567d321..73791b3f9f 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -380,6 +380,10 @@ function _translateMessage (lang, info) { msg = `\`${shared.i18n.t('chatCastSpellUser', {username: info.user, spell: spells[info.class][info.spell].text(lang), target: info.target}, lang)}\``; break; + case 'quest_cancel': + msg = `\`${shared.i18n.t('chatQuestCancelled', {username: info.user, questName: questScrolls[info.quest].text(lang)}, lang)}\``; + break; + case 'quest_abort': msg = `\`${shared.i18n.t('chatQuestAborted', {username: info.user, questName: questScrolls[info.quest].text(lang)}, lang)}\``; break; From 8c59420d4ee894f9a0f815f076c5c84b22aac167 Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Wed, 15 May 2019 17:27:18 +0900 Subject: [PATCH 27/31] Fix quest cancel test --- .../quests/POST-groups_groupid_quests_cancel.test.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/api/v3/integration/quests/POST-groups_groupid_quests_cancel.test.js b/test/api/v3/integration/quests/POST-groups_groupid_quests_cancel.test.js index a8095a3cee..4032c36306 100644 --- a/test/api/v3/integration/quests/POST-groups_groupid_quests_cancel.test.js +++ b/test/api/v3/integration/quests/POST-groups_groupid_quests_cancel.test.js @@ -141,7 +141,14 @@ describe('POST /groups/:groupId/quests/cancel', () => { members: {}, }); expect(Group.prototype.sendChat).to.be.calledOnce; - expect(Group.prototype.sendChat).to.be.calledWithMatch(/cancelled the party quest Wail of the Whale.`/); + expect(Group.prototype.sendChat).to.be.calledWithMatch({ + message: sinon.match(/cancelled the party quest Wail of the Whale.`/), + info: { + quest: 'whale', + type: 'quest_cancel', + user: sinon.match.any, + }, + }); stub.restore(); }); From 28bf024990e7338f7562fcfe0df2cf144ff7186f Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Wed, 15 May 2019 19:07:17 +0900 Subject: [PATCH 28/31] Move translateMessage to libs --- website/server/libs/chat/group-chat.js | 83 ++++++++++++++++++++++++++ website/server/models/group.js | 83 +------------------------- 2 files changed, 85 insertions(+), 81 deletions(-) diff --git a/website/server/libs/chat/group-chat.js b/website/server/libs/chat/group-chat.js index 72edb4d52d..92123b6cf2 100644 --- a/website/server/libs/chat/group-chat.js +++ b/website/server/libs/chat/group-chat.js @@ -1,6 +1,10 @@ import { chatModel as Chat } from '../../models/message'; +import shared from '../../../common'; +import _ from 'lodash'; import { MAX_CHAT_COUNT, MAX_SUBBED_GROUP_CHAT_COUNT } from '../../models/group'; +const questScrolls = shared.content.quests; + // @TODO: Don't use this method when the group can be saved. export async function getGroupChat (group) { const maxChatCount = group.isSubscribed() ? MAX_SUBBED_GROUP_CHAT_COUNT : MAX_CHAT_COUNT; @@ -22,3 +26,82 @@ export async function getGroupChat (group) { return previous; }, []); } + +export function translateMessage (lang, info) { + let msg; + let foundText = ''; + let spells = shared.content.spells; + let quests = shared.content.quests; + + switch (info.type) { + case 'quest_start': + msg = `\`${shared.i18n.t('chatQuestStarted', {questName: questScrolls[info.quest].text(lang)}, lang)}\``; + break; + + case 'boss_damage': + msg = `\`${shared.i18n.t('chatBossDamage', {username: info.user, bossName: questScrolls[info.quest].boss.name(lang), userDamage: info.userDamage, bossDamage: info.bossDamage}, lang)}\``; + break; + + case 'boss_dont_attack': + msg = `\`${shared.i18n.t('chatBossDontAttack', {username: info.user, bossName: questScrolls[info.quest].boss.name(lang), userDamage: info.userDamage}, lang)}\``; + break; + + case 'boss_rage': + msg = `\`${questScrolls[info.quest].boss.rage.effect(lang)}\``; + break; + + case 'boss_defeated': + msg = `\`${shared.i18n.t('chatBossDefeated', {bossName: questScrolls[info.quest].boss.name(lang)}, lang)}\``; + break; + + case 'user_found_items': + foundText = _.reduce(info.items, (m, v, k) => { + m.push(`${v} ${questScrolls[info.quest].collect[k].text(lang)}`); + return m; + }, []).join(', '); + msg = `\`${shared.i18n.t('chatFindItems', {username: info.user, items: foundText}, lang)}\``; + break; + + case 'all_items_found': + msg = `\`${shared.i18n.t('chatItemQuestFinish', lang)}\``; + break; + + case 'spell_cast_party': + msg = `\`${shared.i18n.t('chatCastSpellParty', {username: info.user, spell: spells[info.class][info.spell].text(lang)}, lang)}\``; + break; + + case 'spell_cast_user': + msg = `\`${shared.i18n.t('chatCastSpellUser', {username: info.user, spell: spells[info.class][info.spell].text(lang), target: info.target}, lang)}\``; + break; + + case 'quest_cancel': + msg = `\`${shared.i18n.t('chatQuestCancelled', {username: info.user, questName: questScrolls[info.quest].text(lang)}, lang)}\``; + break; + + case 'quest_abort': + msg = `\`${shared.i18n.t('chatQuestAborted', {username: info.user, questName: questScrolls[info.quest].text(lang)}, lang)}\``; + break; + + case 'tavern_quest_completed': + msg = `\`${quests[info.quest].completionChat(lang)}\``; + break; + + case 'tavern_boss_rage_tired': + msg = `\`${shared.i18n.t('tavernBossTired', {rageName: quests[info.quest].boss.rage.title(lang), bossName: quests[info.quest].boss.name(lang)}, lang)}\``; + break; + + case 'tavern_boss_rage': + msg = `\`${quests[info.quest].boss.rage[info.scene](lang)}\``; + break; + + case 'tavern_boss_desperation': + msg = `\`${quests[info.quest].boss.desperation.text(lang)}\``; + break; + + case 'claim_task': + msg = `${shared.i18n.t('userIsClamingTask', {username: info.user, task: info.task}, lang)}`; + break; + } + + return msg; +} diff --git a/website/server/models/group.js b/website/server/models/group.js index 73791b3f9f..9b846d9af7 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -37,7 +37,7 @@ import { } from './subscriptionPlan'; import amazonPayments from '../libs/payments/amazon'; import stripePayments from '../libs/payments/stripe'; -import { getGroupChat } from '../libs/chat/group-chat'; +import { getGroupChat, translateMessage } from '../libs/chat/group-chat'; import { model as UserNotification } from './userNotification'; const questScrolls = shared.content.quests; @@ -333,85 +333,6 @@ schema.statics.getGroups = async function getGroups (options = {}) { return groupsArray; }; -function _translateMessage (lang, info) { - let msg; - let foundText = ''; - let spells = shared.content.spells; - let quests = shared.content.quests; - - switch (info.type) { - case 'quest_start': - msg = `\`${shared.i18n.t('chatQuestStarted', {questName: questScrolls[info.quest].text(lang)}, lang)}\``; - break; - - case 'boss_damage': - msg = `\`${shared.i18n.t('chatBossDamage', {username: info.user, bossName: questScrolls[info.quest].boss.name(lang), userDamage: info.userDamage, bossDamage: info.bossDamage}, lang)}\``; - break; - - case 'boss_dont_attack': - msg = `\`${shared.i18n.t('chatBossDontAttack', {username: info.user, bossName: questScrolls[info.quest].boss.name(lang), userDamage: info.userDamage}, lang)}\``; - break; - - case 'boss_rage': - msg = `\`${questScrolls[info.quest].boss.rage.effect(lang)}\``; - break; - - case 'boss_defeated': - msg = `\`${shared.i18n.t('chatBossDefeated', {bossName: questScrolls[info.quest].boss.name(lang)}, lang)}\``; - break; - - case 'user_found_items': - foundText = _.reduce(info.items, (m, v, k) => { - m.push(`${v} ${questScrolls[info.quest].collect[k].text(lang)}`); - return m; - }, []).join(', '); - msg = `\`${shared.i18n.t('chatFindItems', {username: info.user, items: foundText}, lang)}\``; - break; - - case 'all_items_found': - msg = `\`${shared.i18n.t('chatItemQuestFinish', lang)}\``; - break; - - case 'spell_cast_party': - msg = `\`${shared.i18n.t('chatCastSpellParty', {username: info.user, spell: spells[info.class][info.spell].text(lang)}, lang)}\``; - break; - - case 'spell_cast_user': - msg = `\`${shared.i18n.t('chatCastSpellUser', {username: info.user, spell: spells[info.class][info.spell].text(lang), target: info.target}, lang)}\``; - break; - - case 'quest_cancel': - msg = `\`${shared.i18n.t('chatQuestCancelled', {username: info.user, questName: questScrolls[info.quest].text(lang)}, lang)}\``; - break; - - case 'quest_abort': - msg = `\`${shared.i18n.t('chatQuestAborted', {username: info.user, questName: questScrolls[info.quest].text(lang)}, lang)}\``; - break; - - case 'tavern_quest_completed': - msg = `\`${quests[info.quest].completionChat(lang)}\``; - break; - - case 'tavern_boss_rage_tired': - msg = `\`${shared.i18n.t('tavernBossTired', {rageName: quests[info.quest].boss.rage.title(lang), bossName: quests[info.quest].boss.name(lang)}, lang)}\``; - break; - - case 'tavern_boss_rage': - msg = `\`${quests[info.quest].boss.rage[info.scene](lang)}\``; - break; - - case 'tavern_boss_desperation': - msg = `\`${quests[info.quest].boss.desperation.text(lang)}\``; - break; - - case 'claim_task': - msg = `${shared.i18n.t('userIsClamingTask', {username: info.user, task: info.task}, lang)}`; - break; - } - - return msg; -} - // When converting to json remove chat messages with more than 1 flag and remove all flags info // unless the user is an admin or said chat is posted by that user // Not putting into toJSON because there we can't access user @@ -536,7 +457,7 @@ schema.methods.translateSystemMessages = function translateSystemMessages (user) this.chat.forEach((chat, i, chatArray) => { if (!_.isEmpty(chat.info)) { - chatArray[i].text = _translateMessage(lang, chat.info); + chatArray[i].text = translateMessage(lang, chat.info); } }); }; From 0824af05b7923190000cbdff7f871be04f12f333 Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Wed, 15 May 2019 19:45:42 +0900 Subject: [PATCH 29/31] Remove unneeded parameter on method call --- website/server/models/group.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/server/models/group.js b/website/server/models/group.js index 9b846d9af7..71b2d33139 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -344,7 +344,7 @@ schema.statics.toJSONCleanChat = async function groupToJSONCleanChat (group, use await getGroupChat(group); } - group.translateSystemMessages(group, user); + group.translateSystemMessages(user); let toJSON = group.toJSON(); From ab0dae8df359ae64d083b97e54d1e9bb4f93e95d Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Wed, 15 May 2019 21:23:26 +0900 Subject: [PATCH 30/31] Translate messages only after serialization --- test/api/unit/models/group.test.js | 80 ++++++++++++++++++------------ website/server/models/group.js | 26 +++++----- 2 files changed, 62 insertions(+), 44 deletions(-) diff --git a/test/api/unit/models/group.test.js b/test/api/unit/models/group.test.js index 44707d22e4..799890338d 100644 --- a/test/api/unit/models/group.test.js +++ b/test/api/unit/models/group.test.js @@ -777,8 +777,9 @@ describe('Group Model', () => { quest: 'basilist', }, }]; - party.translateSystemMessages(questLeader); - translationCheck(party.chat[0].text); + let toJSON = party.toJSON(); + Group.translateSystemMessages(toJSON, questLeader); + translationCheck(toJSON.chat[0].text); }); it('translate boss_damage', () => { @@ -792,8 +793,9 @@ describe('Group Model', () => { bossDamage: 3.7, }, }]; - party.translateSystemMessages(questLeader); - translationCheck(party.chat[0].text); + let toJSON = party.toJSON(); + Group.translateSystemMessages(toJSON, questLeader); + translationCheck(toJSON.chat[0].text); }); it('translate boss_dont_attack', () => { @@ -806,8 +808,9 @@ describe('Group Model', () => { userDamage: 15.3, }, }]; - party.translateSystemMessages(questLeader); - translationCheck(party.chat[0].text); + let toJSON = party.toJSON(); + Group.translateSystemMessages(toJSON, questLeader); + translationCheck(toJSON.chat[0].text); }); it('translate boss_rage', () => { @@ -818,8 +821,9 @@ describe('Group Model', () => { quest: 'lostMasterclasser3', }, }]; - party.translateSystemMessages(questLeader); - translationCheck(party.chat[0].text); + let toJSON = party.toJSON(); + Group.translateSystemMessages(toJSON, questLeader); + translationCheck(toJSON.chat[0].text); }); it('translate boss_defeated', () => { @@ -830,8 +834,9 @@ describe('Group Model', () => { quest: 'lostMasterclasser3', }, }]; - party.translateSystemMessages(questLeader); - translationCheck(party.chat[0].text); + let toJSON = party.toJSON(); + Group.translateSystemMessages(toJSON, questLeader); + translationCheck(toJSON.chat[0].text); }); it('translate user_found_items', () => { @@ -848,8 +853,9 @@ describe('Group Model', () => { }, }, }]; - party.translateSystemMessages(questLeader); - translationCheck(party.chat[0].text); + let toJSON = party.toJSON(); + Group.translateSystemMessages(toJSON, questLeader); + translationCheck(toJSON.chat[0].text); }); it('translate all_items_found', () => { @@ -859,8 +865,9 @@ describe('Group Model', () => { type: 'all_items_found', }, }]; - party.translateSystemMessages(questLeader); - translationCheck(party.chat[0].text); + let toJSON = party.toJSON(); + Group.translateSystemMessages(toJSON, questLeader); + translationCheck(toJSON.chat[0].text); }); it('translate spell_cast_party', () => { @@ -873,8 +880,9 @@ describe('Group Model', () => { spell: 'earth', }, }]; - party.translateSystemMessages(questLeader); - translationCheck(party.chat[0].text); + let toJSON = party.toJSON(); + Group.translateSystemMessages(toJSON, questLeader); + translationCheck(toJSON.chat[0].text); }); it('translate spell_cast_user', () => { @@ -888,8 +896,9 @@ describe('Group Model', () => { target: participatingMember.profile.name, }, }]; - party.translateSystemMessages(questLeader); - translationCheck(party.chat[0].text); + let toJSON = party.toJSON(); + Group.translateSystemMessages(toJSON, questLeader); + translationCheck(toJSON.chat[0].text); }); it('translate quest_cancel', () => { @@ -901,8 +910,9 @@ describe('Group Model', () => { quest: 'basilist', }, }]; - party.translateSystemMessages(questLeader); - translationCheck(party.chat[0].text); + let toJSON = party.toJSON(); + Group.translateSystemMessages(toJSON, questLeader); + translationCheck(toJSON.chat[0].text); }); it('translate quest_abort', () => { @@ -914,8 +924,9 @@ describe('Group Model', () => { quest: 'basilist', }, }]; - party.translateSystemMessages(questLeader); - translationCheck(party.chat[0].text); + let toJSON = party.toJSON(); + Group.translateSystemMessages(toJSON, questLeader); + translationCheck(toJSON.chat[0].text); }); it('translate tavern_quest_completed', () => { @@ -926,8 +937,9 @@ describe('Group Model', () => { quest: 'stressbeast', }, }]; - party.translateSystemMessages(questLeader); - translationCheck(party.chat[0].text); + let toJSON = party.toJSON(); + Group.translateSystemMessages(toJSON, questLeader); + translationCheck(toJSON.chat[0].text); }); it('translate tavern_boss_rage_tired', () => { @@ -938,8 +950,9 @@ describe('Group Model', () => { quest: 'stressbeast', }, }]; - party.translateSystemMessages(questLeader); - translationCheck(party.chat[0].text); + let toJSON = party.toJSON(); + Group.translateSystemMessages(toJSON, questLeader); + translationCheck(toJSON.chat[0].text); }); it('translate tavern_boss_rage', () => { @@ -951,8 +964,9 @@ describe('Group Model', () => { scene: 'market', }, }]; - party.translateSystemMessages(questLeader); - translationCheck(party.chat[0].text); + let toJSON = party.toJSON(); + Group.translateSystemMessages(toJSON, questLeader); + translationCheck(toJSON.chat[0].text); }); it('translate tavern_boss_desperation', () => { @@ -963,8 +977,9 @@ describe('Group Model', () => { quest: 'stressbeast', }, }]; - party.translateSystemMessages(questLeader); - translationCheck(party.chat[0].text); + let toJSON = party.toJSON(); + Group.translateSystemMessages(toJSON, questLeader); + translationCheck(toJSON.chat[0].text); }); it('translate claim_task', () => { @@ -976,8 +991,9 @@ describe('Group Model', () => { task: 'Feed the pet', }, }]; - party.translateSystemMessages(questLeader); - translationCheck(party.chat[0].text); + let toJSON = party.toJSON(); + Group.translateSystemMessages(toJSON, questLeader); + translationCheck(toJSON.chat[0].text); }); }); }); diff --git a/website/server/models/group.js b/website/server/models/group.js index 71b2d33139..1666f50ad8 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -333,6 +333,19 @@ schema.statics.getGroups = async function getGroups (options = {}) { return groupsArray; }; +function _translateSystemMessages (toJSON, user) { + let lang = user.preferences ? user.preferences.language : 'en'; + + toJSON.chat.map(chat => { + if (!_.isEmpty(chat.info)) { + chat.text = translateMessage(lang, chat.info); + } + return chat; + }); +} + +schema.statics.translateSystemMessages = _translateSystemMessages; + // When converting to json remove chat messages with more than 1 flag and remove all flags info // unless the user is an admin or said chat is posted by that user // Not putting into toJSON because there we can't access user @@ -344,9 +357,8 @@ schema.statics.toJSONCleanChat = async function groupToJSONCleanChat (group, use await getGroupChat(group); } - group.translateSystemMessages(user); - let toJSON = group.toJSON(); + _translateSystemMessages(toJSON, user); if (!user.contributor.admin) { _.remove(toJSON.chat, chatMsg => { @@ -452,16 +464,6 @@ schema.statics.validateInvitations = async function getInvitationError (invites, } }; -schema.methods.translateSystemMessages = function translateSystemMessages (user) { - let lang = user.preferences ? user.preferences.language : 'en'; - - this.chat.forEach((chat, i, chatArray) => { - if (!_.isEmpty(chat.info)) { - chatArray[i].text = translateMessage(lang, chat.info); - } - }); -}; - schema.methods.getParticipatingQuestMembers = function getParticipatingQuestMembers () { return Object.keys(this.quest.members).filter(member => this.quest.members[member]); }; From eff8db0afddf74d367ce1f096dbb87c14e133293 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Wed, 15 May 2019 16:54:55 +0200 Subject: [PATCH 31/31] fix issue with language being undefined, refactoring auth middleware, new tests --- test/api/unit/middlewares/auth.test.js | 29 ++++- test/api/unit/models/group.test.js | 119 +++++++++++------- .../server/controllers/api-v3/challenges.js | 3 +- website/server/controllers/api-v3/groups.js | 3 +- website/server/controllers/api-v3/tasks.js | 3 +- website/server/middlewares/auth.js | 17 ++- website/server/models/group.js | 60 +++++---- 7 files changed, 143 insertions(+), 91 deletions(-) diff --git a/test/api/unit/middlewares/auth.test.js b/test/api/unit/middlewares/auth.test.js index 25ba3d17ea..830af9be04 100644 --- a/test/api/unit/middlewares/auth.test.js +++ b/test/api/unit/middlewares/auth.test.js @@ -16,7 +16,7 @@ describe('auth middleware', () => { describe('auth with headers', () => { it('allows to specify a list of user field that we do not want to load', (done) => { const authWithHeaders = authWithHeadersFactory({ - userFieldsToExclude: ['items', 'flags', 'auth.timestamps'], + userFieldsToExclude: ['items'], }); req.headers['x-api-user'] = user._id; @@ -27,11 +27,34 @@ describe('auth middleware', () => { const userToJSON = res.locals.user.toJSON(); expect(userToJSON.items).to.not.exist; - expect(userToJSON.flags).to.not.exist; - expect(userToJSON.auth.timestamps).to.not.exist; + expect(userToJSON.auth).to.exist; + + done(); + }); + }); + + it('makes sure some fields are always included', (done) => { + const authWithHeaders = authWithHeadersFactory({ + userFieldsToExclude: [ + 'items', 'auth.timestamps', + 'preferences', 'notifications', '_id', 'flags', 'auth', // these are always loaded + ], + }); + + req.headers['x-api-user'] = user._id; + req.headers['x-api-key'] = user.apiToken; + + authWithHeaders(req, res, (err) => { + if (err) return done(err); + + const userToJSON = res.locals.user.toJSON(); + expect(userToJSON.items).to.not.exist; + expect(userToJSON.auth.timestamps).to.exist; expect(userToJSON.auth).to.exist; expect(userToJSON.notifications).to.exist; expect(userToJSON.preferences).to.exist; + expect(userToJSON._id).to.exist; + expect(userToJSON.flags).to.exist; done(); }); diff --git a/test/api/unit/models/group.test.js b/test/api/unit/models/group.test.js index 799890338d..7d120c055a 100644 --- a/test/api/unit/models/group.test.js +++ b/test/api/unit/models/group.test.js @@ -769,7 +769,7 @@ describe('Group Model', () => { }); describe('translateSystemMessages', () => { - it('translate quest_start', () => { + it('translate quest_start', async () => { questLeader.preferences.language = 'en'; party.chat = [{ info: { @@ -777,12 +777,11 @@ describe('Group Model', () => { quest: 'basilist', }, }]; - let toJSON = party.toJSON(); - Group.translateSystemMessages(toJSON, questLeader); + let toJSON = await Group.toJSONCleanChat(party, questLeader); translationCheck(toJSON.chat[0].text); }); - it('translate boss_damage', () => { + it('translate boss_damage', async () => { questLeader.preferences.language = 'en'; party.chat = [{ info: { @@ -793,12 +792,11 @@ describe('Group Model', () => { bossDamage: 3.7, }, }]; - let toJSON = party.toJSON(); - Group.translateSystemMessages(toJSON, questLeader); + let toJSON = await Group.toJSONCleanChat(party, questLeader); translationCheck(toJSON.chat[0].text); }); - it('translate boss_dont_attack', () => { + it('translate boss_dont_attack', async () => { questLeader.preferences.language = 'en'; party.chat = [{ info: { @@ -808,12 +806,11 @@ describe('Group Model', () => { userDamage: 15.3, }, }]; - let toJSON = party.toJSON(); - Group.translateSystemMessages(toJSON, questLeader); + let toJSON = await Group.toJSONCleanChat(party, questLeader); translationCheck(toJSON.chat[0].text); }); - it('translate boss_rage', () => { + it('translate boss_rage', async () => { questLeader.preferences.language = 'en'; party.chat = [{ info: { @@ -821,12 +818,11 @@ describe('Group Model', () => { quest: 'lostMasterclasser3', }, }]; - let toJSON = party.toJSON(); - Group.translateSystemMessages(toJSON, questLeader); + let toJSON = await Group.toJSONCleanChat(party, questLeader); translationCheck(toJSON.chat[0].text); }); - it('translate boss_defeated', () => { + it('translate boss_defeated', async () => { questLeader.preferences.language = 'en'; party.chat = [{ info: { @@ -834,12 +830,11 @@ describe('Group Model', () => { quest: 'lostMasterclasser3', }, }]; - let toJSON = party.toJSON(); - Group.translateSystemMessages(toJSON, questLeader); + let toJSON = await Group.toJSONCleanChat(party, questLeader); translationCheck(toJSON.chat[0].text); }); - it('translate user_found_items', () => { + it('translate user_found_items', async () => { questLeader.preferences.language = 'en'; party.chat = [{ info: { @@ -853,24 +848,22 @@ describe('Group Model', () => { }, }, }]; - let toJSON = party.toJSON(); - Group.translateSystemMessages(toJSON, questLeader); + let toJSON = await Group.toJSONCleanChat(party, questLeader); translationCheck(toJSON.chat[0].text); }); - it('translate all_items_found', () => { + it('translate all_items_found', async () => { questLeader.preferences.language = 'en'; party.chat = [{ info: { type: 'all_items_found', }, }]; - let toJSON = party.toJSON(); - Group.translateSystemMessages(toJSON, questLeader); + let toJSON = await Group.toJSONCleanChat(party, questLeader); translationCheck(toJSON.chat[0].text); }); - it('translate spell_cast_party', () => { + it('translate spell_cast_party', async () => { questLeader.preferences.language = 'en'; party.chat = [{ info: { @@ -880,12 +873,11 @@ describe('Group Model', () => { spell: 'earth', }, }]; - let toJSON = party.toJSON(); - Group.translateSystemMessages(toJSON, questLeader); + let toJSON = await Group.toJSONCleanChat(party, questLeader); translationCheck(toJSON.chat[0].text); }); - it('translate spell_cast_user', () => { + it('translate spell_cast_user', async () => { questLeader.preferences.language = 'en'; party.chat = [{ info: { @@ -896,12 +888,11 @@ describe('Group Model', () => { target: participatingMember.profile.name, }, }]; - let toJSON = party.toJSON(); - Group.translateSystemMessages(toJSON, questLeader); + let toJSON = await Group.toJSONCleanChat(party, questLeader); translationCheck(toJSON.chat[0].text); }); - it('translate quest_cancel', () => { + it('translate quest_cancel', async () => { questLeader.preferences.language = 'en'; party.chat = [{ info: { @@ -910,12 +901,11 @@ describe('Group Model', () => { quest: 'basilist', }, }]; - let toJSON = party.toJSON(); - Group.translateSystemMessages(toJSON, questLeader); + let toJSON = await Group.toJSONCleanChat(party, questLeader); translationCheck(toJSON.chat[0].text); }); - it('translate quest_abort', () => { + it('translate quest_abort', async () => { questLeader.preferences.language = 'en'; party.chat = [{ info: { @@ -924,12 +914,11 @@ describe('Group Model', () => { quest: 'basilist', }, }]; - let toJSON = party.toJSON(); - Group.translateSystemMessages(toJSON, questLeader); + let toJSON = await Group.toJSONCleanChat(party, questLeader); translationCheck(toJSON.chat[0].text); }); - it('translate tavern_quest_completed', () => { + it('translate tavern_quest_completed', async () => { questLeader.preferences.language = 'en'; party.chat = [{ info: { @@ -937,12 +926,11 @@ describe('Group Model', () => { quest: 'stressbeast', }, }]; - let toJSON = party.toJSON(); - Group.translateSystemMessages(toJSON, questLeader); + let toJSON = await Group.toJSONCleanChat(party, questLeader); translationCheck(toJSON.chat[0].text); }); - it('translate tavern_boss_rage_tired', () => { + it('translate tavern_boss_rage_tired', async () => { questLeader.preferences.language = 'en'; party.chat = [{ info: { @@ -950,12 +938,11 @@ describe('Group Model', () => { quest: 'stressbeast', }, }]; - let toJSON = party.toJSON(); - Group.translateSystemMessages(toJSON, questLeader); + let toJSON = await Group.toJSONCleanChat(party, questLeader); translationCheck(toJSON.chat[0].text); }); - it('translate tavern_boss_rage', () => { + it('translate tavern_boss_rage', async () => { questLeader.preferences.language = 'en'; party.chat = [{ info: { @@ -964,12 +951,11 @@ describe('Group Model', () => { scene: 'market', }, }]; - let toJSON = party.toJSON(); - Group.translateSystemMessages(toJSON, questLeader); + let toJSON = await Group.toJSONCleanChat(party, questLeader); translationCheck(toJSON.chat[0].text); }); - it('translate tavern_boss_desperation', () => { + it('translate tavern_boss_desperation', async () => { questLeader.preferences.language = 'en'; party.chat = [{ info: { @@ -977,12 +963,11 @@ describe('Group Model', () => { quest: 'stressbeast', }, }]; - let toJSON = party.toJSON(); - Group.translateSystemMessages(toJSON, questLeader); + let toJSON = await Group.toJSONCleanChat(party, questLeader); translationCheck(toJSON.chat[0].text); }); - it('translate claim_task', () => { + it('translate claim_task', async () => { questLeader.preferences.language = 'en'; party.chat = [{ info: { @@ -991,11 +976,49 @@ describe('Group Model', () => { task: 'Feed the pet', }, }]; - let toJSON = party.toJSON(); - Group.translateSystemMessages(toJSON, questLeader); + let toJSON = await Group.toJSONCleanChat(party, questLeader); translationCheck(toJSON.chat[0].text); }); }); + + describe('toJSONCleanChat', () => { + it('shows messages with 1 flag to non-admins', async () => { + party.chat = [{ + flagCount: 1, + info: { + type: 'quest_start', + quest: 'basilist', + }, + }]; + let toJSON = await Group.toJSONCleanChat(party, questLeader); + expect(toJSON.chat.length).to.equal(1); + }); + + it('shows messages with >= 2 flag to admins', async () => { + party.chat = [{ + flagCount: 3, + info: { + type: 'quest_start', + quest: 'basilist', + }, + }]; + const admin = new User({'contributor.admin': true}); + let toJSON = await Group.toJSONCleanChat(party, admin); + expect(toJSON.chat.length).to.equal(1); + }); + + it('doesn\'t show flagged messages to non-admins', async () => { + party.chat = [{ + flagCount: 3, + info: { + type: 'quest_start', + quest: 'basilist', + }, + }]; + let toJSON = await Group.toJSONCleanChat(party, questLeader); + expect(toJSON.chat.length).to.equal(0); + }); + }); }); context('Instance Methods', () => { diff --git a/website/server/controllers/api-v3/challenges.js b/website/server/controllers/api-v3/challenges.js index a60f9502b7..214f3baf78 100644 --- a/website/server/controllers/api-v3/challenges.js +++ b/website/server/controllers/api-v3/challenges.js @@ -442,7 +442,8 @@ api.getGroupChallenges = { method: 'GET', url: '/challenges/groups/:groupId', middlewares: [authWithHeaders({ - userFieldsToInclude: ['_id', 'party', 'guilds'], + // Some fields (including _id) are always loaded (see middlewares/auth) + userFieldsToInclude: ['party', 'guilds'], // Some fields are always loaded (see middlewares/auth) })], async handler (req, res) { let user = res.locals.user; diff --git a/website/server/controllers/api-v3/groups.js b/website/server/controllers/api-v3/groups.js index eed4aff4cb..5b0aebe079 100644 --- a/website/server/controllers/api-v3/groups.js +++ b/website/server/controllers/api-v3/groups.js @@ -375,7 +375,8 @@ api.getGroup = { method: 'GET', url: '/groups/:groupId', middlewares: [authWithHeaders({ - userFieldsToInclude: ['_id', 'party', 'guilds', 'contributor'], + // Some fields (including _id, preferences) are always loaded (see middlewares/auth) + userFieldsToInclude: ['party', 'guilds', 'contributor'], })], async handler (req, res) { let user = res.locals.user; diff --git a/website/server/controllers/api-v3/tasks.js b/website/server/controllers/api-v3/tasks.js index 524669d3a1..c7035f0f99 100644 --- a/website/server/controllers/api-v3/tasks.js +++ b/website/server/controllers/api-v3/tasks.js @@ -285,7 +285,8 @@ api.getUserTasks = { method: 'GET', url: '/tasks/user', middlewares: [authWithHeaders({ - userFieldsToInclude: ['_id', 'tasksOrder', 'preferences'], + // Some fields (including _id, preferences) are always loaded (see middlewares/auth) + userFieldsToInclude: ['tasksOrder'], })], async handler (req, res) { let types = Tasks.tasksTypes.map(type => `${type}s`); diff --git a/website/server/middlewares/auth.js b/website/server/middlewares/auth.js index f4c301acf9..c0881008af 100644 --- a/website/server/middlewares/auth.js +++ b/website/server/middlewares/auth.js @@ -9,22 +9,27 @@ import url from 'url'; import gcpStackdriverTracer from '../libs/gcpTraceAgent'; const COMMUNITY_MANAGER_EMAIL = nconf.get('EMAILS_COMMUNITY_MANAGER_EMAIL'); +const USER_FIELDS_ALWAYS_LOADED = ['_id', 'notifications', 'preferences', 'auth', 'flags']; function getUserFields (options, req) { // A list of user fields that aren't needed for the route and are not loaded from the db. // Must be an array if (options.userFieldsToExclude) { - return options.userFieldsToExclude.map(field => { - return `-${field}`; // -${field} means exclude ${field} in mongodb - }).join(' '); + return options.userFieldsToExclude + .filter(field => { + return !USER_FIELDS_ALWAYS_LOADED.find(fieldToInclude => field.startsWith(fieldToInclude)); + }) + .map(field => { + return `-${field}`; // -${field} means exclude ${field} in mongodb + }) + .join(' '); } if (options.userFieldsToInclude) { - return options.userFieldsToInclude.join(' '); + return options.userFieldsToInclude.concat(USER_FIELDS_ALWAYS_LOADED).join(' '); } // Allows GET requests to /user to specify a list of user fields to return instead of the entire doc - // Notifications are always included const urlPath = url.parse(req.url).pathname; const userFields = req.query.userFields; if (!userFields || urlPath !== '/user') return ''; @@ -32,7 +37,7 @@ function getUserFields (options, req) { const userFieldOptions = userFields.split(','); if (userFieldOptions.length === 0) return ''; - return `notifications ${userFieldOptions.join(' ')}`; + return userFieldOptions.concat(USER_FIELDS_ALWAYS_LOADED).join(' '); } // Make sure stackdriver traces are storing the user id diff --git a/website/server/models/group.js b/website/server/models/group.js index 1666f50ad8..636eb3ca0e 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -333,19 +333,6 @@ schema.statics.getGroups = async function getGroups (options = {}) { return groupsArray; }; -function _translateSystemMessages (toJSON, user) { - let lang = user.preferences ? user.preferences.language : 'en'; - - toJSON.chat.map(chat => { - if (!_.isEmpty(chat.info)) { - chat.text = translateMessage(lang, chat.info); - } - return chat; - }); -} - -schema.statics.translateSystemMessages = _translateSystemMessages; - // When converting to json remove chat messages with more than 1 flag and remove all flags info // unless the user is an admin or said chat is posted by that user // Not putting into toJSON because there we can't access user @@ -357,27 +344,38 @@ schema.statics.toJSONCleanChat = async function groupToJSONCleanChat (group, use await getGroupChat(group); } - let toJSON = group.toJSON(); - _translateSystemMessages(toJSON, user); + const groupToJson = group.toJSON(); + const userLang = user.preferences.language; - if (!user.contributor.admin) { - _.remove(toJSON.chat, chatMsg => { - chatMsg.flags = {}; - if (chatMsg._meta) chatMsg._meta = undefined; - return user._id !== chatMsg.uuid && chatMsg.flagCount >= 2; - }); - } + groupToJson.chat = groupToJson.chat + .map(chatMsg => { + // Translate system messages + if (!_.isEmpty(chatMsg.info)) { + chatMsg.text = translateMessage(userLang, chatMsg.info); + } - // Convert to timestamps because Android expects it - toJSON.chat.forEach(chat => { - // old chats are saved with a numeric timestamp - // new chats use `Date` which then has to be converted to the numeric timestamp - if (chat.timestamp && chat.timestamp.getTime) { - chat.timestamp = chat.timestamp.getTime(); - } - }); + // Convert to timestamps because Android expects it + // old chats are saved with a numeric timestamp + // new chats use `Date` which then has to be converted to the numeric timestamp + if (chatMsg.timestamp && chatMsg.timestamp.getTime) { + chatMsg.timestamp = chatMsg.timestamp.getTime(); + } - return toJSON; + if (!user.contributor.admin) { + // Flags are hidden to non admins + chatMsg.flags = {}; + if (chatMsg._meta) chatMsg._meta = undefined; + + // Messages with >= 2 flags are hidden to non admins and non authors + if (user._id !== chatMsg.uuid && chatMsg.flagCount >= 2) return undefined; + } + + return chatMsg; + }) + // Used to filter for undefined chat messages that should not be shown to non-admins + .filter(chatMsg => chatMsg !== undefined); + + return groupToJson; }; function getInviteError (uuids, emails, usernames) {