Files
habitica/website/server/libs/api-v3/cron.js
Matteo Pagliazzi 28f2e9c356 API v3 [WIP] (#6144)
* Fixed more tests

* Added tags into user service

* Added api-v3 auth urls

* v3: fix package.json

* v3: fix package.json

* Fixed auth tests. Updated Authctrl response

* v3: remove newrelic config file in favour of env variables

* v3: upgrade some deps

* switch from Q to Bluebird

* v3 fix tests with deferred

* Removed extra consoles.log. Changed data.data to res.data

* v3 fix tests and use coroutines instead of regenerator

* v3: fix tests

* v3: do not await a non promise

* v3: q -> bluebird

* Changed id param for registration response

* Updated party query and create

* Ensured login callback happens after user sync

* Add challenges to groups. Fixed isMemberOfGuild check

* Updated party and group tests

* Fixed cron test

* return user.id and send analytics event before changing page

* fix trailing spaces

* disable redirects

* Api v3 party tavern fixes (#7191)

* Added check if user is in party before query

* Cached party query. Prevented party request when user is not in party. Updated Party create with no invites

* Update tavern ctrl to use new promise

* v3: misc fixes

* Api v3 task fixes (#7193)

* Update task view to use _id

* Added try catch to user service ops calls

* v3 client: saving after syncing is complete

* Fixed test broken by part sync change (#7195)

* v3: fix todo scoring and try to fix production testing problem

* revert changes to mongoose config

* mongoose: increase keepAlive

* test mongoose fix

* fix: Only apply captureStackTrace if it exists on the error object

* v3: fix reminders with no startDate

* mongoose: use options

* chore(): rename website/src -> website/server and website/public -> website/client (#7199)

* v3 fix GET /groups: return an error only if an invalid type is supplied not when there are 0 results (#7203)

* [API v3] Fix calls to user.ops and deleting tags (#7204)

* v3: fixes calls to user.ops from views and deleting tags

* v3: fix tests that use user._statsComputed

* Api v3 fixes continued (#7205)

* Added timzeone offset back

* Added APIToken back to settings page

* Fixed fetch recent messages for party

* Fixed returning group description

* Fixed check if user is member of challenge

* Fixed party members appearing in header

* Updated get myGroups param to include public groups. Fixed isMemberOf group

* Fixed hourglass purchase

* Fixed challenge addding tasks on first creating

* Updated tests to accomidate new changes

* fix: Correct checklist on client

Closes #7207

* fix: Pin eslint to 2.9

* minor improvements to cron code for clarity; fix inaccurate comments; add TODOs for rest-in-inn actions

* fix: Add missing type param to equip call

closes #7212

* rename and reword pubChalsMinPrize to reflect that it's only for Tavern challenges

* allows players to send gems to each other; other minor related changes - fixes https://github.com/HabitRPG/habitrpg/issues/7227

* fix tests for /members/transfer-gems

* fix: Set gems sent notification as translatable string

* chore: Remove unusued variable

* fix: Remove requirement on message paramter in transfer-gems

* add a missing variable declaration

* chore: clarify comments on cron code

* fix: Correct client request from habitrpg -> tavern

* update apidoc URL in package.json

Closes #7222

* Fixed start party by invites

* Updated spell casting to v3

* Fixed adding and removing tags on tasks

* Fixed page reload on settings change

* Fixed battle monsters with friends button

* Loaded completed todos when done is clicked

* chore: Reinstate floating version number for eslint

babel-eslint regression fixed

* Fixed reload tests

* change "an user" to "a user" in comments and text (no code changes) (#7257)

* fix: Alert user that drops were recieved

* remove userServices.js from karma.conf - it's been moved to website/client/js/services

* feat: Create debug update user route

* fix: Correct set cron debug function

* feat: Add make admin button to debug menu

* lint: Add missing semicolons in test

* fix: Temporarilly comment out udpate user debug route

* v3: fix _tmp for crit and streakBonus

* v3: execute all actions when leaving a solo party

* v3 client: fix group not found when leaving party

* v3 migration: fix challenge prize

* v3 cron: only save modified tasks

* v3: add CHALLENGE_TASK_NOT_FOUND to valid broken reasons

* v3: fix tasks chart

* v3 client: fix ability to leave challenge

* v3 client: fix filtering by tag and correctly show tag tooltip

* v3 common: fix tags tests

* v3 client: support unlinking not found challenges tasks

* v3: disable Bluebird warning for missing return, fixes #7269

* feat: Separate out update-user into set-cron and make-admin debug routes

* chore: Disable make admin debug route for v3 prod testing

* v3: misc fixes

* v3: misc fixes

* v3: fix adding multiple tasks

* Fixed join/leave button updates

* Queried only user groups to be available when creating challenges

* Fixed bulk add tasks to challenge

* Synced challenge tasks after leave and join.

* Fixed default selected group

* Fixed challenge member info. Fixed challenge winner selection

* Fixed deleting challenge tasks

* Fixed particiapting filter

* v3 client: fix casting spells

* v3: do not log sensitive data

* v3: always save user when casting spell

* v3: always save user when casting spell

* v3: more fixes for spells

* fix typos and missing information in apidocs - fixes https://github.com/HabitRPG/habitrpg/issues/7277 (#7282)

* v3: add TODO for client side spells

* feat: Add modify inventory debug menu

* Fixed viewing user progress on challenge

* Updated tests

* fix: Fix quest progress button

* fix incorrect Armoire test; remove unneeded param details from apidocs; disambiguate health potion

* v3: fix stealth casting

* v3: fix tasks saving and selection for rebirth reroll and reset (server-only)

* v3: fix auto allocation

* v3 client: misc fixes

* rename buyPotion and buy-potion to buyHealthPotion and buy-health-potion; fix apidoc param error

* Added delete for saved challenge task

* Fixed member modal on front page

* adjust text in apidocs for errors / clarity / consistency / standard terminology (no code changes) (#7298)

* fix bug in Rebirth test, add new tests, adjust apidocs (#7293)

* Updated task model to allow setting streak (#7306)

* fix: Correct missing * in apidoc comments

* Api v3 challenge fixes (#7287)

* Fixed join/leave button updates

* Queried only user groups to be available when creating challenges

* Fixed bulk add tasks to challenge

* Synced challenge tasks after leave and join.

* Fixed default selected group

* Fixed challenge member info. Fixed challenge winner selection

* Fixed deleting challenge tasks

* Fixed particiapting filter

* Fixed viewing user progress on challenge

* Updated tests

* Added delete for saved challenge task

* v3: fix sorting

* [API v3] add CRON_SAFE_MODE (#7286)

* add CRON_SAFE_MODE to example config file, fix some bugs, add an unrelated low-priority TODO

* create CRON_SAFE_MODE to disable parts of cron for use after extended outage - fixes https://github.com/HabitRPG/habitrpg/issues/7161

* fix a bug with CRON_SAFE_MODE, remove duplicated code, remove completed TODO comment

* fix check for CRON_SAFE_MODE

* v3 client: fix typo

* adjust debug menu Modify Inventory: hungrier pets, fewer Special items, "Hide" buttons

* completed To-Dos: return the 30 most recent instead of 30 oldest (#7318)

* v3 migration: fix createdAt date

* adjust locales text, key names, and files for Rebirth, Reset, and Fortify / ReRoll for consistency with existing strings (#7321)

* v3: fix unlinking multiple tasks

* v3 fix releasing pets

* v3: fix authenticating with apiUrl

* v3: fix typo

* v3 fix client tests for unlinking

* v3 client: do not show start quest button when quest is active

* v3 client: fix ability to send cards

* v3 client: fix misc challenge issues

* v3: fix notifications

* v3 client: more user friendly errors

* v3 client: only load completed todos once

* v3 client: fix tests

* v3: move TAVERN_ID to common code

* fix: Provide default type and text for new task creation in score route

* fix: Provide default history [] for habit in score route

* fix: Add _legacyId prop to tasks to support non-uuid identifiers

* chore: Change v3 migration to use _legacyId instead of legacyId

* fix: check for _legacyId in tasks if id does not exist

* refactor: Extract out finding task by id or _legacyId into a function

* Api v3 party quest fixes (#7341)

* Fix display of add challenge message when group challenges are empty

* Fixed forced quest start to update quest without reload

* Fixed needing to reload when accepting party invite

* Fix group leave and join reload

* Fixed leave current party and join another

* Updated party tests

* v3 client: remove console.log statement

* v3: misc fixes

* v3 client: fix predicatbale random

* v3: info about API v3

* v3: update footer with links to developer resources

* v3: support party invitation from email

* v3 client: fix chat flagging

* fix: Correct get tasks route to properly get todos (#7349)

* move locales strings from api-v3.json to other locales files (#7347)

* move locales strings from api-v3.json: authentication strings -> front.json

* move locales strings from api-v3.json: authentication strings -> tasks.json

* move locales strings from api-v3.json: authentication strings -> groups.json

* move locales strings from api-v3.json: authentication strings -> challenge.json

* move locales strings from api-v3.json: authentication strings -> groups.json (again)

* move locales strings from api-v3.json: authentication strings -> quests.json

* move locales strings from api-v3.json: authentication strings -> subscriber.json

* move locales strings from api-v3.json: authentication strings -> spells.json

* move locales strings from api-v3.json: authentication strings -> character.json

* move locales strings from api-v3.json: authentication strings -> groups.json (PMs)

* move locales strings from api-v3.json: authentication strings -> npc.json

* move locales strings from api-v3.json: authentication strings -> pets.json

* move locales strings from api-v3.json: authentication strings -> miscellaneous

* move locales strings from api-v3.json: authentication strings -> contrib.json and settings.json

* move locales strings from api-v3.json: delete unused string (invalidTasksOwner), delete api-v3.json, whitespace cleanup

* v3 client: fix sticky header

* v3: remove unused code

* v3 client: correctly redirect after inviting

* Removed v2 calls from views (#7351)

* v3: fix tests for challenge export

* v3: fallbackto authWithHeaders if wuthWithSession or authWithUrl fails

* Added force cache update when fetching new messages (#7360)

* v3: fetch whole user when booting from group tto avoid issues with pre save hook expecting all data

* v3: misc fixes for payments

* v3: limit fields of challenge tasks that can be updated

* fix(tests): never connect to NODE_DB_URI for tests

* Added new route for setting last cron and updated front end

* v3: fix iap url

* v3: fix build and ios IAP

* Changed route to user set custom day start

* v3: iap accessible under /api/v3, fixes to spells and groups invitations

* v3: correctly use v3 routes in client

* remove XP, GP when unticking a Daily with a completed checklist - fixes https://github.com/HabitRPG/habitrpg/issues/7246

* use natural language for error message about skills on challenge tasks (#7336), fix other gramatical error

* Updated ui when user rejects a guild invite (#7368)

* feat: complete custom day start route

Closes #7363

* fix: Correct spelling of healAll skill

fix: Correct sprite name of healAll skill

* fix: Change all instances of spookDust -> spookySparkles

* add dateCreated to all tasks; add empty challenge object to tasks that don't have one (#7386)

* add plumilla to artists for Tangle Tree in Bailey message

* Fixed quest drop modal (#7377)

* Fixed quest drop modal

* Fixed broken party test

* [API v3] Maintenance Mode (#7367)

* WIP(maintenance): maintenance

* WIP(maintenance): working locale features

* fix(maintenance): don't translate info page target

* WIP(maintenance): start adding info page

* fix(maintenance): linting

* feat: Add container to maintenance info page

* fix(maintenance): add config.json edits
Also DRY variables for main vs info pages

* fix(maintenance): linting

* refactor(maintenance): further slim down variables

* refactor: Remove unnecessary variables

* fix: Correct string interpolation in maintenace view

* feat: Dynamically add time to maintenance pages

* maintenance mode: do not connect to mongodb

* fix(maintenance): clean up timezones etc.

* fix(maintenance): remove unneeded sprite

* Tavern party challenges invites fix (#7394)

* Added challenges and invitations to party

* Loaded tavern challenges

* Updated group and quest services tests

* v3: implement automatic syncing if user is not up to date

* Removed unnecessary fields when updating groups and challenges (#7395)

* v3: do not saved populated user

* v3: correctly return user subset

* Chained party promises together (#7396)

* v3: $w -> splitWhitespace

* use bluebird

* use babel polyfill

* migration: fix items

* update links for v3

* Updated shortname validation to support multiple browsers

* Docs changes (#7401)

* chore: Clarify transfer-gems documentation

* chore: Clarify api status route documentation

* chore: Mark webhooks as BETA

* Added tags update route. Added sort to user service (#7381)

* Added tags update route. Added sort to user service

* Change update tasks route to reorder tasks

* Fixed linting issue

* Changed params for reorder tags route

* Fixed not found tag and added test

* Added password confirmation when deleteing account (#7402)

* fix production logging

* feat(commit): push

* empty commit

* feat(maintenance): post-downtime news & awards (#7406)

* fix exporting avatar

* second attempt at fixing exporting avatar

* fix production logging

* s3: convert moment to date instance

* fix avatar sharing and caching (30 minutes)

* fix: Correct missing parameter

Closes #7433

* fix: Validate challenge shortname on server

* adjust text strings - fixes https://github.com/HabitRPG/habitrpg/issues/5631 and also Short Name -> Tag Name
2016-05-23 13:58:31 +02:00

281 lines
9.8 KiB
JavaScript

import moment from 'moment';
import common from '../../../../common/';
import { preenUserHistory } from '../../libs/api-v3/preening';
import _ from 'lodash';
import nconf from 'nconf';
const CRON_SAFE_MODE = nconf.get('CRON_SAFE_MODE') === 'true';
const shouldDo = common.shouldDo;
const scoreTask = common.ops.scoreTask;
// const maxPMs = 200;
let CLEAR_BUFFS = {
str: 0,
int: 0,
per: 0,
con: 0,
stealth: 0,
streaks: false,
};
function grantEndOfTheMonthPerks (user, now) {
let plan = user.purchased.plan;
if (moment(plan.dateUpdated).format('MMYYYY') !== moment().format('MMYYYY')) {
plan.gemsBought = 0; // reset gem-cap
plan.dateUpdated = now;
// For every month, inc their "consecutive months" counter. Give perks based on consecutive blocks
// If they already got perks for those blocks (eg, 6mo subscription, subscription gifts, etc) - then dec the offset until it hits 0
// TODO use month diff instead of ++ / --? see https://github.com/HabitRPG/habitrpg/issues/4317
_.defaults(plan.consecutive, {count: 0, offset: 0, trinkets: 0, gemCapExtra: 0});
plan.consecutive.count++;
if (plan.consecutive.offset > 0) {
plan.consecutive.offset--;
} else if (plan.consecutive.count % 3 === 0) { // every 3 months
plan.consecutive.trinkets++;
plan.consecutive.gemCapExtra += 5;
if (plan.consecutive.gemCapExtra > 25) plan.consecutive.gemCapExtra = 25; // cap it at 50 (hard 25 limit + extra 25)
}
}
}
function removeTerminatedSubscription (user) {
// If subscription's termination date has arrived
let plan = user.purchased.plan;
if (plan.dateTerminated && moment(plan.dateTerminated).isBefore(new Date())) {
_.merge(plan, {
planId: null,
customerId: null,
paymentMethod: null,
});
_.merge(plan.consecutive, {
count: 0,
offset: 0,
gemCapExtra: 0,
});
user.markModified('purchased.plan');
}
}
function performSleepTasks (user, tasksByType, now) {
user.stats.buffs = _.cloneDeep(CLEAR_BUFFS);
tasksByType.dailys.forEach((daily) => {
let completed = daily.completed;
let thatDay = moment(now).subtract({days: 1});
if (shouldDo(thatDay.toDate(), daily, user.preferences) || completed) {
// TODO also untick checklists if the Daily was due on previous missed days, if two or more days were missed at once -- https://github.com/HabitRPG/habitrpg/pull/7218#issuecomment-219256016
daily.checklist.forEach(box => box.completed = false);
}
daily.completed = false;
});
}
// Perform various beginning-of-day reset actions.
export function cron (options = {}) {
let {user, tasksByType, analytics, now = new Date(), daysMissed, timezoneOffsetFromUserPrefs} = options;
user.auth.timestamps.loggedin = now;
user.lastCron = now;
user.preferences.timezoneOffsetAtLastCron = timezoneOffsetFromUserPrefs;
// User is only allowed a certain number of drops a day. This resets the count.
if (user.items.lastDrop.count > 0) user.items.lastDrop.count = 0;
// "Perfect Day" achievement for perfect-days
let perfect = true;
if (user.isSubscribed()) {
grantEndOfTheMonthPerks(user, now);
if (!CRON_SAFE_MODE) removeTerminatedSubscription(user);
}
// User is resting at the inn.
// On cron, buffs are cleared and all dailies are reset without performing damage
if (user.preferences.sleep === true) {
performSleepTasks(user, tasksByType, now);
return;
}
let multiDaysCountAsOneDay = true;
// If the user does not log in for two or more days, cron (mostly) acts as if it were only one day.
// When site-wide difficulty settings are introduced, this can be a user preference option.
// Tally each task
let todoTally = 0;
tasksByType.todos.forEach(task => { // make uncompleted To-Dos redder (further incentive to complete them)
scoreTask({
task,
user,
direction: 'down',
cron: true,
times: multiDaysCountAsOneDay ? 1 : daysMissed,
});
todoTally += task.value;
});
// For incomplete Dailys, add value (further incentive), deduct health, keep records for later decreasing the nightly mana gain
let dailyChecked = 0; // how many dailies were checked?
let dailyDueUnchecked = 0; // how many dailies were un-checked?
if (!user.party.quest.progress.down) user.party.quest.progress.down = 0;
tasksByType.dailys.forEach((task) => {
let completed = task.completed;
// Deduct points for missed Daily tasks
let EvadeTask = 0;
let scheduleMisses = daysMissed;
if (completed) {
dailyChecked += 1;
} else {
// dailys repeat, so need to calculate how many they've missed according to their own schedule
scheduleMisses = 0;
for (let i = 0; i < daysMissed; i++) {
let thatDay = moment(now).subtract({days: i + 1});
if (shouldDo(thatDay.toDate(), task, user.preferences)) {
scheduleMisses++;
if (user.stats.buffs.stealth) {
user.stats.buffs.stealth--;
EvadeTask++;
}
if (multiDaysCountAsOneDay) break;
}
}
if (scheduleMisses > EvadeTask) {
// The user did not complete this due Daily (but no penalty if cron is running in safe mode).
if (CRON_SAFE_MODE) {
dailyChecked += 1; // allows full allotment of mp to be gained
} else {
perfect = false;
if (task.checklist && task.checklist.length > 0) { // Partially completed checklists dock fewer mana points
let fractionChecked = _.reduce(task.checklist, (m, i) => m + (i.completed ? 1 : 0), 0) / task.checklist.length;
dailyDueUnchecked += 1 - fractionChecked;
dailyChecked += fractionChecked;
} else {
dailyDueUnchecked += 1;
}
let delta = scoreTask({
user,
task,
direction: 'down',
times: multiDaysCountAsOneDay ? 1 : scheduleMisses - EvadeTask,
cron: true,
});
// Apply damage from a boss, less damage for Trivial priority (difficulty)
user.party.quest.progress.down += delta * (task.priority < 1 ? task.priority : 1);
// NB: Medium and Hard priorities do not increase damage from boss. This was by accident
// initially, and when we realised, we could not fix it because users are used to
// their Medium and Hard Dailies doing an Easy amount of damage from boss.
// Easy is task.priority = 1. Anything < 1 will be Trivial (0.1) or any future
// setting between Trivial and Easy.
}
}
}
task.history.push({
date: Number(new Date()),
value: task.value,
});
task.completed = false;
if (completed || scheduleMisses > 0) {
task.checklist.forEach(i => i.completed = false);
}
});
// move singleton Habits towards yellow.
tasksByType.habits.forEach((task) => { // slowly reset 'onlies' value to 0
if (task.up === false || task.down === false) {
task.value = Math.abs(task.value) < 0.1 ? 0 : task.value = task.value / 2;
}
});
// Finished tallying
user.history.todos.push({date: now, value: todoTally});
// tally experience
let expTally = user.stats.exp;
let lvl = 0; // iterator
while (lvl < user.stats.lvl - 1) {
lvl++;
expTally += common.tnl(lvl);
}
user.history.exp.push({date: now, value: expTally});
// preen user history so that it doesn't become a performance problem
// also for subscribed users but differently
// TODO also do while resting in the inn. Note that later we'll be allowing the value/color of tasks to change while sleeping (https://github.com/HabitRPG/habitrpg/issues/5232), so the code in performSleepTasks() might be best merged back into here for that. Perhaps wait until then to do preen history for sleeping users.
preenUserHistory(user, tasksByType, user.preferences.timezoneOffset);
if (perfect) {
user.achievements.perfect++;
let lvlDiv2 = Math.ceil(common.capByLevel(user.stats.lvl) / 2);
user.stats.buffs = {
str: lvlDiv2,
int: lvlDiv2,
per: lvlDiv2,
con: lvlDiv2,
stealth: 0,
streaks: false,
};
} else {
user.stats.buffs = _.cloneDeep(CLEAR_BUFFS);
}
// Add 10 MP, or 10% of max MP if that'd be more. Perform this after Perfect Day for maximum benefit
// Adjust for fraction of dailies completed
if (dailyDueUnchecked === 0 && dailyChecked === 0) dailyChecked = 1;
user.stats.mp += _.max([10, 0.1 * user._statsComputed.maxMP]) * dailyChecked / (dailyDueUnchecked + dailyChecked);
if (user.stats.mp > user._statsComputed.maxMP) user.stats.mp = user._statsComputed.maxMP;
// After all is said and done, progress up user's effect on quest, return those values & reset the user's
let progress = user.party.quest.progress;
let _progress = _.cloneDeep(progress);
_.merge(progress, {down: 0, up: 0});
progress.collect = _.transform(progress.collect, (m, v, k) => m[k] = 0);
// TODO: Clean PMs - keep 200 for subscribers and 50 for free users. Should also be done while resting in the inn
// let numberOfPMs = Object.keys(user.inbox.messages).length;
// if (numberOfPMs > maxPMs) {
// _(user.inbox.messages)
// .sortBy('timestamp')
// .takeRight(numberOfPMs - maxPMs)
// .each(pm => {
// delete user.inbox.messages[pm.id];
// }).value();
//
// user.markModified('inbox.messages');
// }
// Analytics
user.flags.cronCount++;
analytics.track('Cron', { // TODO also do while resting in the inn. https://github.com/HabitRPG/habitrpg/issues/7161#issuecomment-218214191
category: 'behavior',
gaLabel: 'Cron Count',
gaValue: user.flags.cronCount,
uuid: user._id,
user,
resting: user.preferences.sleep,
cronCount: user.flags.cronCount,
progressUp: _.min([_progress.up, 900]),
progressDown: _progress.down,
});
return _progress;
}