Compare commits

..

1 Commits

Author SHA1 Message Date
Sabe Jones
6a439922e6 4.151.2 2020-07-31 14:38:55 -05:00
12455 changed files with 458458 additions and 209322 deletions

View File

@@ -1 +1,2 @@
https://github.com/heroku/heroku-buildpack-nodejs.git
https://github.com/heroku/heroku-buildpack-nodejs.git
https://github.com/stomita/heroku-buildpack-phantomjs.git

View File

@@ -8,10 +8,10 @@ website/client/
website/common/transpiled-babel/
dist/
dist-client/
apidoc/html/
apidoc_build/
content_cache/
i18n_cache/
node_modules/
# Old migrations, disabled
migrations/archive/*
migrations/archive/*

View File

@@ -1,11 +1,6 @@
/* eslint-disable import/no-commonjs */
module.exports = {
root: true,
extends: [
'habitrpg/lib/node',
'habitrpg/lib/node'
],
rules: {
'prefer-regex-literals': 'warn',
'import/no-extraneous-dependencies': 'off',
},
};
}

View File

@@ -4,7 +4,7 @@
# Pull Request
[Please see these instructions for adding a pull request](https://habitica.fandom.com/wiki/Using_Your_Local_Install_to_Modify_Habitica's_Website_and_API)
[Please see these instructions for adding a pull request](http://habitica.fandom.com/wiki/Using_Your_Local_Install_to_Modify_Habitica%27s_Website_and_API)
# Requesting a feature
@@ -12,4 +12,4 @@ Habitica uses [this Google form](https://docs.google.com/forms/d/e/1FAIpQLScPhrw
# Contributing Code
See [Contributing to Habitica](https://habitica.fandom.com/wiki/Contributing_to_Habitica#Coders_.28Web_.26_Mobile.29)
See [Contributing to Habitica](http://habitica.fandom.com/wiki/Contributing_to_Habitica#Coders_.28Web_.26_Mobile.29)

View File

@@ -1,19 +1,20 @@
[//]: # (Before logging this issue, please contact site administrators from "Report a Bug" in the Habitica website's Help menu. If a GitHub issue is needed, staff will let you know and will most likely log one on your behalf. It is recommended that you don't create a new issue unless advised to.)
[//]: # (Before logging this issue, please post to the Report a Bug guild from the Habitica website's Help menu. Most bugs can be handled quickly there. If a GitHub issue is needed, you will be advised of that by a moderator or staff member -- a player with a dark blue or purple name. It is recommended that you don't create a new issue unless advised to.)
[//]: # (Bugs in the mobile apps can be reported via Menu > About > Support.)
[//]: # (Bugs in the mobile apps can also be reported there.)
[//]: # (If you have a feature request, use "Help > Request a Feature", not GitHub.)
[//]: # (If you have a feature request, use "Help > Request a Feature", not GitHub or the Report a Bug guild.)
[//]: # (For more guidelines see https://github.com/HabitRPG/habitica/issues/2760)
[//]: # (Fill out relevant information - UUID is found from the Habitica website at User Icon > Settings > API)
[//]: # (Fill out relevant information - UUID is found from the Habitia website at User Icon > Settings > API)
### General Info
* UUID:
* Browser:
* OS:
* UUID:
* Browser:
* OS:
### Description
[//]: # (Describe bug in detail here. Include screenshots if helpful.)
#### Console Errors
[//]: # (Include any JavaScript console errors here.)

View File

@@ -1,4 +1,4 @@
[//]: # (Note: See https://habitica.fandom.com/wiki/Using_Your_Local_Install_to_Modify_Habitica%27s_Website_and_API for more info)
[//]: # (Note: See http://habitica.fandom.com/wiki/Using_Your_Local_Install_to_Modify_Habitica%27s_Website_and_API for more info)
[//]: # (Put Issue # here, if applicable. This will automatically close the issue if your PR is merged in)
Fixes put_#_and_issue_number_here
@@ -11,4 +11,4 @@ Fixes put_#_and_issue_number_here
[//]: # (Put User ID in here - found on the Habitica website at User Icon > Settings > API)
----
UUID:
UUID:

150
.github/dependabot.yml vendored
View File

@@ -1,150 +0,0 @@
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: weekly
time: "06:00"
timezone: Europe/Rome
open-pull-requests-limit: 99
ignore:
- dependency-name: express-validator
versions:
- 6.10.0
- 6.10.1
- 6.9.2
- dependency-name: "@babel/core"
versions:
- 7.12.13
- 7.12.16
- 7.12.17
- 7.13.1
- 7.13.10
- 7.13.13
- 7.13.14
- 7.13.15
- 7.13.8
- dependency-name: redis
versions:
- 3.1.0
- dependency-name: stripe
versions:
- 8.134.0
- 8.135.0
- 8.137.0
- 8.138.0
- 8.140.0
- 8.142.0
- dependency-name: "@babel/register"
versions:
- 7.12.13
- 7.13.14
- 7.13.8
- dependency-name: mongoose
versions:
- 5.11.14
- 5.11.15
- 5.11.16
- 5.11.17
- 5.11.18
- 5.11.19
- 5.12.0
- 5.12.1
- 5.12.2
- 5.12.3
- dependency-name: jwks-rsa
versions:
- 1.12.3
- 2.0.1
- 2.0.2
- dependency-name: "@babel/preset-env"
versions:
- 7.12.13
- 7.12.16
- 7.12.17
- 7.13.10
- 7.13.12
- 7.13.8
- 7.13.9
- dependency-name: image-size
versions:
- 0.9.4
- 0.9.5
- 0.9.7
- dependency-name: winston-loggly-bulk
versions:
- 3.2.0
- dependency-name: chai
versions:
- 4.3.0
- 4.3.3
- dependency-name: mocha
versions:
- 8.2.1
- 8.3.0
- 8.3.1
- dependency-name: "@google-cloud/trace-agent"
versions:
- 5.1.2
- dependency-name: monk
versions:
- 7.3.3
- package-ecosystem: npm
directory: "/website/client"
schedule:
interval: weekly
time: "06:00"
timezone: Europe/Rome
open-pull-requests-limit: 99
ignore:
- dependency-name: eslint-plugin-vue
versions:
- 7.5.0
- 7.6.0
- 7.7.0
- 7.8.0
- 7.9.0
- dependency-name: core-js
versions:
- 3.10.0
- 3.10.1
- 3.9.0
- 3.9.1
- dependency-name: bootstrap
versions:
- 4.6.0
- dependency-name: y18n
versions:
- 4.0.1
- dependency-name: hellojs
versions:
- 1.18.8
- 1.19.2
- dependency-name: chai
versions:
- 4.3.0
- 4.3.3
- dependency-name: amplitude-js
versions:
- 7.4.2
- 7.4.3
- 7.4.4
- dependency-name: pug
versions:
- 3.0.2
- dependency-name: sass
versions:
- 1.32.6
- 1.32.7
- 1.32.8
- dependency-name: "@vue/test-utils"
versions:
- 1.1.2
- 1.1.3
- dependency-name: intro.js
versions:
- 3.2.1
- 3.3.1
- dependency-name: sass-loader
versions:
- 10.1.1

View File

@@ -2,28 +2,24 @@ name: Test
on: [push, pull_request]
permissions:
contents: read
jobs:
lint:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [21.x]
node-version: [12.x]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: sudo apt-get -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
npm i
npm ci
env:
CI: true
NODE_ENV: test
@@ -32,20 +28,19 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [21.x]
node-version: [12.x]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: sudo apt-get -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
npm i
npm ci
env:
CI: true
NODE_ENV: test
@@ -54,20 +49,19 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [21.x]
node-version: [12.x]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: sudo apt-get -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
npm i
npm ci
env:
CI: true
NODE_ENV: test
@@ -77,20 +71,19 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [21.x]
node-version: [12.x]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: sudo apt-get -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
npm i
npm ci
env:
CI: true
NODE_ENV: test
@@ -99,20 +92,19 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [21.x]
node-version: [12.x]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: sudo apt-get -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
npm i
npm ci
env:
CI: true
NODE_ENV: test
@@ -122,14 +114,14 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [21.x]
node-version: [12.x]
mongodb-version: [4.2]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Start MongoDB ${{ matrix.mongodb-version }} Replica Set
@@ -137,11 +129,10 @@ jobs:
with:
mongodb-version: ${{ matrix.mongodb-version }}
mongodb-replica-set: rs
- run: sudo apt-get -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
npm i
npm ci
env:
CI: true
NODE_ENV: test
@@ -152,14 +143,14 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [21.x]
node-version: [12.x]
mongodb-version: [4.2]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Start MongoDB ${{ matrix.mongodb-version }} Replica Set
@@ -167,11 +158,10 @@ jobs:
with:
mongodb-version: ${{ matrix.mongodb-version }}
mongodb-replica-set: rs
- run: sudo apt-get -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
npm i
npm ci
env:
CI: true
NODE_ENV: test
@@ -182,14 +172,14 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [21.x]
node-version: [12.x]
mongodb-version: [4.2]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Start MongoDB ${{ matrix.mongodb-version }} Replica Set
@@ -197,11 +187,10 @@ jobs:
with:
mongodb-version: ${{ matrix.mongodb-version }}
mongodb-replica-set: rs
- run: sudo apt-get -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
npm i
npm ci
env:
CI: true
NODE_ENV: test
@@ -213,44 +202,21 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [21.x]
node-version: [12.x]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: sudo apt-get -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
npm i
npm ci
env:
CI: true
NODE_ENV: test
- run: npm run test:unit
working-directory: ./website/client
production-build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [21.x]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: sudo apt-get -y install libkrb5-dev
- run: cp config.json.example config.json
- name: npm install
run: |
npm install
env:
CI: true
NODE_ENV: production
working-directory: ./website/client

4
.gitignore vendored
View File

@@ -5,7 +5,7 @@ website/common/transpiled-babel/
node_modules
content_cache
i18n_cache
apidoc/html
apidoc_build
*.swp
.idea*
config.json
@@ -38,9 +38,7 @@ yarn.lock
.elasticbeanstalk/*
!.elasticbeanstalk/*.cfg.yml
!.elasticbeanstalk/*.global.yml
/.vscode
habitica.code-workspace
# webstorm fake webpack for path intellisense
webpack.webstorm.config

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "habitica-images"]
path = habitica-images
url = https://github.com/HabitRPG/habitica-images

2
.nvmrc
View File

@@ -1 +1 @@
20
12

View File

@@ -1,7 +0,0 @@
# Files not included in deployments to Heroku, to save on file size.
/habitica-images
/test
/migrations
/scripts
/database_reports

29
Dockerfile Normal file
View File

@@ -0,0 +1,29 @@
FROM node:12
ENV ADMIN_EMAIL admin@habitica.com
ENV EMAILS_COMMUNITY_MANAGER_EMAIL admin@habitica.com
ENV AMAZON_PAYMENTS_CLIENT_ID amzn1.application-oa2-client.68ed9e6904ef438fbc1bf86bf494056e
ENV AMAZON_PAYMENTS_SELLER_ID AMQ3SB4SG5E91
ENV AMPLITUDE_KEY e8d4c24b3d6ef3ee73eeba715023dd43
ENV BASE_URL https://habitica.com
ENV FACEBOOK_KEY 128307497299777
ENV GA_ID UA-33510635-1
ENV GOOGLE_CLIENT_ID 1035232791481-32vtplgnjnd1aufv3mcu1lthf31795fq.apps.googleusercontent.com
ENV LOGGLY_CLIENT_TOKEN ab5663bf-241f-4d14-8783-7d80db77089a
ENV NODE_ENV production
ENV STRIPE_PUB_KEY pk_85fQ0yMECHNfHTSsZoxZXlPSwSNfA
ENV APPLE_AUTH_CLIENT_ID 9Q9SMRMCNN.com.habitrpg.ios.Habitica
# Install global packages
RUN npm install -g gulp-cli mocha
# Clone Habitica repo and install dependencies
RUN mkdir -p /usr/src/habitrpg
WORKDIR /usr/src/habitrpg
RUN git clone --branch release --depth 1 https://github.com/HabitRPG/habitica.git /usr/src/habitrpg
RUN npm set unsafe-perm true
RUN npm install
# Start Habitica
EXPOSE 80 8080 36612
CMD ["node", "./website/transpiled-babel/index.js"]

View File

@@ -1,4 +1,4 @@
FROM node:20
FROM node:12
# Install global packages
RUN npm install -g gulp-cli mocha

View File

@@ -4,11 +4,11 @@ Habitica ![Build Status](https://github.com/HabitRPG/habitica/workflows/Test/bad
[Habitica](https://habitica.com) is an open source habit building program which treats your life like a Role Playing Game. Level up as you succeed, lose HP as you fail, earn money to buy weapons and armor.
**We need more programmers!** Your assistance will be greatly appreciated. The wiki pages below and the additional pages they link to will tell you how to get started on contributing code and where you can go to seek further help or ask questions:
* [Guidance for Blacksmiths](https://habitica.fandom.com/wiki/Guidance_for_Blacksmiths) - an introduction to the technologies used and how the software is organized.
* [Setting up Habitica Locally](https://habitica.fandom.com/wiki/Setting_up_Habitica_Locally) - how to set up a local install of Habitica for development and testing on various platforms.
* [Guidance for Blacksmiths](http://habitica.fandom.com/wiki/Guidance_for_Blacksmiths) - an introduction to the technologies used and how the software is organized.
* [Setting up Habitica Locally](http://habitica.fandom.com/wiki/Setting_up_Habitica_Locally) - how to set up a local install of Habitica for development and testing on various platforms.
Habitica's code is licensed as described at https://github.com/HabitRPG/habitica/blob/develop/LICENSE
**Found a bug?** Please report it to [admin email](mailto:admin@habitica.com) rather than creating an issue (an admin will advise you if a new issue is necessary; usually it is not).
**Found a bug?** Please report it in the [Report a Bug guild](https://habitica.com/groups/guild/a29da26b-37de-4a71-b0c6-48e72a900dac) rather than creating an issue (an admin will advise you if a new issue is necessary; usually it is not).
**Have any questions about Habitica or its community?** See the links in the [habitica.com](https://habitica.com) website's Help menu or drop in to [Guilds > Tavern Chat](https://habitica.com/groups/tavern) to ask questions or chat socially!

View File

@@ -2,8 +2,6 @@
"name": "Habitica V3 API Documentation",
"title": "Habitica",
"url": "https://habitica.com",
"version": "3.0.0",
"sampleUrl": null,
"header": {
"title": "Introduction",
"filename": "apidoc/header.md"

View File

@@ -2,4 +2,4 @@
This webpage includes the documentation for version 3 of the [Habitica](https://habitica.com) API.
If you're developing a 3rd party tool that uses the Habitica API you should read the [Guidance for Comrades](https://habitica.fandom.com/wiki/Guidance_for_Comrades) and in particular the section called [Rules for Third-Party Tools](https://habitica.fandom.com/wiki/Guidance_for_Comrades#Rules_for_Third-Party_Tools) which includes suggestions on how to best use the API and the rules to follow when interacting with it.
If you're developing a 3rd party tool that uses the Habitica API you should read the [Guidance for Comrades](https://habitica.fandom.com/wiki/Guidance_for_Comrades) and in particular the section called [Rules for Third-Party Tools](https://habitica.fandom.com/wiki/Guidance_for_Comrades#Rules_for_Third-Party_Tools) which includes suggestions on how to best use the API and the rules to follow when interacting with it.

View File

@@ -1,5 +1,4 @@
{
"ACCOUNT_MIN_CHAT_AGE": "0",
"ADMIN_EMAIL": "you@example.com",
"AMAZON_PAYMENTS_CLIENT_ID": "CLIENT_ID",
"AMAZON_PAYMENTS_MODE": "sandbox",
@@ -72,7 +71,6 @@
"SLACK_URL": "https://hooks.slack.com/services/some-url",
"STRIPE_API_KEY": "aaaabbbbccccddddeeeeffff00001111",
"STRIPE_PUB_KEY": "22223333444455556666777788889999",
"STRIPE_WEBHOOKS_ENDPOINT_SECRET": "111111",
"TRANSIFEX_SLACK_CHANNEL": "transifex",
"WEB_CONCURRENCY": 1,
"SKIP_SSL_CHECK_KEY": "key",
@@ -86,6 +84,5 @@
"RATE_LIMITER_ENABLED": "false",
"REDIS_HOST": "aaabbbcccdddeeefff",
"REDIS_PORT": "1234",
"REDIS_PASSWORD": "12345678",
"TRUSTED_DOMAINS": "localhost,https://habitica.com"
"REDIS_PASSWORD": "12345678"
}

View File

@@ -119,7 +119,7 @@ function path(obj, path, def) {
* @param {String} path dot separated
* @param {*} def default value ( if result undefined )
* @returns {*}
* https://stackoverflow.com/a/16190716
* http://stackoverflow.com/a/16190716
* Usage: console.log(path(someObject, pathname));
*/
for(var i = 0,path = path.split('.'),len = path.length; i < len; i++){

View File

@@ -1,3 +1,4 @@
version: "3"
services:
client:
build:
@@ -8,6 +9,7 @@ services:
- server
environment:
- BASE_URL=http://server:3000
image: habitica
networks:
- habitica
ports:
@@ -25,6 +27,7 @@ services:
- mongo
environment:
- NODE_DB_URI=mongodb://mongo/habitrpg
image: habitica
networks:
- habitica
ports:

View File

@@ -2,9 +2,8 @@ import gulp from 'gulp';
import clean from 'rimraf';
import apidoc from 'apidoc';
const APIDOC_DEST_PATH = './apidoc/html';
const APIDOC_DEST_PATH = './apidoc_build';
const APIDOC_SRC_PATH = './website/server';
const APIDOC_CONFIG_PATH = './apidoc/apidoc.json';
gulp.task('apidoc:clean', done => {
clean(APIDOC_DEST_PATH, done);
});
@@ -13,7 +12,6 @@ gulp.task('apidoc', gulp.series('apidoc:clean', done => {
const result = apidoc.createDoc({
src: APIDOC_SRC_PATH,
dest: APIDOC_DEST_PATH,
config: APIDOC_CONFIG_PATH,
});
if (result === false) {

View File

@@ -1,5 +1,3 @@
/* eslint-disable no-console */
import gulp from 'gulp';
import path from 'path';
import babel from 'gulp-babel';
@@ -48,17 +46,17 @@ gulp.task('build:prepare-mongo', async () => {
return;
}
console.log('MongoDB data folder is missing, setting up.'); // eslint-disable-line no-console
console.log('MongoDB data folder is missing, setting up.');
// use run-rs without --keep, kill it as soon as the replica set starts
const runRsProcess = spawn('run-rs', ['-v', '4.1.1', '-l', 'ubuntu1804', '--dbpath', 'mongodb-data', '--number', '1', '--quiet']);
const runRsProcess = spawn('run-rs', ['-v', '4.2.8', '-l', 'ubuntu1804', '--dbpath', 'mongodb-data', '--number', '1', '--quiet']);
for await (const chunk of runRsProcess.stdout) {
const stringChunk = chunk.toString();
console.log(stringChunk); // eslint-disable-line no-console
console.log(stringChunk);
// kills the process after the replica set is setup
if (stringChunk.includes('Started replica set')) {
console.log('MongoDB setup correctly.'); // eslint-disable-line no-console
console.log('MongoDB setup correctly.');
runRsProcess.kill();
}
}

View File

@@ -2,6 +2,7 @@ import mongoose from 'mongoose';
import nconf from 'nconf';
import repl from 'repl';
import gulp from 'gulp';
import logger from '../website/server/libs/logger';
import {
getDevelopmentConnectionUrl,
getDefaultConnectionOptions,
@@ -38,6 +39,10 @@ const improveRepl = context => {
mongoose.connect(
connectionUrl,
mongooseOptions,
err => {
if (err) throw err;
logger.info('Connected with Mongoose');
},
);
};

View File

@@ -1,8 +1,16 @@
import gulp from 'gulp';
import imagemin from 'gulp-imagemin';
import spritesmith from 'gulp.spritesmith';
import clean from 'rimraf';
import sizeOf from 'image-size';
import mergeStream from 'merge-stream';
import { basename } from 'path';
import { sync } from 'glob';
import { each } from 'lodash';
import vinylBuffer from 'vinyl-buffer';
// https://github.com/Ensighten/grunt-spritesmith/issues/67#issuecomment-34786248
const MAX_SPRITESHEET_SIZE = 1024 * 1024 * 3;
const IMG_DIST_PATH = 'website/client/src/assets/images/sprites/';
const CSS_DIST_PATH = 'website/client/src/assets/css/sprites/';
@@ -12,6 +20,49 @@ function checkForSpecialTreatment (name) {
return name.match(regex) || name === 'head_0';
}
function calculateImgDimensions (img, addPadding) {
let dims = sizeOf(img);
const requiresSpecialTreatment = checkForSpecialTreatment(img);
if (requiresSpecialTreatment) {
const newWidth = dims.width < 90 ? 90 : dims.width;
const newHeight = dims.height < 90 ? 90 : dims.height;
dims = {
width: newWidth,
height: newHeight,
};
}
let padding = 0;
if (addPadding) {
padding = dims.width * 8 + dims.height * 8;
}
if (!dims.width || !dims.height) console.error('MISSING DIMENSIONS:', dims); // eslint-disable-line no-console
const totalPixelSize = dims.width * dims.height + padding;
return totalPixelSize;
}
function calculateSpritesheetsSrcIndicies (src) {
let totalPixels = 0;
const slices = [0];
each(src, (img, index) => {
const imageSize = calculateImgDimensions(img, true);
totalPixels += imageSize;
if (totalPixels > MAX_SPRITESHEET_SIZE) {
slices.push(index - 1);
totalPixels = imageSize;
}
});
return slices;
}
function cssVarMap (sprite) {
// For hair, skins, beards, etc. we want to output a '.customize-options.WHATEVER' class,
// which works as a 60x60 image pointing at the proper part of the 90x90 sprite.
@@ -20,56 +71,94 @@ function cssVarMap (sprite) {
if (requiresSpecialTreatment) {
sprite.custom = {
px: {
offsetX: '-25px',
offsetY: '-15px',
offsetX: `-${sprite.x + 25}px`,
offsetY: `-${sprite.y + 15}px`,
width: '60px',
height: '60px',
},
};
}
// even more for shirts
if (sprite.name.indexOf('shirt') !== -1) {
sprite.custom.px.offsetX = '-29px';
sprite.custom.px.offsetY = '-42px';
}
if (sprite.name.indexOf('shirt') !== -1) sprite.custom.px.offsetY = `-${sprite.y + 35}px`; // even more for shirts
if (sprite.name.indexOf('hair_base') !== -1) {
const styleArray = sprite.name.split('_').slice(2, 3);
if (Number(styleArray[0]) > 14) {
sprite.custom.px.offsetY = '0'; // don't crop updos
}
if (Number(styleArray[0]) > 14) sprite.custom.px.offsetY = `-${sprite.y}px`; // don't crop updos
}
}
function createSpritesStream (name, src) {
const spritesheetSliceIndicies = calculateSpritesheetsSrcIndicies(src);
const stream = mergeStream();
const spriteData = gulp.src(src)
.pipe(spritesmith({
imgName: `spritesmith-${name}.png`,
cssName: `spritesmith-${name}.css`,
algorithm: 'binary-tree',
padding: 1,
cssTemplate: 'website/raw_sprites/css/css.template.handlebars',
cssVarMap,
}));
each(spritesheetSliceIndicies, (start, index) => {
const slicedSrc = src.slice(start, spritesheetSliceIndicies[index + 1]);
const cssStream = spriteData.css
.pipe(gulp.dest(CSS_DIST_PATH));
const spriteData = gulp.src(slicedSrc)
.pipe(spritesmith({
imgName: `spritesmith-${name}-${index}.png`,
cssName: `spritesmith-${name}-${index}.css`,
algorithm: 'binary-tree',
padding: 1,
cssTemplate: 'website/raw_sprites/css/css.template.handlebars',
cssVarMap,
}));
stream.add(cssStream);
const imgStream = spriteData.img
.pipe(vinylBuffer())
.pipe(imagemin())
.pipe(gulp.dest(IMG_DIST_PATH));
const cssStream = spriteData.css
.pipe(gulp.dest(CSS_DIST_PATH));
stream.add(imgStream);
stream.add(cssStream);
});
return stream;
}
gulp.task('sprites:main', () => {
const mainSrc = sync('habitica-images/**/*.png');
const mainSrc = sync('website/raw_sprites/spritesmith/**/*.png');
return createSpritesStream('main', mainSrc);
});
gulp.task('sprites:largeSprites', () => {
const largeSrc = sync('website/raw_sprites/spritesmith_large/**/*.png');
return createSpritesStream('largeSprites', largeSrc);
});
gulp.task('sprites:clean', done => {
clean(`${IMG_DIST_PATH}spritesmith*,${CSS_DIST_PATH}spritesmith*}`, done);
});
gulp.task('sprites:compile', gulp.series('sprites:clean', 'sprites:main', done => done()));
gulp.task('sprites:checkCompiledDimensions', gulp.series('sprites:main', 'sprites:largeSprites', done => {
console.log('Verifiying that images do not exceed max dimensions'); // eslint-disable-line no-console
let numberOfSheetsThatAreTooBig = 0;
const distSpritesheets = sync(`${IMG_DIST_PATH}*.png`);
each(distSpritesheets, img => {
const spriteSize = calculateImgDimensions(img);
if (spriteSize > MAX_SPRITESHEET_SIZE) {
numberOfSheetsThatAreTooBig += 1;
const name = basename(img, '.png');
console.error(`WARNING: ${name} might be too big - ${spriteSize} > ${MAX_SPRITESHEET_SIZE}`); // eslint-disable-line no-console
}
});
if (numberOfSheetsThatAreTooBig > 0) {
// https://github.com/HabitRPG/habitica/pull/6683#issuecomment-185462180
console.error( // eslint-disable-line no-console
`${numberOfSheetsThatAreTooBig} sheets might too big for mobile Safari to be able to handle
them, but there is a margin of error in these calculations so it is probably okay. Mention
this to an admin so they can test a staging site on mobile Safari after your PR is merged.`,
);
} else {
console.log('All images are within the correct dimensions'); // eslint-disable-line no-console
}
done();
}));
gulp.task('sprites:compile', gulp.series('sprites:clean', 'sprites:checkCompiledDimensions', done => done()));

View File

@@ -3,7 +3,9 @@ import { exec } from 'child_process';
import gulp from 'gulp';
import os from 'os';
import nconf from 'nconf';
import { pipe } from './taskHelper';
import {
pipe,
} from './taskHelper';
import {
getDevelopmentConnectionUrl,
getDefaultConnectionOptions,
@@ -19,16 +21,15 @@ const TEST_DB_URI = nconf.get('TEST_DB_URI');
const SANITY_TEST_COMMAND = 'npm run test:sanity';
const COMMON_TEST_COMMAND = 'npm run test:common';
const CONTENT_TEST_COMMAND = 'npm run test:content';
const LIMIT_MAX_BUFFER_OPTIONS = { maxBuffer: 1024 * 500 };
const CONTENT_OPTIONS = { maxBuffer: 1024 * 500 };
/* Helper method for reporting test summary */
/* Helper methods for reporting test summary */
const testResults = [];
const testCount = (stdout, regexp) => {
const match = stdout.match(regexp);
return parseInt(match && (match[1] || 0), 10);
};
/* Helper methods to correctly run child test processes */
const testBin = (string, additionalEnvVariables = '') => {
if (os.platform() === 'win32') {
if (additionalEnvVariables !== '') {
@@ -40,15 +41,6 @@ const testBin = (string, additionalEnvVariables = '') => {
return `NODE_ENV=test ${additionalEnvVariables} ${string}`;
};
function runInChildProcess (command, options = {}, envVariables = '') {
return done => pipe(exec(testBin(command, envVariables), options, done));
}
function integrationTestCommand (testDir, coverageDir) {
return `istanbul cover --dir coverage/${coverageDir} --report lcovonly node_modules/mocha/bin/_mocha -- ${testDir} --recursive --require ./test/helpers/start-server`;
}
/* Test task definitions */
gulp.task('test:nodemon', gulp.series(done => {
process.env.PORT = TEST_SERVER_PORT; // eslint-disable-line no-process-env
process.env.NODE_DB_URI = TEST_DB_URI; // eslint-disable-line no-process-env
@@ -59,15 +51,13 @@ gulp.task('test:prepare:mongo', cb => {
const mongooseOptions = getDefaultConnectionOptions();
const connectionUrl = getDevelopmentConnectionUrl(TEST_DB_URI);
mongoose.connect(connectionUrl, mongooseOptions)
.then(() => mongoose.connection.dropDatabase())
.then(() => mongoose.connection.close()).then(() => {
cb();
})
.catch(err => {
if (err) return cb(`Unable to connect to mongo database. Are you sure it's running? \n\n${err}`);
throw err;
mongoose.connect(connectionUrl, mongooseOptions, err => {
if (err) return cb(`Unable to connect to mongo database. Are you sure it's running? \n\n${err}`);
return mongoose.connection.dropDatabase(err2 => {
if (err2) return cb(err2);
return mongoose.connection.close(cb);
});
});
});
gulp.task('test:prepare:server', gulp.series('test:prepare:mongo', done => {
@@ -92,9 +82,31 @@ gulp.task('test:prepare', gulp.series(
done => done(),
));
gulp.task('test:sanity', runInChildProcess(SANITY_TEST_COMMAND));
gulp.task('test:sanity', cb => {
const runner = exec(
testBin(SANITY_TEST_COMMAND),
err => {
if (err) {
process.exit(1);
}
cb();
},
);
pipe(runner);
});
gulp.task('test:common', gulp.series('test:prepare:build', runInChildProcess(COMMON_TEST_COMMAND)));
gulp.task('test:common', gulp.series('test:prepare:build', cb => {
const runner = exec(
testBin(COMMON_TEST_COMMAND),
err => {
if (err) {
process.exit(1);
}
cb();
},
);
pipe(runner);
}));
gulp.task('test:common:clean', cb => {
pipe(exec(testBin(COMMON_TEST_COMMAND), () => cb()));
@@ -118,13 +130,22 @@ gulp.task('test:common:safe', gulp.series('test:prepare:build', cb => {
pipe(runner);
}));
gulp.task('test:content', gulp.series(
'test:prepare:build',
runInChildProcess(CONTENT_TEST_COMMAND, LIMIT_MAX_BUFFER_OPTIONS),
));
gulp.task('test:content', gulp.series('test:prepare:build', cb => {
const runner = exec(
testBin(CONTENT_TEST_COMMAND),
CONTENT_OPTIONS,
err => {
if (err) {
process.exit(1);
}
cb();
},
);
pipe(runner);
}));
gulp.task('test:content:clean', cb => {
pipe(exec(testBin(CONTENT_TEST_COMMAND), LIMIT_MAX_BUFFER_OPTIONS, () => cb()));
pipe(exec(testBin(CONTENT_TEST_COMMAND), CONTENT_OPTIONS, () => cb()));
});
gulp.task('test:content:watch', gulp.series('test:content:clean', () => gulp.watch(['common/script/content/**', 'test/**'], gulp.series('test:content:clean', done => done()))));
@@ -132,7 +153,7 @@ gulp.task('test:content:watch', gulp.series('test:content:clean', () => gulp.wat
gulp.task('test:content:safe', gulp.series('test:prepare:build', cb => {
const runner = exec(
testBin(CONTENT_TEST_COMMAND),
LIMIT_MAX_BUFFER_OPTIONS,
CONTENT_OPTIONS,
(err, stdout) => { // eslint-disable-line handle-callback-err
testResults.push({
suite: 'Content Specs\t',
@@ -146,45 +167,76 @@ gulp.task('test:content:safe', gulp.series('test:prepare:build', cb => {
pipe(runner);
}));
gulp.task(
'test:api:unit:run',
runInChildProcess(integrationTestCommand('test/api/unit', 'coverage/api-unit')),
);
gulp.task('test:api:unit:run', done => {
const runner = exec(
testBin('istanbul cover --dir coverage/api-unit node_modules/mocha/bin/_mocha -- test/api/unit --recursive --require ./test/helpers/start-server'),
err => {
if (err) {
process.exit(1);
}
done();
},
);
pipe(runner);
});
gulp.task('test:api:unit:watch', () => gulp.watch(['website/server/libs/*', 'test/api/unit/**/*', 'website/server/controllers/**/*'], gulp.series('test:api:unit:run', done => done())));
gulp.task('test:api-v3:integration', gulp.series(
'test:prepare:mongo',
runInChildProcess(
integrationTestCommand('test/api/v3/integration', 'coverage/api-v3-integration'),
LIMIT_MAX_BUFFER_OPTIONS,
),
));
gulp.task('test:api-v3:integration', gulp.series('test:prepare:mongo', done => {
const runner = exec(
testBin('istanbul cover --dir coverage/api-v3-integration --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v3/integration --recursive --require ./test/helpers/start-server'),
{ maxBuffer: 500 * 1024 },
err => {
if (err) {
process.exit(1);
}
done();
},
);
pipe(runner);
}));
gulp.task('test:api-v3:integration:watch', () => gulp.watch([
'website/server/controllers/api-v3/**/*', 'common/script/ops/*', 'website/server/libs/*.js',
'test/api/v3/integration/**/*',
], gulp.series('test:api-v3:integration', done => done())));
gulp.task('test:api-v3:integration:separate-server', runInChildProcess(
'mocha test/api/v3/integration --recursive --require ./test/helpers/start-server',
LIMIT_MAX_BUFFER_OPTIONS,
'LOAD_SERVER=0',
));
gulp.task('test:api-v3:integration:separate-server', done => {
const runner = exec(
testBin('mocha test/api/v3/integration --recursive --require ./test/helpers/start-server', 'LOAD_SERVER=0'),
{ maxBuffer: 500 * 1024 },
err => done(err),
);
gulp.task('test:api-v4:integration', gulp.series(
'test:prepare:mongo',
runInChildProcess(
integrationTestCommand('test/api/v4', 'api-v4-integration'),
LIMIT_MAX_BUFFER_OPTIONS,
),
));
pipe(runner);
});
gulp.task('test:api-v4:integration:separate-server', runInChildProcess(
'mocha test/api/v4 --recursive --require ./test/helpers/start-server',
LIMIT_MAX_BUFFER_OPTIONS,
'LOAD_SERVER=0',
));
gulp.task('test:api-v4:integration', gulp.series('test:prepare:mongo', done => {
const runner = exec(
testBin('istanbul cover --dir coverage/api-v4-integration --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v4 --recursive --require ./test/helpers/start-server'),
{ maxBuffer: 500 * 1024 },
err => {
if (err) {
process.exit(1);
}
done();
},
);
pipe(runner);
}));
gulp.task('test:api-v4:integration:separate-server', done => {
const runner = exec(
testBin('mocha test/api/v4 --recursive --require ./test/helpers/start-server', 'LOAD_SERVER=0'),
{ maxBuffer: 500 * 1024 },
err => done(err),
);
pipe(runner);
});
gulp.task('test:api:unit', gulp.series(
'test:prepare:mongo',

Submodule habitica-images deleted from e2ce955c24

View File

@@ -3,6 +3,6 @@ module.exports = {
root: false,
rules: {
'no-console': 0,
'no-use-before-define': ['error', { functions: false }],
},
};
'no-use-before-define': ['error', { functions: false }]
}
}

View File

@@ -2,7 +2,7 @@
// TODO it might be better we just find() and save() all user objects using mongoose, and rely on our defined pre('save')
// and default values to "migrate" users. This way we can make sure those parts are working properly too
// @see https://stackoverflow.com/questions/14867697/mongoose-full-collection-scan
// @see http://stackoverflow.com/questions/14867697/mongoose-full-collection-scan
// Also, what do we think of a Mongoose Migration module? something like https://github.com/madhums/mongoose-migrate
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.

View File

@@ -19,7 +19,7 @@ const Timer = require('./utils/timer');
const connectToDb = require('./utils/connect').connectToDb;
const closeDb = require('./utils/connect').closeDb;
const message = '`This party\'s collection quest has been made easier! For details, refer to https://habitica.fandom.com/wiki/User_blog:LadyAlys/Collection_Quests_are_Now_Easier`';
const message = '`This party\'s collection quest has been made easier! For details, refer to http://habitica.fandom.com/wiki/User_blog:LadyAlys/Collection_Quests_are_Now_Easier`';
const timer = new Timer();

View File

@@ -0,0 +1,82 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20200218_pet_color_achievements';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {
migration: MIGRATION_NAME,
};
if (user && user.items && user.items.pets) {
const pets = user.items.pets;
if (pets['Wolf-CottonCandyPink'] > 0
&& pets['TigerCub-CottonCandyPink'] > 0
&& pets['PandaCub-CottonCandyPink'] > 0
&& pets['LionCub-CottonCandyPink'] > 0
&& pets['Fox-CottonCandyPink'] > 0
&& pets['FlyingPig-CottonCandyPink'] > 0
&& pets['Dragon-CottonCandyPink'] > 0
&& pets['Cactus-CottonCandyPink'] > 0
&& pets['BearCub-CottonCandyPink'] > 0) {
set['achievements.tickledPink'] = true;
}
}
if (user && user.items && user.items.mounts) {
const mounts = user.items.mounts;
if (mounts['Wolf-CottonCandyPink']
&& mounts['TigerCub-CottonCandyPink']
&& mounts['PandaCub-CottonCandyPink']
&& mounts['LionCub-CottonCandyPink']
&& mounts['Fox-CottonCandyPink']
&& mounts['FlyingPig-CottonCandyPink']
&& mounts['Dragon-CottonCandyPink']
&& mounts['Cactus-CottonCandyPink']
&& mounts['BearCub-CottonCandyPink'] ) {
set['achievements.rosyOutlook'] = true;
}
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({ _id: user._id }, { $set: set }).exec();
}
module.exports = async function processUsers () {
let query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2020-02-01') },
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1]._id,
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,82 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20200818_pet_color_achievements';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {
migration: MIGRATION_NAME,
};
if (user && user.items && user.items.pets) {
const pets = user.items.pets;
if (pets['Wolf-Golden'] > 0
&& pets['TigerCub-Golden'] > 0
&& pets['PandaCub-Golden'] > 0
&& pets['LionCub-Golden'] > 0
&& pets['Fox-Golden'] > 0
&& pets['FlyingPig-Golden'] > 0
&& pets['Dragon-Golden'] > 0
&& pets['Cactus-Golden'] > 0
&& pets['BearCub-Golden'] > 0) {
set['achievements.goodAsGold'] = true;
}
}
if (user && user.items && user.items.mounts) {
const mounts = user.items.mounts;
if (mounts['Wolf-Golden']
&& mounts['TigerCub-Golden']
&& mounts['PandaCub-Golden']
&& mounts['LionCub-Golden']
&& mounts['Fox-Golden']
&& mounts['FlyingPig-Golden']
&& mounts['Dragon-Golden']
&& mounts['Cactus-Golden']
&& mounts['BearCub-Golden'] ) {
set['achievements.allThatGlitters'] = true;
}
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({ _id: user._id }, { $set: set }).exec();
}
module.exports = async function processUsers () {
let query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2020-08-01') },
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1]._id,
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,82 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20201020_pet_color_achievements';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {
migration: MIGRATION_NAME,
};
if (user && user.items && user.items.pets) {
const pets = user.items.pets;
if (pets['Wolf-Golden'] > 0
&& pets['TigerCub-Skeleton'] > 0
&& pets['PandaCub-Skeleton'] > 0
&& pets['LionCub-Skeleton'] > 0
&& pets['Fox-Skeleton'] > 0
&& pets['FlyingPig-Skeleton'] > 0
&& pets['Dragon-Skeleton'] > 0
&& pets['Cactus-Skeleton'] > 0
&& pets['BearCub-Skeleton'] > 0) {
set['achievements.boneCollector'] = true;
}
}
if (user && user.items && user.items.mounts) {
const mounts = user.items.mounts;
if (mounts['Wolf-Skeleton']
&& mounts['TigerCub-Skeleton']
&& mounts['PandaCub-Skeleton']
&& mounts['LionCub-Skeleton']
&& mounts['Fox-Skeleton']
&& mounts['FlyingPig-Skeleton']
&& mounts['Dragon-Skeleton']
&& mounts['Cactus-Skeleton']
&& mounts['BearCub-Skeleton'] ) {
set['achievements.skeletonCrew'] = true;
}
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({ _id: user._id }, { $set: set }).exec();
}
module.exports = async function processUsers () {
let query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2020-10-01') },
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1]._id,
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,84 +0,0 @@
/*
* Award Habitoween ladder items to participants in this month's Habitoween festivities
*/
/* eslint-disable no-console */
const MIGRATION_NAME = '20201029_habitoween_ladder'; // Update when running in future years
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {};
const inc = {
'items.food.Candy_Skeleton': 1,
'items.food.Candy_Base': 1,
'items.food.Candy_CottonCandyBlue': 1,
'items.food.Candy_CottonCandyPink': 1,
'items.food.Candy_Shade': 1,
'items.food.Candy_White': 1,
'items.food.Candy_Golden': 1,
'items.food.Candy_Zombie': 1,
'items.food.Candy_Desert': 1,
'items.food.Candy_Red': 1,
};
set.migration = MIGRATION_NAME;
if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Glow']) {
set['items.pets.JackOLantern-RoyalPurple'] = 5;
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Glow']) {
set['items.mounts.JackOLantern-Glow'] = true;
} else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Ghost']) {
set['items.pets.JackOLantern-Glow'] = 5;
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Ghost']) {
set['items.mounts.JackOLantern-Ghost'] = true;
} else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Base']) {
set['items.pets.JackOLantern-Ghost'] = 5;
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Base']) {
set['items.mounts.JackOLantern-Base'] = true;
} else {
set['items.pets.JackOLantern-Base'] = 5;
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({_id: user._id}, {$inc: inc, $set: set}).exec();
}
module.exports = async function processUsers () {
let query = {
migration: {$ne: MIGRATION_NAME},
'auth.timestamps.loggedin': {$gt: new Date('2020-10-01')},
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,58 +0,0 @@
/*
* Fix JackOLantern-Base for users that signed up recently
*/
/* eslint-disable no-console */
const MIGRATION_NAME = '20201102_fix_habitoween'; // Update when running in future years
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {};
set.migration = MIGRATION_NAME;
set['items.pets.JackOLantern-Base'] = 5;
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({_id: user._id}, {$inc: inc, $set: set}).exec();
}
module.exports = async function processUsers () {
let query = {
migration: {$ne: MIGRATION_NAME},
'auth.timestamps.created': {$gt: new Date('2020-10-26')},
'items.pets.JackOLantern-Base': true,
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,62 +0,0 @@
/*
* All web users should be enrolled in the Drop Cap AB Test
*/
/* eslint-disable no-console */
const MIGRATION_NAME = '20201103_drop_cap_ab_tweaks';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {};
set.migration = MIGRATION_NAME;
const testGroup = Math.random();
// Enroll 100% of users, splitting them 50/50
const value = testGroup <= 0.50 ? 'drop-cap-notif-enabled' : 'drop-cap-notif-disabled';
set['_ABtests.dropCapNotif'] = value;
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({_id: user._id}, {$set: set}).exec();
}
module.exports = async function processUsers () {
let query = {
migration: {$ne: MIGRATION_NAME},
'auth.timestamps.loggedin': {$gt: new Date('2020-10-10')},
'_ABtests.dropCapNotif': 'drop-cap-notif-not-enrolled',
};
const fields = {
_id: 1,
_ABtests: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,62 +0,0 @@
/*
* Fix dates in the database that were stored as $type string instead of Date
*/
/* eslint-disable no-console */
const MIGRATION_NAME = '20201111_api_date';
import * as Tasks from '../../../website/server/models/task';
const progressCount = 1000;
let count = 0;
async function updateUser (todo) {
count++;
if (count % progressCount === 0) console.warn(`${count} ${todo._id}`);
const newDate = new Date(todo.date);
if (isValidDate(newDate)) return;
return await Tasks.Task.update({_id: todo._id, type: 'todo'}, {$unset: {date: ''}}).exec();
}
module.exports = async function processUsers () {
let query = {
type: 'todo',
date: {$exists: true},
updatedAt: {$gt: new Date('2020-11-23')},
};
const fields = {
_id: 1,
type: 1,
date: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await Tasks.Task // eslint-disable-line no-await-in-loop
.find(query)
.select(fields)
.limit(250)
.sort({_id: 1})
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate tasks found and modified.');
console.warn(`\n${count} tasks processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};
function isValidDate(d) {
return !isNaN(d.getTime());
}

View File

@@ -1,82 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20201124_pet_color_achievements';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {
migration: MIGRATION_NAME,
};
if (user && user.items && user.items.pets) {
const pets = user.items.pets;
if (pets['Wolf-Red'] > 0
&& pets['TigerCub-Red'] > 0
&& pets['PandaCub-Red'] > 0
&& pets['LionCub-Red'] > 0
&& pets['Fox-Red'] > 0
&& pets['FlyingPig-Red'] > 0
&& pets['Dragon-Red'] > 0
&& pets['Cactus-Red'] > 0
&& pets['BearCub-Red'] > 0) {
set['achievements.seeingRed'] = true;
}
}
if (user && user.items && user.items.mounts) {
const mounts = user.items.mounts;
if (mounts['Wolf-Red']
&& mounts['TigerCub-Red']
&& mounts['PandaCub-Red']
&& mounts['LionCub-Red']
&& mounts['Fox-Red']
&& mounts['FlyingPig-Red']
&& mounts['Dragon-Red']
&& mounts['Cactus-Red']
&& mounts['BearCub-Red'] ) {
set['achievements.redLetterDay'] = true;
}
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({ _id: user._id }, { $set: set }).exec();
}
module.exports = async function processUsers () {
let query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2020-11-01') },
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1]._id,
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,126 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20201126_harvest_feast';
import { v4 as uuid } from 'uuid';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {};
let inc;
let push;
set.migration = MIGRATION_NAME;
if (typeof user.items.gear.owned.head_special_turkeyHelmGilded !== 'undefined') {
inc = {
'items.food.Pie_Base': 1,
'items.food.Pie_CottonCandyBlue': 1,
'items.food.Pie_CottonCandyPink': 1,
'items.food.Pie_Desert': 1,
'items.food.Pie_Golden': 1,
'items.food.Pie_Red': 1,
'items.food.Pie_Shade': 1,
'items.food.Pie_Skeleton': 1,
'items.food.Pie_Zombie': 1,
'items.food.Pie_White': 1,
}
} else if (typeof user.items.gear.owned.armor_special_turkeyArmorBase !== 'undefined') {
set['items.gear.owned.head_special_turkeyHelmGilded'] = false;
set['items.gear.owned.armor_special_turkeyArmorGilded'] = false;
set['items.gear.owned.back_special_turkeyTailGilded'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_turkeyHelmGilded',
_id: uuid(),
},
{
type: 'marketGear',
path: 'gear.flat.armor_special_turkeyArmorGilded',
_id: uuid(),
},
{
type: 'marketGear',
path: 'gear.flat.back_special_turkeyTailGilded',
_id: uuid(),
},
];
} else if (user.items && user.items.mounts && user.items.mounts['Turkey-Gilded']) {
set['items.gear.owned.head_special_turkeyHelmBase'] = false;
set['items.gear.owned.armor_special_turkeyArmorBase'] = false;
set['items.gear.owned.back_special_turkeyTailBase'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_turkeyHelmBase',
_id: uuid(),
},
{
type: 'marketGear',
path: 'gear.flat.armor_special_turkeyArmorBase',
_id: uuid(),
},
{
type: 'marketGear',
path: 'gear.flat.back_special_turkeyTailBase',
_id: uuid(),
},
];
} else if (user.items && user.items.pets && user.items.pets['Turkey-Gilded']) {
set['items.mounts.Turkey-Gilded'] = true;
} else if (user.items && user.items.mounts && user.items.mounts['Turkey-Base']) {
set['items.pets.Turkey-Gilded'] = 5;
} else if (user.items && user.items.pets && user.items.pets['Turkey-Base']) {
set['items.mounts.Turkey-Base'] = true;
} else {
set['items.pets.Turkey-Base'] = 5;
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
if (inc) {
return await User.update({_id: user._id}, {$inc: inc, $set: set}).exec();
} else if (push) {
return await User.update({_id: user._id}, {$set: set, $push: {pinnedItems: {$each: push}}}).exec();
} else {
return await User.update({_id: user._id}, {$set: set}).exec();
}
}
export default async function processUsers () {
let query = {
migration: {$ne: MIGRATION_NAME},
'auth.timestamps.loggedin': {$gt: new Date('2019-11-01')},
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,126 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20201229_nye';
import { model as User } from '../../../website/server/models/user';
import { v4 as uuid } from 'uuid';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = { migration: MIGRATION_NAME };
let push;
if (typeof user.items.gear.owned.head_special_nye2019 !== 'undefined') {
set['items.gear.owned.head_special_nye2020'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2020',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye2018 !== 'undefined') {
set['items.gear.owned.head_special_nye2019'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2019',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye2017 !== 'undefined') {
set['items.gear.owned.head_special_nye2018'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2018',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye2016 !== 'undefined') {
set['items.gear.owned.head_special_nye2017'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2017',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye2015 !== 'undefined') {
set['items.gear.owned.head_special_nye2016'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2016',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye2014 !== 'undefined') {
set['items.gear.owned.head_special_nye2015'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2015',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye !== 'undefined') {
set['items.gear.owned.head_special_nye2014'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2014',
_id: uuid(),
},
];
} else {
set['items.gear.owned.head_special_nye'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye',
_id: uuid(),
},
];
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({_id: user._id}, {$set: set, $push: {pinnedItems: {$each: push}}}).exec();
}
export default async function processUsers () {
let query = {
'auth.timestamps.loggedin': {$gt: new Date('2020-12-01')},
migration: {$ne: MIGRATION_NAME},
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,73 +0,0 @@
import { model as User } from '../../website/server/models/user';
const MIGRATION_NAME = 'tag-challenge-field-string2bool';
const progressCount = 1000;
let count = 0;
export default async function processUsers () {
const query = {
migration: { $ne: MIGRATION_NAME },
tags: {
$elemMatch: {
challenge: {
$exists: true,
$type: 'string',
},
},
},
};
while (true) { // eslint-disable-line no-constant-condition
// eslint-disable-next-line no-await-in-loop
const users = await User.find(query)
.sort({ _id: 1 })
.limit(250)
.select({ _id: 1, tags: 1 })
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
}
async function updateUser (user) {
count += 1;
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
let requiresUpdate = false;
if (user && user.tags) {
user.tags.forEach(tag => {
if (tag && typeof tag.challenge === 'string') {
requiresUpdate = true;
if (tag.challenge === 'true') {
tag.challenge = true;
} else if (tag.challenge === 'false') {
tag.challenge = false;
} else {
tag.challenge = null;
}
}
});
}
if (requiresUpdate) {
const set = {
migration: MIGRATION_NAME,
tags: user.tags,
};
return User.update({ _id: user._id }, { $set: set }).exec();
}
return null;
}

View File

@@ -1,94 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20210129_habit_birthday';
import { v4 as uuid } from 'uuid';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const inc = {
'items.food.Cake_Skeleton': 1,
'items.food.Cake_Base': 1,
'items.food.Cake_CottonCandyBlue': 1,
'items.food.Cake_CottonCandyPink': 1,
'items.food.Cake_Shade': 1,
'items.food.Cake_White': 1,
'items.food.Cake_Golden': 1,
'items.food.Cake_Zombie': 1,
'items.food.Cake_Desert': 1,
'items.food.Cake_Red': 1,
'achievements.habitBirthdays': 1,
};
const set = {};
let push;
set.migration = MIGRATION_NAME;
if (typeof user.items.gear.owned.armor_special_birthday2020 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2021'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2021', _id: uuid()}};
} else if (typeof user.items.gear.owned.armor_special_birthday2019 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2020'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2020', _id: uuid()}};
} else if (typeof user.items.gear.owned.armor_special_birthday2018 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2019'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2019', _id: uuid()}};
} else if (typeof user.items.gear.owned.armor_special_birthday2017 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2018'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2018', _id: uuid()}};
} else if (typeof user.items.gear.owned.armor_special_birthday2016 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2017'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2017', _id: uuid()}};
} else if (typeof user.items.gear.owned.armor_special_birthday2015 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2016'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2016', _id: uuid()}};
} else if (typeof user.items.gear.owned.armor_special_birthday !== 'undefined') {
set['items.gear.owned.armor_special_birthday2015'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2015', _id: uuid()}};
} else {
set['items.gear.owned.armor_special_birthday'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday', _id: uuid()}};
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({_id: user._id}, {$inc: inc, $set: set, $push: push}).exec();
}
export default async function processUsers () {
let query = {
migration: {$ne: MIGRATION_NAME},
'auth.timestamps.loggedin': {$gt: new Date('2021-01-01')},
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,108 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20210216_pet_group_achievements';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {
migration: MIGRATION_NAME,
};
if (user && user.items && user.items.pets) {
const pets = user.items.pets;
if (pets['Dragon-Base']
&& pets['Dragon-CottonCandyBlue']
&& pets['Dragon-CottonCandyPink']
&& pets['Dragon-Desert']
&& pets['Dragon-Golden']
&& pets['Dragon-Red']
&& pets['Dragon-Shade']
&& pets['Dragon-Skeleton']
&& pets['Dragon-White']
&& pets['Dragon-Zombie']
&& pets['FlyingPig-Base']
&& pets['FlyingPig-CottonCandyBlue']
&& pets['FlyingPig-CottonCandyPink']
&& pets['FlyingPig-Desert']
&& pets['FlyingPig-Golden']
&& pets['FlyingPig-Red']
&& pets['FlyingPig-Shade']
&& pets['FlyingPig-Skeleton']
&& pets['FlyingPig-White']
&& pets['FlyingPig-Zombie']
&& pets['Gryphon-Base']
&& pets['Gryphon-CottonCandyBlue']
&& pets['Gryphon-CottonCandyPink']
&& pets['Gryphon-Desert']
&& pets['Gryphon-Golden']
&& pets['Gryphon-Red']
&& pets['Gryphon-Shade']
&& pets['Gryphon-Skeleton']
&& pets['Gryphon-White']
&& pets['Gryphon-Zombie']
&& pets['SeaSerpent-Base']
&& pets['SeaSerpent-CottonCandyBlue']
&& pets['SeaSerpent-CottonCandyPink']
&& pets['SeaSerpent-Desert']
&& pets['SeaSerpent-Golden']
&& pets['SeaSerpent-Red']
&& pets['SeaSerpent-Shade']
&& pets['SeaSerpent-Skeleton']
&& pets['SeaSerpent-White']
&& pets['SeaSerpent-Zombie']
&& pets['Unicorn-Base']
&& pets['Unicorn-CottonCandyBlue']
&& pets['Unicorn-CottonCandyPink']
&& pets['Unicorn-Desert']
&& pets['Unicorn-Golden']
&& pets['Unicorn-Red']
&& pets['Unicorn-Shade']
&& pets['Unicorn-Skeleton']
&& pets['Unicorn-White']
&& pets['Unicorn-Zombie']) {
set['achievements.legendaryBestiary'] = true;
}
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({ _id: user._id }, { $set: set }).exec();
}
export default async function processUsers () {
let query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2021-02-01') },
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1]._id,
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,82 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20210525_pet_color_achievements';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {
migration: MIGRATION_NAME,
};
if (user && user.items && user.items.pets) {
const pets = user.items.pets;
if (pets['Wolf-CottonCandyBlue'] > 0
&& pets['TigerCub-CottonCandyBlue'] > 0
&& pets['PandaCub-CottonCandyBlue'] > 0
&& pets['LionCub-CottonCandyBlue'] > 0
&& pets['Fox-CottonCandyBlue'] > 0
&& pets['FlyingPig-CottonCandyBlue'] > 0
&& pets['Dragon-CottonCandyBlue'] > 0
&& pets['Cactus-CottonCandyBlue'] > 0
&& pets['BearCub-CottonCandyBlue'] > 0) {
set['achievements.violetsAreBlue'] = true;
}
}
if (user && user.items && user.items.mounts) {
const mounts = user.items.mounts;
if (mounts['Wolf-CottonCandyBlue']
&& mounts['TigerCub-CottonCandyBlue']
&& mounts['PandaCub-CottonCandyBlue']
&& mounts['LionCub-CottonCandyBlue']
&& mounts['Fox-CottonCandyBlue']
&& mounts['FlyingPig-CottonCandyBlue']
&& mounts['Dragon-CottonCandyBlue']
&& mounts['Cactus-CottonCandyBlue']
&& mounts['BearCub-CottonCandyBlue'] ) {
set['achievements.wildBlueYonder'] = true;
}
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({ _id: user._id }, { $set: set }).exec();
}
module.exports = async function processUsers () {
let query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2021-05-01') },
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1]._id,
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,138 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20210824_pet_group_achievements';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {
migration: MIGRATION_NAME,
};
if (user && user.items && user.items.pets) {
const pets = user.items.pets;
if (pets['FlyingPig-Base']
&& pets['FlyingPig-CottonCandyBlue']
&& pets['FlyingPig-CottonCandyPink']
&& pets['FlyingPig-Desert']
&& pets['FlyingPig-Golden']
&& pets['FlyingPig-Red']
&& pets['FlyingPig-Shade']
&& pets['FlyingPig-Skeleton']
&& pets['FlyingPig-White']
&& pets['FlyingPig-Zombie']
&& pets['Ferret-Base']
&& pets['Ferret-CottonCandyBlue']
&& pets['Ferret-CottonCandyPink']
&& pets['Ferret-Desert']
&& pets['Ferret-Golden']
&& pets['Ferret-Red']
&& pets['Ferret-Shade']
&& pets['Ferret-Skeleton']
&& pets['Ferret-White']
&& pets['Ferret-Zombie']
&& pets['GuineaPig-Base']
&& pets['GuineaPig-CottonCandyBlue']
&& pets['GuineaPig-CottonCandyPink']
&& pets['GuineaPig-Desert']
&& pets['GuineaPig-Golden']
&& pets['GuineaPig-Red']
&& pets['GuineaPig-Shade']
&& pets['GuineaPig-Skeleton']
&& pets['GuineaPig-White']
&& pets['GuineaPig-Zombie']
&& pets['Rooster-Base']
&& pets['Rooster-CottonCandyBlue']
&& pets['Rooster-CottonCandyPink']
&& pets['Rooster-Desert']
&& pets['Rooster-Golden']
&& pets['Rooster-Red']
&& pets['Rooster-Shade']
&& pets['Rooster-Skeleton']
&& pets['Rooster-White']
&& pets['Rooster-Zombie']
&& pets['Rat-Base']
&& pets['Rat-CottonCandyBlue']
&& pets['Rat-CottonCandyPink']
&& pets['Rat-Desert']
&& pets['Rat-Golden']
&& pets['Rat-Red']
&& pets['Rat-Shade']
&& pets['Rat-Skeleton']
&& pets['Rat-White']
&& pets['Rat-Zombie']
&& pets['Bunny-Base']
&& pets['Bunny-CottonCandyBlue']
&& pets['Bunny-CottonCandyPink']
&& pets['Bunny-Desert']
&& pets['Bunny-Golden']
&& pets['Bunny-Red']
&& pets['Bunny-Shade']
&& pets['Bunny-Skeleton']
&& pets['Bunny-White']
&& pets['Bunny-Zombie']
&& pets['Horse-Base']
&& pets['Horse-CottonCandyBlue']
&& pets['Horse-CottonCandyPink']
&& pets['Horse-Desert']
&& pets['Horse-Golden']
&& pets['Horse-Red']
&& pets['Horse-Shade']
&& pets['Horse-Skeleton']
&& pets['Horse-White']
&& pets['Horse-Zombie']
&& pets['Cow-Base']
&& pets['Cow-CottonCandyBlue']
&& pets['Cow-CottonCandyPink']
&& pets['Cow-Desert']
&& pets['Cow-Golden']
&& pets['Cow-Red']
&& pets['Cow-Shade']
&& pets['Cow-Skeleton']
&& pets['Cow-White']
&& pets['Cow-Zombie']) {
set['achievements.domesticated'] = true;
}
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({ _id: user._id }, { $set: set }).exec();
}
export default async function processUsers () {
let query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2021-08-01') },
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1]._id,
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,82 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20211021_pet_color_achievements';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {
migration: MIGRATION_NAME,
};
if (user && user.items && user.items.pets) {
const pets = user.items.pets;
if (pets['Wolf-Shade'] > 0
&& pets['TigerCub-Shade'] > 0
&& pets['PandaCub-Shade'] > 0
&& pets['LionCub-Shade'] > 0
&& pets['Fox-Shade'] > 0
&& pets['FlyingPig-Shade'] > 0
&& pets['Dragon-Shade'] > 0
&& pets['Cactus-Shade'] > 0
&& pets['BearCub-Shade'] > 0) {
set['achievements.shadyCustomer'] = true;
}
}
if (user && user.items && user.items.mounts) {
const mounts = user.items.mounts;
if (mounts['Wolf-Shade']
&& mounts['TigerCub-Shade']
&& mounts['PandaCub-Shade']
&& mounts['LionCub-Shade']
&& mounts['Fox-Shade']
&& mounts['FlyingPig-Shade']
&& mounts['Dragon-Shade']
&& mounts['Cactus-Shade']
&& mounts['BearCub-Shade'] ) {
set['achievements.shadeOfItAll'] = true;
}
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({ _id: user._id }, { $set: set }).exec();
}
export default async function processUsers () {
let query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2021-10-01') },
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1]._id,
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,86 +0,0 @@
/*
* Award Habitoween ladder items to participants in this month's Habitoween festivities
*/
/* eslint-disable no-console */
const MIGRATION_NAME = '20211028_habitoween_ladder'; // Update when running in future years
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {};
const inc = {
'items.food.Candy_Skeleton': 1,
'items.food.Candy_Base': 1,
'items.food.Candy_CottonCandyBlue': 1,
'items.food.Candy_CottonCandyPink': 1,
'items.food.Candy_Shade': 1,
'items.food.Candy_White': 1,
'items.food.Candy_Golden': 1,
'items.food.Candy_Zombie': 1,
'items.food.Candy_Desert': 1,
'items.food.Candy_Red': 1,
};
set.migration = MIGRATION_NAME;
if (user && user.items && user.items.pets && user.items.pets['JackOLantern-RoyalPurple']) {
set['items.mounts.JackOLantern-RoyalPurple'] = true;
} else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Glow']) {
set['items.pets.JackOLantern-RoyalPurple'] = 5;
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Glow']) {
set['items.mounts.JackOLantern-Glow'] = true;
} else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Ghost']) {
set['items.pets.JackOLantern-Glow'] = 5;
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Ghost']) {
set['items.mounts.JackOLantern-Ghost'] = true;
} else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Base']) {
set['items.pets.JackOLantern-Ghost'] = 5;
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Base']) {
set['items.mounts.JackOLantern-Base'] = true;
} else {
set['items.pets.JackOLantern-Base'] = 5;
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({_id: user._id}, {$inc: inc, $set: set}).exec();
}
export default async function processUsers () {
let query = {
migration: {$ne: MIGRATION_NAME},
'auth.timestamps.loggedin': {$gt: new Date('2021-10-01')},
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,126 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20211124_harvest_feast';
import { v4 as uuid } from 'uuid';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {};
let inc;
let push;
set.migration = MIGRATION_NAME;
if (typeof user.items.gear.owned.head_special_turkeyHelmGilded !== 'undefined') {
inc = {
'items.food.Pie_Base': 1,
'items.food.Pie_CottonCandyBlue': 1,
'items.food.Pie_CottonCandyPink': 1,
'items.food.Pie_Desert': 1,
'items.food.Pie_Golden': 1,
'items.food.Pie_Red': 1,
'items.food.Pie_Shade': 1,
'items.food.Pie_Skeleton': 1,
'items.food.Pie_Zombie': 1,
'items.food.Pie_White': 1,
}
} else if (typeof user.items.gear.owned.armor_special_turkeyArmorBase !== 'undefined') {
set['items.gear.owned.head_special_turkeyHelmGilded'] = false;
set['items.gear.owned.armor_special_turkeyArmorGilded'] = false;
set['items.gear.owned.back_special_turkeyTailGilded'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_turkeyHelmGilded',
_id: uuid(),
},
{
type: 'marketGear',
path: 'gear.flat.armor_special_turkeyArmorGilded',
_id: uuid(),
},
{
type: 'marketGear',
path: 'gear.flat.back_special_turkeyTailGilded',
_id: uuid(),
},
];
} else if (user.items && user.items.mounts && user.items.mounts['Turkey-Gilded']) {
set['items.gear.owned.head_special_turkeyHelmBase'] = false;
set['items.gear.owned.armor_special_turkeyArmorBase'] = false;
set['items.gear.owned.back_special_turkeyTailBase'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_turkeyHelmBase',
_id: uuid(),
},
{
type: 'marketGear',
path: 'gear.flat.armor_special_turkeyArmorBase',
_id: uuid(),
},
{
type: 'marketGear',
path: 'gear.flat.back_special_turkeyTailBase',
_id: uuid(),
},
];
} else if (user.items && user.items.pets && user.items.pets['Turkey-Gilded']) {
set['items.mounts.Turkey-Gilded'] = true;
} else if (user.items && user.items.mounts && user.items.mounts['Turkey-Base']) {
set['items.pets.Turkey-Gilded'] = 5;
} else if (user.items && user.items.pets && user.items.pets['Turkey-Base']) {
set['items.mounts.Turkey-Base'] = true;
} else {
set['items.pets.Turkey-Base'] = 5;
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
if (inc) {
return await User.update({_id: user._id}, {$inc: inc, $set: set}).exec();
} else if (push) {
return await User.update({_id: user._id}, {$set: set, $push: {pinnedItems: {$each: push}}}).exec();
} else {
return await User.update({_id: user._id}, {$set: set}).exec();
}
}
export default async function processUsers () {
let query = {
migration: {$ne: MIGRATION_NAME},
'auth.timestamps.loggedin': {$gt: new Date('2021-11-01')},
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,135 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20211230_nye';
import { model as User } from '../../../website/server/models/user';
import { v4 as uuid } from 'uuid';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = { migration: MIGRATION_NAME };
let push;
if (typeof user.items.gear.owned.head_special_nye2020 !== 'undefined') {
set['items.gear.owned.head_special_nye2021'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2021',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye2019 !== 'undefined') {
set['items.gear.owned.head_special_nye2020'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2020',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye2018 !== 'undefined') {
set['items.gear.owned.head_special_nye2019'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2019',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye2017 !== 'undefined') {
set['items.gear.owned.head_special_nye2018'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2018',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye2016 !== 'undefined') {
set['items.gear.owned.head_special_nye2017'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2017',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye2015 !== 'undefined') {
set['items.gear.owned.head_special_nye2016'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2016',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye2014 !== 'undefined') {
set['items.gear.owned.head_special_nye2015'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2015',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye !== 'undefined') {
set['items.gear.owned.head_special_nye2014'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2014',
_id: uuid(),
},
];
} else {
set['items.gear.owned.head_special_nye'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye',
_id: uuid(),
},
];
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({_id: user._id}, {$set: set, $push: {pinnedItems: {$each: push}}}).exec();
}
export default async function processUsers () {
let query = {
'auth.timestamps.loggedin': {$gt: new Date('2021-12-01')},
migration: {$ne: MIGRATION_NAME},
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,97 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20220131_habit_birthday';
import { v4 as uuid } from 'uuid';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const inc = {
'items.food.Cake_Skeleton': 1,
'items.food.Cake_Base': 1,
'items.food.Cake_CottonCandyBlue': 1,
'items.food.Cake_CottonCandyPink': 1,
'items.food.Cake_Shade': 1,
'items.food.Cake_White': 1,
'items.food.Cake_Golden': 1,
'items.food.Cake_Zombie': 1,
'items.food.Cake_Desert': 1,
'items.food.Cake_Red': 1,
'achievements.habitBirthdays': 1,
};
const set = {};
let push;
set.migration = MIGRATION_NAME;
if (typeof user.items.gear.owned.armor_special_birthday2021 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2022'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2022', _id: uuid()}};
} else if (typeof user.items.gear.owned.armor_special_birthday2020 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2021'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2021', _id: uuid()}};
} else if (typeof user.items.gear.owned.armor_special_birthday2019 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2020'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2020', _id: uuid()}};
} else if (typeof user.items.gear.owned.armor_special_birthday2018 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2019'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2019', _id: uuid()}};
} else if (typeof user.items.gear.owned.armor_special_birthday2017 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2018'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2018', _id: uuid()}};
} else if (typeof user.items.gear.owned.armor_special_birthday2016 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2017'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2017', _id: uuid()}};
} else if (typeof user.items.gear.owned.armor_special_birthday2015 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2016'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2016', _id: uuid()}};
} else if (typeof user.items.gear.owned.armor_special_birthday !== 'undefined') {
set['items.gear.owned.armor_special_birthday2015'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2015', _id: uuid()}};
} else {
set['items.gear.owned.armor_special_birthday'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday', _id: uuid()}};
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({_id: user._id}, {$inc: inc, $set: set, $push: push}).exec();
}
export default async function processUsers () {
let query = {
migration: {$ne: MIGRATION_NAME},
'auth.timestamps.loggedin': {$gt: new Date('2022-01-01')},
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,178 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20220201_pet_group_achievements';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {
migration: MIGRATION_NAME,
};
if (user && user.items && user.items.pets) {
const pets = user.items.pets;
if (pets['FlyingPig-Base']
&& pets['FlyingPig-CottonCandyBlue']
&& pets['FlyingPig-CottonCandyPink']
&& pets['FlyingPig-Desert']
&& pets['FlyingPig-Golden']
&& pets['FlyingPig-Red']
&& pets['FlyingPig-Shade']
&& pets['FlyingPig-Skeleton']
&& pets['FlyingPig-White']
&& pets['FlyingPig-Zombie']
&& pets['Snake-Base']
&& pets['Snake-CottonCandyBlue']
&& pets['Snake-CottonCandyPink']
&& pets['Snake-Desert']
&& pets['Snake-Golden']
&& pets['Snake-Red']
&& pets['Snake-Shade']
&& pets['Snake-Skeleton']
&& pets['Snake-White']
&& pets['Snake-Zombie']
&& pets['Sheep-Base']
&& pets['Sheep-CottonCandyBlue']
&& pets['Sheep-CottonCandyPink']
&& pets['Sheep-Desert']
&& pets['Sheep-Golden']
&& pets['Sheep-Red']
&& pets['Sheep-Shade']
&& pets['Sheep-Skeleton']
&& pets['Sheep-White']
&& pets['Sheep-Zombie']
&& pets['Rooster-Base']
&& pets['Rooster-CottonCandyBlue']
&& pets['Rooster-CottonCandyPink']
&& pets['Rooster-Desert']
&& pets['Rooster-Golden']
&& pets['Rooster-Red']
&& pets['Rooster-Shade']
&& pets['Rooster-Skeleton']
&& pets['Rooster-White']
&& pets['Rooster-Zombie']
&& pets['Rat-Base']
&& pets['Rat-CottonCandyBlue']
&& pets['Rat-CottonCandyPink']
&& pets['Rat-Desert']
&& pets['Rat-Golden']
&& pets['Rat-Red']
&& pets['Rat-Shade']
&& pets['Rat-Skeleton']
&& pets['Rat-White']
&& pets['Rat-Zombie']
&& pets['Bunny-Base']
&& pets['Bunny-CottonCandyBlue']
&& pets['Bunny-CottonCandyPink']
&& pets['Bunny-Desert']
&& pets['Bunny-Golden']
&& pets['Bunny-Red']
&& pets['Bunny-Shade']
&& pets['Bunny-Skeleton']
&& pets['Bunny-White']
&& pets['Bunny-Zombie']
&& pets['Horse-Base']
&& pets['Horse-CottonCandyBlue']
&& pets['Horse-CottonCandyPink']
&& pets['Horse-Desert']
&& pets['Horse-Golden']
&& pets['Horse-Red']
&& pets['Horse-Shade']
&& pets['Horse-Skeleton']
&& pets['Horse-White']
&& pets['Horse-Zombie']
&& pets['Cow-Base']
&& pets['Cow-CottonCandyBlue']
&& pets['Cow-CottonCandyPink']
&& pets['Cow-Desert']
&& pets['Cow-Golden']
&& pets['Cow-Red']
&& pets['Cow-Shade']
&& pets['Cow-Skeleton']
&& pets['Cow-White']
&& pets['Cow-Zombie']
&& pets['Monkey-Base']
&& pets['Monkey-CottonCandyBlue']
&& pets['Monkey-CottonCandyPink']
&& pets['Monkey-Desert']
&& pets['Monkey-Golden']
&& pets['Monkey-Red']
&& pets['Monkey-Shade']
&& pets['Monkey-Skeleton']
&& pets['Monkey-White']
&& pets['Monkey-Zombie']
&& pets['Wolf-Base']
&& pets['Wolf-CottonCandyBlue']
&& pets['Wolf-CottonCandyPink']
&& pets['Wolf-Desert']
&& pets['Wolf-Golden']
&& pets['Wolf-Red']
&& pets['Wolf-Shade']
&& pets['Wolf-Skeleton']
&& pets['Wolf-White']
&& pets['Wolf-Zombie']
&& pets['Tiger-Base']
&& pets['Tiger-CottonCandyBlue']
&& pets['Tiger-CottonCandyPink']
&& pets['Tiger-Desert']
&& pets['Tiger-Golden']
&& pets['Tiger-Red']
&& pets['Tiger-Shade']
&& pets['Tiger-Skeleton']
&& pets['Tiger-White']
&& pets['Tiger-Zombie']
&& pets['Dragon-Base']
&& pets['Dragon-CottonCandyBlue']
&& pets['Dragon-CottonCandyPink']
&& pets['Dragon-Desert']
&& pets['Dragon-Golden']
&& pets['Dragon-Red']
&& pets['Dragon-Shade']
&& pets['Dragon-Skeleton']
&& pets['Dragon-White']
&& pets['Dragon-Zombie']) {
set['achievements.zodiacZookeeper'] = true;
}
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({ _id: user._id }, { $set: set }).exec();
}
export default async function processUsers () {
let query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2021-08-01') },
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1]._id,
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,138 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20220309_pet_group_achievements';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {
migration: MIGRATION_NAME,
};
if (user && user.items && user.items.pets) {
const pets = user.items.pets;
if (pets['FlyingPig-Base']
&& pets['FlyingPig-CottonCandyBlue']
&& pets['FlyingPig-CottonCandyPink']
&& pets['FlyingPig-Desert']
&& pets['FlyingPig-Golden']
&& pets['FlyingPig-Red']
&& pets['FlyingPig-Shade']
&& pets['FlyingPig-Skeleton']
&& pets['FlyingPig-White']
&& pets['FlyingPig-Zombie']
&& pets['Owl-Base']
&& pets['Owl-CottonCandyBlue']
&& pets['Owl-CottonCandyPink']
&& pets['Owl-Desert']
&& pets['Owl-Golden']
&& pets['Owl-Red']
&& pets['Owl-Shade']
&& pets['Owl-Skeleton']
&& pets['Owl-White']
&& pets['Owl-Zombie']
&& pets['Parrot-Base']
&& pets['Parrot-CottonCandyBlue']
&& pets['Parrot-CottonCandyPink']
&& pets['Parrot-Desert']
&& pets['Parrot-Golden']
&& pets['Parrot-Red']
&& pets['Parrot-Shade']
&& pets['Parrot-Skeleton']
&& pets['Parrot-White']
&& pets['Parrot-Zombie']
&& pets['Rooster-Base']
&& pets['Rooster-CottonCandyBlue']
&& pets['Rooster-CottonCandyPink']
&& pets['Rooster-Desert']
&& pets['Rooster-Golden']
&& pets['Rooster-Red']
&& pets['Rooster-Shade']
&& pets['Rooster-Skeleton']
&& pets['Rooster-White']
&& pets['Rooster-Zombie']
&& pets['Pterodactyl-Base']
&& pets['Pterodactyl-CottonCandyBlue']
&& pets['Pterodactyl-CottonCandyPink']
&& pets['Pterodactyl-Desert']
&& pets['Pterodactyl-Golden']
&& pets['Pterodactyl-Red']
&& pets['Pterodactyl-Shade']
&& pets['Pterodactyl-Skeleton']
&& pets['Pterodactyl-White']
&& pets['Pterodactyl-Zombie']
&& pets['Gryphon-Base']
&& pets['Gryphon-CottonCandyBlue']
&& pets['Gryphon-CottonCandyPink']
&& pets['Gryphon-Desert']
&& pets['Gryphon-Golden']
&& pets['Gryphon-Red']
&& pets['Gryphon-Shade']
&& pets['Gryphon-Skeleton']
&& pets['Gryphon-White']
&& pets['Gryphon-Zombie']
&& pets['Falcon-Base']
&& pets['Falcon-CottonCandyBlue']
&& pets['Falcon-CottonCandyPink']
&& pets['Falcon-Desert']
&& pets['Falcon-Golden']
&& pets['Falcon-Red']
&& pets['Falcon-Shade']
&& pets['Falcon-Skeleton']
&& pets['Falcon-White']
&& pets['Falcon-Zombie']
&& pets['Peacock-Base']
&& pets['Peacock-CottonCandyBlue']
&& pets['Peacock-CottonCandyPink']
&& pets['Peacock-Desert']
&& pets['Peacock-Golden']
&& pets['Peacock-Red']
&& pets['Peacock-Shade']
&& pets['Peacock-Skeleton']
&& pets['Peacock-White']
&& pets['Peacock-Zombie']) {
set['achievements.birdsOfAFeather'] = true;
}
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({ _id: user._id }, { $set: set }).exec();
}
export default async function processUsers () {
let query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2021-08-01') },
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1]._id,
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,128 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20220524_pet_group_achievements';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {
migration: MIGRATION_NAME,
};
if (user && user.items && user.items.pets) {
const pets = user.items.pets;
if (pets['Alligator-Base']
&& pets['Alligator-CottonCandyBlue']
&& pets['Alligator-CottonCandyPink']
&& pets['Alligator-Desert']
&& pets['Alligator-Golden']
&& pets['Alligator-Red']
&& pets['Alligator-Shade']
&& pets['Alligator-Skeleton']
&& pets['Alligator-White']
&& pets['Alligator-Zombie']
&& pets['Snake-Base']
&& pets['Snake-CottonCandyBlue']
&& pets['Snake-CottonCandyPink']
&& pets['Snake-Desert']
&& pets['Snake-Golden']
&& pets['Snake-Red']
&& pets['Snake-Shade']
&& pets['Snake-Skeleton']
&& pets['Snake-White']
&& pets['Snake-Zombie']
&& pets['Triceratops-Base']
&& pets['Triceratops-CottonCandyBlue']
&& pets['Triceratops-CottonCandyPink']
&& pets['Triceratops-Desert']
&& pets['Triceratops-Golden']
&& pets['Triceratops-Red']
&& pets['Triceratops-Shade']
&& pets['Triceratops-Skeleton']
&& pets['Triceratops-White']
&& pets['Triceratops-Zombie']
&& pets['TRex-Base']
&& pets['TRex-CottonCandyBlue']
&& pets['TRex-CottonCandyPink']
&& pets['TRex-Desert']
&& pets['TRex-Golden']
&& pets['TRex-Red']
&& pets['TRex-Shade']
&& pets['TRex-Skeleton']
&& pets['TRex-White']
&& pets['TRex-Zombie']
&& pets['Pterodactyl-Base']
&& pets['Pterodactyl-CottonCandyBlue']
&& pets['Pterodactyl-CottonCandyPink']
&& pets['Pterodactyl-Desert']
&& pets['Pterodactyl-Golden']
&& pets['Pterodactyl-Red']
&& pets['Pterodactyl-Shade']
&& pets['Pterodactyl-Skeleton']
&& pets['Pterodactyl-White']
&& pets['Pterodactyl-Zombie']
&& pets['Turtle-Base']
&& pets['Turtle-CottonCandyBlue']
&& pets['Turtle-CottonCandyPink']
&& pets['Turtle-Desert']
&& pets['Turtle-Golden']
&& pets['Turtle-Red']
&& pets['Turtle-Shade']
&& pets['Turtle-Skeleton']
&& pets['Turtle-White']
&& pets['Turtle-Zombie']
&& pets['Velociraptor-Base']
&& pets['Velociraptor-CottonCandyBlue']
&& pets['Velociraptor-CottonCandyPink']
&& pets['Velociraptor-Desert']
&& pets['Velociraptor-Golden']
&& pets['Velociraptor-Red']
&& pets['Velociraptor-Shade']
&& pets['Velociraptor-Skeleton']
&& pets['Velociraptor-White']
&& pets['Velociraptor-Zombie']) {
set['achievements.reptacularRumble'] = true;
}
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({ _id: user._id }, { $set: set }).exec();
}
export default async function processUsers () {
let query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2022-01-01') },
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1]._id,
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,97 +0,0 @@
/* eslint-disable no-console */
import { model as UserModel } from '../../../website/server/models/user';
import { TransactionModel } from '../../../website/server/models/transaction';
const MIGRATION_NAME = '20220915_transactions_user_name';
/* transaction config */
const transactionPerRun = 500;
const progressCount = 1000;
const transactionQuery = {
migration: { $ne: MIGRATION_NAME }, // skip already migrated entries
'transactionType': { $in: ['gift_send', 'gift_receive'] },
};
let count = 0;
async function updateTransaction (transaction, userNameMap) {
count++;
const set = {
migration: MIGRATION_NAME,
};
if (userNameMap.has(transaction.reference)) {
set['referenceText'] = userNameMap.get(transaction.reference);
} else {
set['referenceText'] = 'Account not found';
}
if (count % progressCount === 0) {
console.warn(`${count} ${transaction._id}`);
}
return TransactionModel.updateOne({
_id: transaction._id
}, { $set: set }).exec();
}
export default async function processTransactions () {
const fields = {
_id: 1,
reference: 1,
referenceText: 1,
};
const userNameMap = new Map();
while (true) { // eslint-disable-line no-constant-condition
const foundTransactions = await TransactionModel // eslint-disable-line no-await-in-loop
.find(transactionQuery)
.limit(transactionPerRun)
.sort({reference: 1})
.select(fields)
.lean()
.exec();
if (foundTransactions.length === 0) {
console.warn('All appropriate transactions found and modified.');
console.warn(`\n${count} transactions processed\n`);
break;
}
// check for unknown users and load the names
const userIdsToLoad = [];
for (const foundTransaction of foundTransactions) {
const userId = foundTransaction.reference;
if (userNameMap.has(userId)) {
continue;
}
userIdsToLoad.push(userId);
}
const users = await UserModel // eslint-disable-line no-await-in-loop
.find({
_id: { $in: userIdsToLoad }
})
.select({
_id: 1,
'auth.local.username': 1,
})
.lean()
.exec();
for (const user of users) {
const localUserName = user.auth?.local?.username;
if (!localUserName) {
console.warn(`\nNo Username found for ID: ${user._id}\n`);
continue;
}
userNameMap.set(user._id, localUserName)
}
await Promise.all(foundTransactions.map(t => updateTransaction(t, userNameMap))); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,86 +0,0 @@
/*
* Award Habitoween ladder items to participants in this month's Habitoween festivities
*/
/* eslint-disable no-console */
const MIGRATION_NAME = '20221031_habitoween_ladder'; // Update when running in future years
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {};
const inc = {
'items.food.Candy_Skeleton': 1,
'items.food.Candy_Base': 1,
'items.food.Candy_CottonCandyBlue': 1,
'items.food.Candy_CottonCandyPink': 1,
'items.food.Candy_Shade': 1,
'items.food.Candy_White': 1,
'items.food.Candy_Golden': 1,
'items.food.Candy_Zombie': 1,
'items.food.Candy_Desert': 1,
'items.food.Candy_Red': 1,
};
set.migration = MIGRATION_NAME;
if (user && user.items && user.items.pets && user.items.pets['JackOLantern-RoyalPurple']) {
set['items.mounts.JackOLantern-RoyalPurple'] = true;
} else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Glow']) {
set['items.pets.JackOLantern-RoyalPurple'] = 5;
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Glow']) {
set['items.mounts.JackOLantern-Glow'] = true;
} else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Ghost']) {
set['items.pets.JackOLantern-Glow'] = 5;
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Ghost']) {
set['items.mounts.JackOLantern-Ghost'] = true;
} else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Base']) {
set['items.pets.JackOLantern-Ghost'] = 5;
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Base']) {
set['items.mounts.JackOLantern-Base'] = true;
} else {
set['items.pets.JackOLantern-Base'] = 5;
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({_id: user._id}, {$inc: inc, $set: set}).exec();
}
export default async function processUsers () {
let query = {
migration: {$ne: MIGRATION_NAME},
'auth.timestamps.loggedin': {$gt: new Date('2022-10-01')},
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,119 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20221031_pet_set_group_achievements';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {
migration: MIGRATION_NAME,
};
if (user && user.items && user.items.pets) {
const pets = user.items.pets;
if (pets['Wolf-Skeleton']
&& pets['TigerCub-Skeleton']
&& pets['PandaCub-Skeleton']
&& pets['LionCub-Skeleton']
&& pets['Fox-Skeleton']
&& pets['FlyingPig-Skeleton']
&& pets['Dragon-Skeleton']
&& pets['Cactus-Skeleton']
&& pets['BearCub-Skeleton']
&& pets['Gryphon-Skeleton']
&& pets['Hedgehog-Skeleton']
&& pets['Deer-Skeleton']
&& pets['Egg-Skeleton']
&& pets['Rat-Skeleton']
&& pets['Octopus-Skeleton']
&& pets['Seahorse-Skeleton']
&& pets['Parrot-Skeleton']
&& pets['Rooster-Skeleton']
&& pets['Spider-Skeleton']
&& pets['Owl-Skeleton']
&& pets['Penguin-Skeleton']
&& pets['TRex-Skeleton']
&& pets['Rock-Skeleton']
&& pets['Bunny-Skeleton']
&& pets['Slime-Skeleton']
&& pets['Sheep-Skeleton']
&& pets['Cuttlefish-Skeleton']
&& pets['Whale-Skeleton']
&& pets['Cheetah-Skeleton']
&& pets['Horse-Skeleton']
&& pets['Frog-Skeleton']
&& pets['Snake-Skeleton']
&& pets['Unicorn-Skeleton']
&& pets['Sabretooth-Skeleton']
&& pets['Monkey-Skeleton']
&& pets['Snail-Skeleton']
&& pets['Falcon-Skeleton']
&& pets['Treeling-Skeleton']
&& pets['Axolotl-Skeleton']
&& pets['Turtle-Skeleton']
&& pets['Armadillo-Skeleton']
&& pets['Cow-Skeleton']
&& pets['Beetle-Skeleton']
&& pets['Ferret-Skeleton']
&& pets['Sloth-Skeleton']
&& pets['Triceratops-Skeleton']
&& pets['GuineaPig-Skeleton']
&& pets['Peacock-Skeleton']
&& pets['Butterfly-Skeleton']
&& pets['Nudibranch-Skeleton']
&& pets['Hippo-Skeleton']
&& pets['Yarn-Skeleton']
&& pets['Pterodactyl-Skeleton']
&& pets['Badger-Skeleton']
&& pets['Squirrel-Skeleton']
&& pets['SeaSerpent-Skeleton']
&& pets['Kangaroo-Skeleton']
&& pets['Alligator-Skeleton']
&& pets['Velociraptor-Skeleton']
&& pets['Dolphin-Skeleton']
&& pets['Robot-Skeleton']) {
set['achievements.boneToPick'] = true;
}
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({ _id: user._id }, { $set: set }).exec();
}
export default async function processUsers () {
let query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2022-01-01') },
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1]._id,
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,108 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20221213_pet_group_achievements';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {
migration: MIGRATION_NAME,
};
if (user && user.items && user.items.pets) {
const pets = user.items.pets;
if (pets['BearCub-Base']
&& pets['BearCub-CottonCandyBlue']
&& pets['BearCub-CottonCandyPink']
&& pets['BearCub-Desert']
&& pets['BearCub-Golden']
&& pets['BearCub-Red']
&& pets['BearCub-Shade']
&& pets['BearCub-Skeleton']
&& pets['BearCub-White']
&& pets['BearCub-Zombie']
&& pets['Fox-Base']
&& pets['Fox-CottonCandyBlue']
&& pets['Fox-CottonCandyPink']
&& pets['Fox-Desert']
&& pets['Fox-Golden']
&& pets['Fox-Red']
&& pets['Fox-Shade']
&& pets['Fox-Skeleton']
&& pets['Fox-White']
&& pets['Fox-Zombie']
&& pets['Penguin-Base']
&& pets['Penguin-CottonCandyBlue']
&& pets['Penguin-CottonCandyPink']
&& pets['Penguin-Desert']
&& pets['Penguin-Golden']
&& pets['Penguin-Red']
&& pets['Penguin-Shade']
&& pets['Penguin-Skeleton']
&& pets['Penguin-White']
&& pets['Penguin-Zombie']
&& pets['Whale-Base']
&& pets['Whale-CottonCandyBlue']
&& pets['Whale-CottonCandyPink']
&& pets['Whale-Desert']
&& pets['Whale-Golden']
&& pets['Whale-Red']
&& pets['Whale-Shade']
&& pets['Whale-Skeleton']
&& pets['Whale-White']
&& pets['Whale-Zombie']
&& pets['Wolf-Base']
&& pets['Wolf-CottonCandyBlue']
&& pets['Wolf-CottonCandyPink']
&& pets['Wolf-Desert']
&& pets['Wolf-Golden']
&& pets['Wolf-Red']
&& pets['Wolf-Shade']
&& pets['Wolf-Skeleton']
&& pets['Wolf-White']
&& pets['Wolf-Zombie']) {
set['achievements.polarPro'] = true;
}
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({ _id: user._id }, { $set: set }).exec();
}
export default async function processUsers () {
let query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2022-11-01') },
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1]._id,
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,144 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20221227_nye';
import { model as User } from '../../../website/server/models/user';
import { v4 as uuid } from 'uuid';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = { migration: MIGRATION_NAME };
let push;
if (typeof user.items.gear.owned.head_special_nye2021 !== 'undefined') {
set['items.gear.owned.head_special_nye2022'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2022',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye2020 !== 'undefined') {
set['items.gear.owned.head_special_nye2021'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2021',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye2019 !== 'undefined') {
set['items.gear.owned.head_special_nye2020'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2020',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye2018 !== 'undefined') {
set['items.gear.owned.head_special_nye2019'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2019',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye2017 !== 'undefined') {
set['items.gear.owned.head_special_nye2018'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2018',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye2016 !== 'undefined') {
set['items.gear.owned.head_special_nye2017'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2017',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye2015 !== 'undefined') {
set['items.gear.owned.head_special_nye2016'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2016',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye2014 !== 'undefined') {
set['items.gear.owned.head_special_nye2015'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2015',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye !== 'undefined') {
set['items.gear.owned.head_special_nye2014'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2014',
_id: uuid(),
},
];
} else {
set['items.gear.owned.head_special_nye'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye',
_id: uuid(),
},
];
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({_id: user._id}, {$set: set, $push: {pinnedItems: {$each: push}}}).exec();
}
export default async function processUsers () {
let query = {
'auth.timestamps.loggedin': {$gt: new Date('2022-12-01')},
migration: {$ne: MIGRATION_NAME},
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,71 +0,0 @@
import filter from 'lodash/filter';
import find from 'lodash/find';
import isArray from 'lodash/isArray';
import { model as Group } from '../../website/server/models/group';
import { model as User } from '../../website/server/models/user';
import * as Tasks from '../../website/server/models/task';
async function updateTeamTasks (team) {
const toSave = [];
const teamTasks = await Tasks.Task.find({
'group.id': team._id,
}).exec();
const teamBoardTasks = filter(teamTasks, task => !task.userId);
const teamUserTasks = filter(teamTasks, task => task.userId);
for (const boardTask of teamBoardTasks) {
if (isArray(boardTask.group.assignedUsers)) {
boardTask.group.approval = undefined;
boardTask.group.assignedDate = undefined;
boardTask.group.assigningUsername = undefined;
boardTask.group.sharedCompletion = undefined;
for (const assignedUserId of boardTask.group.assignedUsers) {
const assignedUser = await User.findById(assignedUserId, 'auth'); // eslint-disable-line no-await-in-loop
const userTask = find(teamUserTasks, task => task.userId === assignedUserId
&& task.group.taskId === boardTask._id);
if (!boardTask.group.assignedUsersDetail) boardTask.group.assignedUsersDetail = {};
if (userTask && assignedUser) {
boardTask.group.assignedUsersDetail[assignedUserId] = {
assignedDate: userTask.group.assignedDate,
assignedUsername: assignedUser.auth.local.username,
assigningUsername: userTask.group.assigningUsername,
completed: userTask.completed || false,
completedDate: userTask.dateCompleted,
};
} else if (assignedUser) {
boardTask.group.assignedUsersDetail[assignedUserId] = {
assignedDate: new Date(),
assignedUsername: assignedUser.auth.local.username,
assigningUsername: null,
completed: false,
completedDate: null,
};
} else {
const taskIndex = boardTask.group.assignedUsers.indexOf(assignedUserId);
boardTask.group.assignedUsers.splice(taskIndex, 1);
}
if (userTask) toSave.push(Tasks.Task.findByIdAndDelete(userTask._id));
}
boardTask.markModified('group');
toSave.push(boardTask.save());
}
}
return Promise.all(toSave);
}
export default async function processTeams () {
const activeTeams = await Group.find({
'purchased.plan.customerId': { $exists: true },
$or: [
{ 'purchased.plan.dateTerminated': { $exists: false } },
{ 'purchased.plan.dateTerminated': null },
{ 'purchased.plan.dateTerminated': { $gt: new Date() } },
],
}).exec();
const taskPromises = activeTeams.map(updateTeamTasks);
return Promise.all(taskPromises);
}

View File

@@ -1,88 +0,0 @@
/* eslint-disable no-console */
import { v4 as uuid } from 'uuid';
import { model as User } from '../../../website/server/models/user';
const MIGRATION_NAME = '20230123_habit_birthday';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count += 1;
const inc = { 'balance': 5 };
const set = {};
const push = {};
set.migration = MIGRATION_NAME;
if (typeof user.items.gear.owned.armor_special_birthday2022 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2023'] = true;
} else if (typeof user.items.gear.owned.armor_special_birthday2021 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2022'] = true;
} else if (typeof user.items.gear.owned.armor_special_birthday2020 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2021'] = true;
} else if (typeof user.items.gear.owned.armor_special_birthday2019 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2020'] = true;
} else if (typeof user.items.gear.owned.armor_special_birthday2018 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2019'] = true;
} else if (typeof user.items.gear.owned.armor_special_birthday2017 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2018'] = true;
} else if (typeof user.items.gear.owned.armor_special_birthday2016 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2017'] = true;
} else if (typeof user.items.gear.owned.armor_special_birthday2015 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2016'] = true;
} else if (typeof user.items.gear.owned.armor_special_birthday !== 'undefined') {
set['items.gear.owned.armor_special_birthday2015'] = true;
} else {
set['items.gear.owned.armor_special_birthday'] = true;
}
push.notifications = {
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_head_special_nye',
title: 'Birthday Bash Day 1!',
text: 'Enjoy your new Birthday Robe and 20 Gems on us!',
destination: 'equipment',
},
seen: false,
};
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({_id: user._id}, {$inc: inc, $set: set, $push: push}).exec();
}
export default async function processUsers () {
let query = {
migration: {$ne: MIGRATION_NAME},
'auth.timestamps.loggedin': {$gt: new Date('2022-12-23')},
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,69 +0,0 @@
/* eslint-disable no-console */
import { v4 as uuid } from 'uuid';
import { model as User } from '../../../website/server/models/user';
const MIGRATION_NAME = '20230127_habit_birthday_day5';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count += 1;
const set = {};
const push = {};
set.migration = MIGRATION_NAME;
set['items.gear.owned.back_special_anniversary'] = true;
set['items.gear.owned.body_special_anniversary'] = true;
set['items.gear.owned.eyewear_special_anniversary'] = true;
push.notifications = {
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_head_special_nye',
title: 'Birthday Bash Day 5!',
text: 'Come celebrate by wearing your new Habitica Hero Cape, Collar, and Mask!',
destination: 'equipment',
},
seen: false,
};
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({_id: user._id}, {$set: set, $push: push}).exec();
}
export default async function processUsers () {
let query = {
migration: {$ne: MIGRATION_NAME},
'auth.timestamps.loggedin': {$gt: new Date('2022-12-23')},
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,79 +0,0 @@
/* eslint-disable no-console */
import { v4 as uuid } from 'uuid';
import { model as User } from '../../../website/server/models/user';
const MIGRATION_NAME = '20230201_habit_birthday_day10';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count += 1;
const set = {
migration: MIGRATION_NAME,
'purchased.background.birthday_bash': true,
};
const push = {
notifications: {
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_head_special_nye',
title: 'Birthday Bash Day 10!',
text: 'Join in for the end of our birthday celebrations with 10th Birthday background, Cake, and achievement!',
destination: 'backgrounds',
},
seen: false,
},
};
const inc = {
'items.food.Cake_Skeleton': 1,
'items.food.Cake_Base': 1,
'items.food.Cake_CottonCandyBlue': 1,
'items.food.Cake_CottonCandyPink': 1,
'items.food.Cake_Shade': 1,
'items.food.Cake_White': 1,
'items.food.Cake_Golden': 1,
'items.food.Cake_Zombie': 1,
'items.food.Cake_Desert': 1,
'items.food.Cake_Red': 1,
'achievements.habitBirthdays': 1,
};
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({_id: user._id}, {$set: set, $push: push, $inc: inc }).exec();
}
export default async function processUsers () {
let query = {
migration: {$ne: MIGRATION_NAME},
'auth.timestamps.loggedin': {$gt: new Date('2022-12-23')},
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,158 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20230522_pet_group_achievements';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {
migration: MIGRATION_NAME,
};
if (user && user.items && user.items.pets) {
const pets = user.items.pets;
if (pets['Parrot-Base']
&& pets['Parrot-CottonCandyBlue']
&& pets['Parrot-CottonCandyPink']
&& pets['Parrot-Desert']
&& pets['Parrot-Golden']
&& pets['Parrot-Red']
&& pets['Parrot-Shade']
&& pets['Parrot-Skeleton']
&& pets['Parrot-White']
&& pets['Parrot-Zombie']
&& pets['Rooster-Base']
&& pets['Rooster-CottonCandyBlue']
&& pets['Rooster-CottonCandyPink']
&& pets['Rooster-Desert']
&& pets['Rooster-Golden']
&& pets['Rooster-Red']
&& pets['Rooster-Shade']
&& pets['Rooster-Skeleton']
&& pets['Rooster-White']
&& pets['Rooster-Zombie']
&& pets['Triceratops-Base']
&& pets['Triceratops-CottonCandyBlue']
&& pets['Triceratops-CottonCandyPink']
&& pets['Triceratops-Desert']
&& pets['Triceratops-Golden']
&& pets['Triceratops-Red']
&& pets['Triceratops-Shade']
&& pets['Triceratops-Skeleton']
&& pets['Triceratops-White']
&& pets['Triceratops-Zombie']
&& pets['TRex-Base']
&& pets['TRex-CottonCandyBlue']
&& pets['TRex-CottonCandyPink']
&& pets['TRex-Desert']
&& pets['TRex-Golden']
&& pets['TRex-Red']
&& pets['TRex-Shade']
&& pets['TRex-Skeleton']
&& pets['TRex-White']
&& pets['TRex-Zombie']
&& pets['Pterodactyl-Base']
&& pets['Pterodactyl-CottonCandyBlue']
&& pets['Pterodactyl-CottonCandyPink']
&& pets['Pterodactyl-Desert']
&& pets['Pterodactyl-Golden']
&& pets['Pterodactyl-Red']
&& pets['Pterodactyl-Shade']
&& pets['Pterodactyl-Skeleton']
&& pets['Pterodactyl-White']
&& pets['Pterodactyl-Zombie']
&& pets['Owl-Base']
&& pets['Owl-CottonCandyBlue']
&& pets['Owl-CottonCandyPink']
&& pets['Owl-Desert']
&& pets['Owl-Golden']
&& pets['Owl-Red']
&& pets['Owl-Shade']
&& pets['Owl-Skeleton']
&& pets['Owl-White']
&& pets['Owl-Zombie']
&& pets['Velociraptor-Base']
&& pets['Velociraptor-CottonCandyBlue']
&& pets['Velociraptor-CottonCandyPink']
&& pets['Velociraptor-Desert']
&& pets['Velociraptor-Golden']
&& pets['Velociraptor-Red']
&& pets['Velociraptor-Shade']
&& pets['Velociraptor-Skeleton']
&& pets['Velociraptor-White']
&& pets['Velociraptor-Zombie']
&& pets['Penguin-Base']
&& pets['Penguin-CottonCandyBlue']
&& pets['Penguin-CottonCandyPink']
&& pets['Penguin-Desert']
&& pets['Penguin-Golden']
&& pets['Penguin-Red']
&& pets['Penguin-Shade']
&& pets['Penguin-Skeleton']
&& pets['Penguin-White']
&& pets['Penguin-Zombie']
&& pets['Falcon-Base']
&& pets['Falcon-CottonCandyBlue']
&& pets['Falcon-CottonCandyPink']
&& pets['Falcon-Desert']
&& pets['Falcon-Golden']
&& pets['Falcon-Red']
&& pets['Falcon-Shade']
&& pets['Falcon-Skeleton']
&& pets['Falcon-White']
&& pets['Falcon-Zombie']
&& pets['Peacock-Base']
&& pets['Peacock-CottonCandyBlue']
&& pets['Peacock-CottonCandyPink']
&& pets['Peacock-Desert']
&& pets['Peacock-Golden']
&& pets['Peacock-Red']
&& pets['Peacock-Shade']
&& pets['Peacock-Skeleton']
&& pets['Peacock-White']
&& pets['Peacock-Zombie']) {
set['achievements.dinosaurDynasty'] = true;
}
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({ _id: user._id }, { $set: set }).exec();
}
export default async function processUsers () {
let query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2023-04-15') },
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1]._id,
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,79 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20230718_summer_splash_orcas';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = { migration: MIGRATION_NAME };
const push = {};
if (user && user.items && user.items.pets && typeof user.items.pets['Orca-Base'] !== 'undefined') {
return;
} else if (user && user.items && user.items.mounts && typeof user.items.mounts['Orca-Base'] !== 'undefined') {
set['items.pets.Orca-Base'] = 5;
push.notifications = {
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_orca_pet',
title: 'Orcas for Summer Splash!',
text: 'To celebrate Summer Splash, we\'ve given you an Orca Pet!',
destination: 'stable',
},
seen: false,
};
} else {
set['items.mounts.Orca-Base'] = true;
push.notifications = {
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_orca_mount',
title: 'Orcas for Summer Splash!',
text: 'To celebrate Summer Splash, we\'ve given you an Orca Mount!',
destination: 'stable',
},
seen: false,
};
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await user.updateOne({ $set: set, $push: push }).exec();
}
export default async function processUsers () {
let query = {
migration: {$ne: MIGRATION_NAME},
'auth.timestamps.loggedin': {$gt: new Date('2023-06-18')},
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,155 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20230731_naming_day';
import { v4 as uuid } from 'uuid';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
let set;
let push;
const inc = {
'items.food.Cake_Base': 1,
'items.food.Cake_CottonCandyBlue': 1,
'items.food.Cake_CottonCandyPink': 1,
'items.food.Cake_Desert': 1,
'items.food.Cake_Golden': 1,
'items.food.Cake_Red': 1,
'items.food.Cake_Shade': 1,
'items.food.Cake_Skeleton': 1,
'items.food.Cake_White': 1,
'items.food.Cake_Zombie': 1,
'achievements.habiticaDays': 1,
};
if (user && user.items && user.items.gear && user.items.gear.owned && typeof user.items.gear.owned.back_special_namingDay2020 !== 'undefined') {
set = { migration: MIGRATION_NAME };
push = {
notifications: {
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_namingDay_cake',
title: 'Happy Naming Day!',
text: 'To celebrate the day we became Habitica, weve awarded you some cake!',
destination: '/inventory/items',
},
seen: false,
},
};
} else if (user && user.items && user.items.gear && user.items.gear.owned && typeof user.items.gear.owned.body_special_namingDay2018 !== 'undefined') {
set = { migration: MIGRATION_NAME, 'items.gear.owned.back_special_namingDay2020': true };
push = {
notifications: {
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_namingDay_back',
title: 'Happy Naming Day!',
text: 'To celebrate the day we became Habitica, weve awarded you a Royal Purple Gryphon Tail and cake!',
destination: '/inventory/equipment',
},
seen: false,
},
};
} else if (user && user.items && user.items.gear && user.items.gear.owned && typeof user.items.gear.owned.head_special_namingDay2017 !== 'undefined') {
set = { migration: MIGRATION_NAME, 'items.gear.owned.body_special_namingDay2018': true };
push = {
notifications: {
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_namingDay_body',
title: 'Happy Naming Day!',
text: 'To celebrate the day we became Habitica, weve awarded you a Royal Purple Gryphon Cloak and cake!',
destination: '/inventory/equipment',
},
seen: false,
},
};
} else if (user && user.items && user.items.pets && typeof user.items.pets['Gryphon-RoyalPurple'] !== 'undefined') {
set = { migration: MIGRATION_NAME, 'items.gear.owned.head_special_namingDay2017': true };
push = {
notifications: {
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_namingDay_head',
title: 'Happy Naming Day!',
text: 'To celebrate the day we became Habitica, weve awarded you a Royal Purple Gryphon Helm and cake!',
destination: '/inventory/equipment',
},
seen: false,
},
};
} else if (user && user.items && user.items.mounts && typeof user.items.mounts['Gryphon-RoyalPurple'] !== 'undefined') {
set = { migration: MIGRATION_NAME, 'items.pets.Gryphon-RoyalPurple': 5 };
push = {
notifications: {
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_namingDay_pet',
title: 'Happy Naming Day!',
text: 'To celebrate the day we became Habitica, weve awarded you a Royal Purple Gryphon Pet and cake!',
destination: '/inventory/stable',
},
seen: false,
},
};
} else {
set = { migration: MIGRATION_NAME, 'items.mounts.Gryphon-RoyalPurple': true };
push = {
notifications: {
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_namingDay_mount',
title: 'Happy Naming Day!',
text: 'To celebrate the day we became Habitica, weve awarded you a Royal Purple Gryphon Mount and cake!',
destination: '/inventory/stable',
},
seen: false,
},
};
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
if (push) {
return await user.updateOne({ $set: set, $inc: inc, $push: push }).exec();
} else {
return await user.updateOne({ $set: set, $inc: inc }).exec();
}
}
export default async function processUsers () {
let query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2023-07-01') },
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1]._id,
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,72 +0,0 @@
/* eslint-disable no-console */
import { model as User } from '../../../website/server/models/user';
import { model as Group } from '../../../website/server/models/group';
const guildsPerRun = 500;
const progressCount = 1000;
const guildsQuery = {
type: 'guild',
};
let count = 0;
async function updateGroup (guild) {
count++;
if (count % progressCount === 0) {
console.warn(`${count} ${guild._id}`);
}
if (guild.hasActiveGroupPlan()) {
return console.warn(`Guild ${guild._id} is active Group Plan`);
}
const leader = await User
.findOne({ _id: guild.leader })
.select({ _id: true })
.exec();
if (!leader) {
return console.warn(`Leader not found for Guild ${guild._id}`);
}
if (guild.balance > 0) {
await leader.updateBalance(
guild.balance,
'create_guild',
'',
`Guild Bank refund for ${guild.name} (${guild._id})`,
);
}
return guild.updateOne({ $set: { balance: 0 } }).exec();
}
export default async function processGroups () {
const guildFields = {
_id: 1,
balance: 1,
leader: 1,
name: 1,
purchased: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const foundGroups = await Group // eslint-disable-line no-await-in-loop
.find(guildsQuery)
.limit(guildsPerRun)
.sort({ _id: 1 })
.select(guildFields)
.exec();
if (foundGroups.length === 0) {
console.warn('All appropriate Guilds found and modified.');
console.warn(`\n${count} Guilds processed\n`);
break;
} else {
guildsQuery._id = {
$gt: foundGroups[foundGroups.length - 1],
};
}
await Promise.all(foundGroups.map(guild => updateGroup(guild))); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,62 +0,0 @@
/* eslint-disable no-console */
import { model as User } from '../../../website/server/models/user';
import { TransactionModel as Transaction } from '../../../website/server/models/transaction';
const transactionsPerRun = 500;
const progressCount = 1000;
const transactionsQuery = {
transactionType: 'create_guild',
amount: { $gt: 0 },
};
let count = 0;
async function updateTransaction (transaction) {
count++;
if (count % progressCount === 0) {
console.warn(`${count} ${transaction._id}`);
}
const leader = await User
.findOne({ _id: transaction.userId })
.select({ _id: true })
.exec();
if (!leader) {
return console.warn(`User not found for transaction ${transaction._id}`);
}
return leader.updateOne(
{ $inc: { balance: transaction.amount }},
).exec();
}
export default async function processTransactions () {
const transactionFields = {
_id: 1,
userId: 1,
currency: 1,
amount: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const foundTransactions = await Transaction // eslint-disable-line no-await-in-loop
.find(transactionsQuery)
.limit(transactionsPerRun)
.sort({ _id: 1 })
.select(transactionFields)
.lean()
.exec();
if (foundTransactions.length === 0) {
console.warn('All appropriate transactions found and modified.');
console.warn(`\n${count} transactions processed\n`);
break;
} else {
transactionsQuery._id = {
$gt: foundTransactions[foundTransactions.length - 1],
};
}
await Promise.all(foundTransactions.map(txn => updateTransaction(txn))); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,144 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20230808_veteran_pet_ladder';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {};
let push = { notifications: { $each: [] }};
set.migration = MIGRATION_NAME;
if (user.items.pets['Fox-Veteran']) {
set['items.pets.Dragon-Veteran'] = 5;
push.notifications.$each.push({
type: 'ITEM_RECEIVED',
data: {
icon: 'icon_pet_veteran_dragon',
title: 'Youve received a Veteran Pet!',
text: 'To commemorate being here for a new era of Habitica, weve awarded you a Veteran Dragon.',
destination: '/inventory/stable',
},
seen: false,
});
} else if (user.items.pets['Bear-Veteran']) {
set['items.pets.Fox-Veteran'] = 5;
push.notifications.$each.push({
type: 'ITEM_RECEIVED',
data: {
icon: 'icon_pet_veteran_fox',
title: 'Youve received a Veteran Pet!',
text: 'To commemorate being here for a new era of Habitica, weve awarded you a Veteran Fox.',
destination: '/inventory/stable',
},
seen: false,
});
} else if (user.items.pets['Lion-Veteran']) {
set['items.pets.Bear-Veteran'] = 5;
push.notifications.$each.push({
type: 'ITEM_RECEIVED',
data: {
icon: 'icon_pet_veteran_bear',
title: 'Youve received a Veteran Pet!',
text: 'To commemorate being here for a new era of Habitica, weve awarded you a Veteran Bear.',
destination: '/inventory/stable',
},
seen: false,
});
} else if (user.items.pets['Tiger-Veteran']) {
set['items.pets.Lion-Veteran'] = 5;
push.notifications.$each.push({
type: 'ITEM_RECEIVED',
data: {
icon: 'icon_pet_veteran_lion',
title: 'Youve received a Veteran Pet!',
text: 'To commemorate being here for a new era of Habitica, weve awarded you a Veteran Lion.',
destination: '/inventory/stable',
},
seen: false,
});
} else if (user.items.pets['Wolf-Veteran']) {
set['items.pets.Tiger-Veteran'] = 5;
push.notifications.$each.push({
type: 'ITEM_RECEIVED',
data: {
icon: 'icon_pet_veteran_tiger',
title: 'Youve received a Veteran Pet!',
text: 'To commemorate being here for a new era of Habitica, weve awarded you a Veteran Tiger.',
destination: '/inventory/stable',
},
seen: false,
});
} else {
set['items.pets.Wolf-Veteran'] = 5;
push.notifications.$each.push({
type: 'ITEM_RECEIVED',
data: {
icon: 'icon_pet_veteran_wolf',
title: 'Youve received a Veteran Pet!',
text: 'To commemorate being here for a new era of Habitica, weve awarded you a Veteran Wolf.',
destination: '/inventory/stable',
},
seen: false,
});
}
if (user.contributor.level > 0) {
set['items.gear.owned.armor_special_heroicTunic'] = true;
set['items.gear.owned.back_special_heroicAureole'] = true;
set['items.gear.owned.headAccessory_special_heroicCirclet'] = true;
push.notifications.$each.push({
type: 'ITEM_RECEIVED',
data: {
icon: 'heroic_set_icon',
title: 'Youve received the Heroic Set!',
text: 'To commemorate your hard work as a contributor, weve awarded you the Heroic Circlet, Heroic Aureole, and Heroic Tunic.',
destination: '/inventory/equipment',
},
seen: false,
});
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({_id: user._id}, {$set: set, $push: push}).exec();
}
export default async function processUsers () {
let query = {
migration: {$ne: MIGRATION_NAME},
'auth.timestamps.loggedin': { $gt: new Date('2023-07-08') },
};
const fields = {
_id: 1,
items: 1,
migration: 1,
contributor: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,118 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20231017_pet_group_achievements';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {
migration: MIGRATION_NAME,
};
if (user && user.items && user.items.pets) {
const pets = user.items.pets;
if (pets['Armadillo-Base']
&& pets['Armadillo-CottonCandyBlue']
&& pets['Armadillo-CottonCandyPink']
&& pets['Armadillo-Desert']
&& pets['Armadillo-Golden']
&& pets['Armadillo-Red']
&& pets['Armadillo-Shade']
&& pets['Armadillo-Skeleton']
&& pets['Armadillo-White']
&& pets['Armadillo-Zombie']
&& pets['Cactus-Base']
&& pets['Cactus-CottonCandyBlue']
&& pets['Cactus-CottonCandyPink']
&& pets['Cactus-Desert']
&& pets['Cactus-Golden']
&& pets['Cactus-Red']
&& pets['Cactus-Shade']
&& pets['Cactus-Skeleton']
&& pets['Cactus-White']
&& pets['Cactus-Zombie']
&& pets['Fox-Base']
&& pets['Fox-CottonCandyBlue']
&& pets['Fox-CottonCandyPink']
&& pets['Fox-Desert']
&& pets['Fox-Golden']
&& pets['Fox-Red']
&& pets['Fox-Shade']
&& pets['Fox-Skeleton']
&& pets['Fox-White']
&& pets['Fox-Zombie']
&& pets['Frog-Base']
&& pets['Frog-CottonCandyBlue']
&& pets['Frog-CottonCandyPink']
&& pets['Frog-Desert']
&& pets['Frog-Golden']
&& pets['Frog-Red']
&& pets['Frog-Shade']
&& pets['Frog-Skeleton']
&& pets['Frog-White']
&& pets['Frog-Zombie']
&& pets['Snake-Base']
&& pets['Snake-CottonCandyBlue']
&& pets['Snake-CottonCandyPink']
&& pets['Snake-Desert']
&& pets['Snake-Golden']
&& pets['Snake-Red']
&& pets['Snake-Shade']
&& pets['Snake-Skeleton']
&& pets['Snake-White']
&& pets['Snake-Zombie']
&& pets['Spider-Base']
&& pets['Spider-CottonCandyBlue']
&& pets['Spider-CottonCandyPink']
&& pets['Spider-Desert']
&& pets['Spider-Golden']
&& pets['Spider-Red']
&& pets['Spider-Shade']
&& pets['Spider-Skeleton']
&& pets['Spider-White']
&& pets['Spider-Zombie']) {
set['achievements.duneBuddy'] = true;
}
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.updateOne({ _id: user._id }, { $set: set }).exec();
}
export default async function processUsers () {
let query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2023-09-16') },
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1]._id,
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,124 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20231114_pet_group_achievements';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
let set = {
migration: MIGRATION_NAME,
};
if (user && user.items && user.items.pets) {
const pets = user.items.pets;
if (pets['Cactus-Zombie'] > 0
&& pets['Cactus-Skeleton'] > 0
&& pets['Cactus-Base'] > 0
&& pets['Cactus-Desert'] > 0
&& pets['Cactus-Red'] > 0
&& pets['Cactus-Shade'] > 0
&& pets['Cactus-White']> 0
&& pets['Cactus-Golden'] > 0
&& pets['Cactus-CottonCandyBlue'] > 0
&& pets['Cactus-CottonCandyPink'] > 0
&& pets['Hedgehog-Zombie'] > 0
&& pets['Hedgehog-Skeleton'] > 0
&& pets['Hedgehog-Base'] > 0
&& pets['Hedgehog-Desert'] > 0
&& pets['Hedgehog-Red'] > 0
&& pets['Hedgehog-Shade'] > 0
&& pets['Hedgehog-White'] > 0
&& pets['Hedgehog-Golder'] > 0
&& pets['Hedgehog-CottonCandyBlue'] > 0
&& pets['Hedgehog-CottonCandyPink'] > 0
&& pets['Rock-Zombie'] > 0
&& pets['Rock-Skeleton'] > 0
&& pets['Rock-Base'] > 0
&& pets['Rock-Desert'] > 0
&& pets['Rock-Red'] > 0
&& pets['Rock-Shade'] > 0
&& pets['Rock-White'] > 0
&& pets['Rock-Golden'] > 0
&& pets['Rock-CottonCandyBlue'] > 0
&& pets['Rock-CottonCandyPink'] > 0 ) {
set['achievements.roughRider'] = true;
}
}
if (user && user.items && user.items.mounts) {
const mounts = user.items.mounts;
if (mounts['Cactus-Zombie']
&& mounts['Cactus-Skeleton']
&& mounts['Cactus-Base']
&& mounts['Cactus-Desert']
&& mounts['Cactus-Red']
&& mounts['Cactus-Shade']
&& mounts['Cactus-White']
&& mounts['Cactus-Golden']
&& mounts['Cactus-CottonCandyPink']
&& mounts['Cactus-CottonCandyBlue']
&& mounts['Hedgehog-Zombie']
&& mounts['Hedgehog-Skeleton']
&& mounts['Hedgehog-Base']
&& mounts['Hedgehog-Desert']
&& mounts['Hedgehog-Red']
&& mounts['Hedgehog-Shade']
&& mounts['Hedgehog-White']
&& mounts['Hedgehog-Golden']
&& mounts['Hedgehog-CottonCandyPink']
&& mounts['Hedgehog-CottonCandyBlue']
&& mounts['Rock-Zombie']
&& mounts['Rock-Skeleton']
&& mounts['Rock-Base']
&& mounts['Rock-Desert']
&& mounts['Rock-Red']
&& mounts['Rock-Shade']
&& mounts['Rock-White']
&& mounts['Rock-Golden']
&& mounts['Rock-CottonCandyPink']
&& mounts['Rock-CottonCandyBlue'] ) {
set['achievements.roughRider'] = true;
}
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({ _id: user._id }, { $set: set }).exec();
}
module.exports = async function processUsers () {
let query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2023-02-01') },
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1]._id,
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,87 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20231228_nye';
import { model as User } from '../../../website/server/models/user';
import { v4 as uuid } from 'uuid';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = { migration: MIGRATION_NAME };
let push = {};
if (typeof user.items.gear.owned.head_special_nye2022 !== 'undefined') {
set['items.gear.owned.head_special_nye2023'] = true;
} else if (typeof user.items.gear.owned.head_special_nye2021 !== 'undefined') {
set['items.gear.owned.head_special_nye2022'] = true;
} else if (typeof user.items.gear.owned.head_special_nye2020 !== 'undefined') {
set['items.gear.owned.head_special_nye2021'] = true;
} else if (typeof user.items.gear.owned.head_special_nye2019 !== 'undefined') {
set['items.gear.owned.head_special_nye2020'] = true;
} else if (typeof user.items.gear.owned.head_special_nye2018 !== 'undefined') {
set['items.gear.owned.head_special_nye2019'] = true;
} else if (typeof user.items.gear.owned.head_special_nye2017 !== 'undefined') {
set['items.gear.owned.head_special_nye2018'] = true;
} else if (typeof user.items.gear.owned.head_special_nye2016 !== 'undefined') {
set['items.gear.owned.head_special_nye2017'] = true;
} else if (typeof user.items.gear.owned.head_special_nye2015 !== 'undefined') {
set['items.gear.owned.head_special_nye2016'] = true;
} else if (typeof user.items.gear.owned.head_special_nye2014 !== 'undefined') {
set['items.gear.owned.head_special_nye2015'] = true;
} else if (typeof user.items.gear.owned.head_special_nye !== 'undefined') {
set['items.gear.owned.head_special_nye2014'] = true;
} else {
set['items.gear.owned.head_special_nye'] = true;
}
push.notifications = {
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_head_special_nye',
title: 'Happy New Year!',
text: 'Check your Equipment for this year\'s party hat!',
destination: 'inventory/equipment',
},
seen: false,
};
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.updateOne({_id: user._id}, {$set: set, $push: push}).exec();
}
export default async function processUsers () {
let query = {
'auth.timestamps.loggedin': { $gt: new Date('2023-12-01') },
migration: { $ne: MIGRATION_NAME },
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,102 +0,0 @@
/* eslint-disable no-console */
import { v4 as uuid } from 'uuid';
import { model as User } from '../../../website/server/models/user';
const MIGRATION_NAME = '20240131_habit_birthday';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count += 1;
const inc = {
'items.food.Cake_Skeleton': 1,
'items.food.Cake_Base': 1,
'items.food.Cake_CottonCandyBlue': 1,
'items.food.Cake_CottonCandyPink': 1,
'items.food.Cake_Shade': 1,
'items.food.Cake_White': 1,
'items.food.Cake_Golden': 1,
'items.food.Cake_Zombie': 1,
'items.food.Cake_Desert': 1,
'items.food.Cake_Red': 1,
'achievements.habitBirthdays': 1,
};
const set = {};
const push = {
notifications: {
type: 'ITEM_RECEIVED',
data: {
icon: 'notif_namingDay_cake',
title: 'Happy Habit Birthday!',
text: 'Habitica turns 11 today! Enjoy free party robes and cake!',
destination: 'inventory/equipment',
},
seen: false,
},
};
set.migration = MIGRATION_NAME;
if (typeof user.items.gear.owned.armor_special_birthday2023 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2024'] = true;
} else if (typeof user.items.gear.owned.armor_special_birthday2022 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2023'] = true;
} else if (typeof user.items.gear.owned.armor_special_birthday2021 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2022'] = true;
} else if (typeof user.items.gear.owned.armor_special_birthday2020 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2021'] = true;
} else if (typeof user.items.gear.owned.armor_special_birthday2019 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2020'] = true;
} else if (typeof user.items.gear.owned.armor_special_birthday2018 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2019'] = true;
} else if (typeof user.items.gear.owned.armor_special_birthday2017 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2018'] = true;
} else if (typeof user.items.gear.owned.armor_special_birthday2016 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2017'] = true;
} else if (typeof user.items.gear.owned.armor_special_birthday2015 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2016'] = true;
} else if (typeof user.items.gear.owned.armor_special_birthday !== 'undefined') {
set['items.gear.owned.armor_special_birthday2015'] = true;
} else {
set['items.gear.owned.armor_special_birthday'] = true;
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.updateOne({_id: user._id}, {$inc: inc, $set: set, $push: push}).exec();
}
export default async function processUsers () {
let query = {
migration: {$ne: MIGRATION_NAME},
'auth.timestamps.loggedin': {$gt: new Date('2023-12-23')},
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -1,89 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '202403_pet_group_achievements';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
let set = {
migration: MIGRATION_NAME,
};
if (user && user.items && user.items.pets) {
const pets = user.items.pets;
if (pets['GuineaPig-Zombie'] > 0
&& pets['GuineaPig-Skeleton'] > 0
&& pets['GuineaPig-Base'] > 0
&& pets['GuineaPig-Desert'] > 0
&& pets['GuineaPig-Red'] > 0
&& pets['GuineaPig-Shade'] > 0
&& pets['GuineaPig-White']> 0
&& pets['GuineaPig-Golden'] > 0
&& pets['GuineaPig-CottonCandyBlue'] > 0
&& pets['GuineaPig-CottonCandyPink'] > 0
&& pets['Squirrel-Zombie'] > 0
&& pets['Squirrel-Skeleton'] > 0
&& pets['Squirrel-Base'] > 0
&& pets['Squirrel-Desert'] > 0
&& pets['Squirrel-Red'] > 0
&& pets['Squirrel-Shade'] > 0
&& pets['Squirrel-White'] > 0
&& pets['Squirrel-Golden'] > 0
&& pets['Squirrel-CottonCandyBlue'] > 0
&& pets['Squirrel-CottonCandyPink'] > 0
&& pets['Rat-Zombie'] > 0
&& pets['Rat-Skeleton'] > 0
&& pets['Rat-Base'] > 0
&& pets['Rat-Desert'] > 0
&& pets['Rat-Red'] > 0
&& pets['Rat-Shade'] > 0
&& pets['Rat-White'] > 0
&& pets['Rat-Golden'] > 0
&& pets['Rat-CottonCandyBlue'] > 0
&& pets['Rat-CottonCandyPink'] > 0 ) {
set['achievements.rodentRuler'] = true;
}
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.updateOne({ _id: user._id }, { $set: set }).exec();
}
export default async function processUsers () {
let query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2024-02-01') },
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1]._id,
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -16,7 +16,7 @@ AWS.config.update({
const BUCKET_NAME = config.S3.bucket;
const s3 = new AWS.S3();
// Adapted from https://stackoverflow.com/a/22210077/2601552
// Adapted from http://stackoverflow.com/a/22210077/2601552
function uploadFile (buffer, fileName) {
return new Promise((resolve, reject) => {
s3.putObject({

View File

@@ -0,0 +1,118 @@
let migrationName = '20180904_takeThis.js'; // Update per month
let authorName = 'Sabe'; // in case script author needs to know when their ...
let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done
/*
* Award Take This ladder items to participants in this month's challenge
*/
import monk from 'monk';
import nconf from 'nconf';
const CONNECTION_STRING = nconf.get('MIGRATION_CONNECT_STRING'); // FOR TEST DATABASE
let dbUsers = monk(CONNECTION_STRING).get('users', { castIds: false });
function processUsers (lastId) {
// specify a query to limit the affected users (empty for all users):
let query = {
migration: {$ne: migrationName},
challenges: {$in: ['1044ec0c-4a85-48c5-9f36-d51c0c62c7d3']}, // Update per month
};
if (lastId) {
query._id = {
$gt: lastId,
};
}
dbUsers.find(query, {
sort: {_id: 1},
limit: 250,
fields: [
'items.gear.owned',
], // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
})
.then(updateUsers)
.catch((err) => {
console.log(err);
return exiting(1, `ERROR! ${ err}`);
});
}
let progressCount = 1000;
let count = 0;
function updateUsers (users) {
if (!users || users.length === 0) {
console.warn('All appropriate users found and modified.');
displayData();
return;
}
let userPromises = users.map(updateUser);
let lastUser = users[users.length - 1];
return Promise.all(userPromises)
.then(() => {
processUsers(lastUser._id);
});
}
function updateUser (user) {
count++;
let set = {};
let push;
if (typeof user.items.gear.owned.back_special_takeThis !== 'undefined') {
set = {migration: migrationName};
} else if (typeof user.items.gear.owned.body_special_takeThis !== 'undefined') {
set = {migration: migrationName, 'items.gear.owned.back_special_takeThis': false};
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.back_special_takeThis', _id: monk.id()}};
} else if (typeof user.items.gear.owned.head_special_takeThis !== 'undefined') {
set = {migration: migrationName, 'items.gear.owned.body_special_takeThis': false};
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.body_special_takeThis', _id: monk.id()}};
} else if (typeof user.items.gear.owned.armor_special_takeThis !== 'undefined') {
set = {migration: migrationName, 'items.gear.owned.head_special_takeThis': false};
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_takeThis', _id: monk.id()}};
} else if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') {
set = {migration: migrationName, 'items.gear.owned.armor_special_takeThis': false};
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_takeThis', _id: monk.id()}};
} else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') {
set = {migration: migrationName, 'items.gear.owned.weapon_special_takeThis': false};
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.weapon_special_takeThis', _id: monk.id()}};
} else {
set = {migration: migrationName, 'items.gear.owned.shield_special_takeThis': false};
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.shield_special_takeThis', _id: monk.id()}};
}
if (push) {
dbUsers.update({_id: user._id}, {$set: set, $push: push});
} else {
dbUsers.update({_id: user._id}, {$set: set});
}
if (count % progressCount === 0) console.warn(`${count } ${ user._id}`);
if (user._id === authorUuid) console.warn(`${authorName } processed`);
}
function displayData () {
console.warn(`\n${ count } users processed\n`);
return exiting(0);
}
function exiting (code, msg) {
code = code || 0; // 0 = success
if (code && !msg) {
msg = 'ERROR!';
}
if (msg) {
if (code) {
console.error(msg);
} else {
console.log(msg);
}
}
process.exit(code);
}
module.exports = processUsers;

View File

@@ -2,7 +2,7 @@
// For some reason people often to contact me to cancel their sub,
// rather than do it online. Even when I point them to
// the FAQ (https://habitica.fandom.com/wiki/FAQ) they insist...
// the FAQ (http://goo.gl/1uoPGQ) they insist...
db.users.update(
{ _id: '' },

10
migrations/csvexport.py Normal file
View File

@@ -0,0 +1,10 @@
import csv
with open(r"/home/slappybag/Documents/SurveyScrape.csv") as f:
reader = csv.reader(f, delimiter=',', quotechar='"')
column = []
for row in reader:
if row:
column.append(row[4])
print column

View File

@@ -21,14 +21,12 @@ async function handOutJackalopes () {
if (user.party._id) groupList.push(user.party._id);
groupList = groupList.concat(user.guilds);
const subscribedGroup = await Group.findOne(
{
_id: { $in: groupList },
'purchased.plan.planId': 'group_monthly',
'purchased.plan.dateTerminated': null,
},
{ _id: 1 },
);
const subscribedGroup = await Group.findOne({
_id: { $in: groupList },
'purchased.plan.planId': 'group_monthly',
'purchased.plan.dateTerminated': null,
},
{ _id: 1 });
if (subscribedGroup) {
User.update({ _id: user._id }, { $set: { 'items.mounts.Jackalope-RoyalPurple': true } }).exec();

View File

@@ -18,7 +18,7 @@ function setUpServer () {
setUpServer();
// Replace this with your migration
const processUsers = require().default;
const processUsers = require('').default;
processUsers()
.then(() => {

View File

@@ -51,8 +51,7 @@ function getAchievementUpdate (newUser, oldUser) {
// Rebirth level
if (achievementsUpdate.rebirthLevel) {
achievementsUpdate.rebirthLevel = Math.max(
achievementsUpdate.rebirthLevel,
oldAchievements.rebirthLevel,
achievementsUpdate.rebirthLevel, oldAchievements.rebirthLevel,
);
} else if (oldAchievements.rebirthLevel) {
achievementsUpdate.rebirthLevel = oldAchievements.rebirthLevel;

View File

@@ -16,7 +16,6 @@ async function updateUser (user) {
if (count % progressCount === 0) {
console.warn(`${count} ${user._id}`);
// eslint-disable-next-line no-promise-executor-return
await new Promise(resolve => setTimeout(resolve, 5000));
}

View File

@@ -51,7 +51,7 @@ async function updateUser (user) {
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return User.updateOne({ _id: user._id }, { $set: set }).exec();
return User.update({ _id: user._id }, { $set: set }).exec();
}
export default async function processUsers () {

View File

@@ -3,13 +3,13 @@ import { v4 as uuid } from 'uuid';
import { model as User } from '../../website/server/models/user';
const MIGRATION_NAME = '20240314_pi_day';
const MIGRATION_NAME = '20200314_pi_day';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count += 1;
count *= 1;
const inc = {
'items.food.Pie_Skeleton': 1,
@@ -54,7 +54,7 @@ async function updateUser (user) {
export default async function processUsers () {
const query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2023-02-15') },
'auth.timestamps.loggedin': { $gt: new Date('2020-02-15') },
};
const fields = {

25358
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,85 +1,86 @@
{
"name": "habitica",
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
"version": "5.22.2",
"version": "4.151.2",
"main": "./website/server/index.js",
"dependencies": {
"@babel/core": "^7.22.10",
"@babel/preset-env": "^7.22.10",
"@babel/register": "^7.22.15",
"@google-cloud/trace-agent": "^7.1.2",
"@parse/node-apn": "^5.2.3",
"@slack/webhook": "^6.1.0",
"accepts": "^1.3.8",
"amazon-payments": "^0.2.9",
"amplitude": "^6.0.0",
"apidoc": "^0.54.0",
"apple-auth": "^1.0.9",
"bcrypt": "^5.1.1",
"body-parser": "^1.20.2",
"bootstrap": "^4.6.2",
"@babel/core": "^7.10.3",
"@babel/preset-env": "^7.10.3",
"@babel/register": "^7.10.3",
"@google-cloud/trace-agent": "^5.1.0",
"@slack/client": "^4.12.0",
"accepts": "^1.3.5",
"amazon-payments": "^0.2.8",
"amplitude": "^3.5.0",
"apidoc": "^0.24.0",
"apn": "^2.2.0",
"apple-auth": "^1.0.6",
"bcrypt": "^5.0.0",
"body-parser": "^1.18.3",
"compression": "^1.7.4",
"cookie-session": "^2.0.0",
"cookie-session": "^1.4.0",
"coupon-code": "^0.4.5",
"csv-stringify": "^5.6.5",
"csv-stringify": "^5.5.0",
"cwait": "^1.1.1",
"domain-middleware": "~0.1.0",
"eslint": "^8.55.0",
"eslint-config-habitrpg": "^6.2.3",
"eslint": "^6.8.0",
"eslint-config-habitrpg": "^6.2.0",
"eslint-plugin-mocha": "^5.0.0",
"express": "^4.18.2",
"express-basic-auth": "^1.2.1",
"express": "^4.16.3",
"express-basic-auth": "^1.1.5",
"express-validator": "^5.2.0",
"glob": "^8.1.0",
"got": "^11.8.6",
"glob": "^7.1.6",
"got": "^11.5.0",
"gulp": "^4.0.0",
"gulp-babel": "^8.0.0",
"gulp-imagemin": "^7.1.0",
"gulp-nodemon": "^2.5.0",
"gulp.spritesmith": "^6.13.0",
"habitica-markdown": "^3.0.0",
"helmet": "^4.6.0",
"gulp.spritesmith": "^6.9.0",
"habitica-markdown": "^2.0.2",
"helmet": "^3.23.3",
"image-size": "^0.8.3",
"in-app-purchase": "^1.11.3",
"js2xmlparser": "^5.0.0",
"jsonwebtoken": "^9.0.2",
"jwks-rsa": "^2.1.5",
"lodash": "^4.17.21",
"js2xmlparser": "^4.0.1",
"jsonwebtoken": "^8.5.1",
"jwks-rsa": "^1.8.1",
"lodash": "^4.17.19",
"merge-stream": "^2.0.0",
"method-override": "^3.0.0",
"moment": "^2.29.4",
"moment": "^2.27.0",
"moment-recur": "^1.0.7",
"mongoose": "^7.6.3",
"mongoose": "^5.9.25",
"morgan": "^1.10.0",
"nconf": "^0.12.1",
"node-gcm": "^1.0.5",
"nodemon": "^2.0.20",
"nconf": "^0.10.0",
"node-gcm": "^1.0.3",
"on-headers": "^1.0.2",
"passport": "^0.5.3",
"passport": "^0.4.1",
"passport-facebook": "^3.0.0",
"passport-google-oauth2": "^0.2.0",
"passport-google-oauth20": "2.0.0",
"passport-google-oauth20": "1.0.0",
"paypal-ipn": "3.0.0",
"paypal-rest-sdk": "^1.8.1",
"pp-ipn": "^1.1.0",
"ps-tree": "^1.0.0",
"rate-limiter-flexible": "^2.4.2",
"redis": "^3.1.2",
"remove-markdown": "^0.5.0",
"rate-limiter-flexible": "^2.1.7",
"redis": "^3.0.2",
"regenerator-runtime": "^0.13.7",
"remove-markdown": "^0.3.0",
"rimraf": "^3.0.2",
"short-uuid": "^4.2.2",
"stripe": "^12.18.0",
"superagent": "^8.1.2",
"universal-analytics": "^0.5.3",
"short-uuid": "^3.0.0",
"stripe": "^7.15.0",
"superagent": "^5.3.1",
"universal-analytics": "^0.4.23",
"useragent": "^2.1.9",
"uuid": "^9.0.0",
"validator": "^13.11.0",
"winston": "^3.10.0",
"winston-loggly-bulk": "^3.3.0",
"xml2js": "^0.6.2"
"uuid": "^8.2.0",
"validator": "^13.1.1",
"vinyl-buffer": "^1.0.1",
"winston": "^3.3.3",
"winston-loggly-bulk": "^3.1.0",
"xml2js": "^0.4.23"
},
"private": true,
"engines": {
"node": "20",
"npm": "^10"
"node": "^12",
"npm": "^6"
},
"scripts": {
"lint": "eslint --ext .js --fix . && cd website/client && npm run lint",
@@ -103,25 +104,26 @@
"client:unit": "cd website/client && npm run test:unit",
"start": "gulp nodemon",
"debug": "gulp nodemon --inspect",
"mongo:dev": "run-rs -v 5.0.23 -l ubuntu1804 --keep --dbpath mongodb-data --number 1 --quiet",
"postinstall": "git config --global url.\"https://\".insteadOf git:// && gulp build && cd website/client && npm install",
"mongo:dev": "run-rs -v 4.2.8 -l ubuntu1804 --keep --dbpath mongodb-data --number 1 --quiet",
"postinstall": "gulp build && cd website/client && npm install",
"apidoc": "gulp apidoc"
},
"devDependencies": {
"axios": "^1.4.0",
"chai": "^4.3.7",
"axios": "^0.19.2",
"chai": "^4.1.2",
"chai-as-promised": "^7.1.1",
"chai-moment": "^0.1.0",
"chalk": "^5.3.0",
"chalk": "^4.1.0",
"cross-spawn": "^7.0.3",
"expect.js": "^0.3.1",
"istanbul": "^1.1.0-alpha.1",
"mocha": "^5.1.1",
"monk": "^7.3.4",
"monk": "^7.3.0",
"require-again": "^2.0.0",
"run-rs": "^0.7.7",
"sinon": "^15.2.0",
"sinon-chai": "^3.7.0",
"run-rs": "^0.6.2",
"sinon": "^9.0.2",
"sinon-chai": "^3.5.0",
"sinon-stub-promise": "^4.0.0"
}
},
"optionalDependencies": {}
}

View File

@@ -34,18 +34,16 @@ async function deleteAmplitudeData (userId, email) {
}
async function deleteHabiticaData (user, email) {
const truncatedEmail = email.slice(0, email.indexOf('@'));
const set = {
'auth.blocked': false,
'auth.local.hashed_password': '$2a$10$QDnNh1j1yMPnTXDEOV38xOePEWFd4X8DSYwAM8XTmqmacG5X0DKjW',
'auth.local.passwordHashMethod': 'bcrypt',
};
if (!user.auth.local.email) set['auth.local.email'] = `${user._id}@example.com`;
await User.updateOne(
if (!user.auth.local.email) set['auth.local.email'] = `${truncatedEmail}@example.com`;
await User.update(
{ _id: user._id },
{ $set: set },
);
// eslint-disable-next-line no-promise-executor-return
await new Promise(resolve => setTimeout(resolve, 1000));
const response = await axios.delete(
`${BASE_URL}/api/v3/user`,
{
@@ -83,7 +81,6 @@ async function processEmailAddress (email) {
$or: [
{ 'auth.facebook.emails.value': email },
{ 'auth.google.emails.value': email },
{ 'auth.apple.emails.value': email },
],
},
{ _id: 1, apiToken: 1, auth: 1 },
@@ -97,8 +94,6 @@ async function processEmailAddress (email) {
return console.log(`No users found with email address ${email}`);
}
// eslint-disable-next-line no-promise-executor-return
await new Promise(resolve => setTimeout(resolve, 1000));
return Promise.all(users.map(user => (async () => {
await deleteAmplitudeData(user._id, email); // eslint-disable-line no-await-in-loop
await deleteHabiticaData(user, email); // eslint-disable-line no-await-in-loop

View File

@@ -1,105 +0,0 @@
import forEach from 'lodash/forEach';
import { model as Group } from '../website/server/models/group';
import { model as User } from '../website/server/models/user';
import * as Tasks from '../website/server/models/task';
import { daysSince, shouldDo } from '../website/common/script/cron';
const TASK_VALUE_CHANGE_FACTOR = 0.9747;
const MIN_TASK_VALUE = -47.27;
async function updateTeamTasks (team) {
const toSave = [];
let teamLeader = await User.findOne({ _id: team.leader }, 'preferences').exec();
if (!teamLeader) { // why would this happen?
teamLeader = {
preferences: { }, // when options are sanitized this becomes CDS 0 at UTC
};
}
if (
!team.cron || !team.cron.lastProcessed
|| daysSince(team.cron.lastProcessed, teamLeader.preferences) > 0
) {
const tasks = await Tasks.Task.find({
'group.id': team._id,
userId: { $exists: false },
$or: [
{ type: 'todo', completed: false },
{ type: { $in: ['habit', 'daily'] } },
],
}).exec();
const tasksByType = {
habits: [], dailys: [], todos: [], rewards: [],
};
forEach(tasks, task => tasksByType[`${task.type}s`].push(task));
forEach(tasksByType.habits, habit => {
if (!(habit.up && habit.down) && habit.value !== 0) {
habit.value *= 0.5;
if (Math.abs(habit.value) < 0.1) habit.value = 0;
toSave.push(habit.save());
}
});
forEach(tasksByType.todos, todo => {
if (!todo.completed) {
const delta = TASK_VALUE_CHANGE_FACTOR ** todo.value;
todo.value -= delta;
if (todo.value < MIN_TASK_VALUE) todo.value = MIN_TASK_VALUE;
toSave.push(todo.save());
}
});
forEach(tasksByType.dailys, daily => {
let processChecklist = false;
let assignments = 0;
let completions = 0;
for (const assignedUser in daily.group.assignedUsersDetail) {
if (Object.prototype.hasOwnProperty.call(daily.group.assignedUsersDetail, assignedUser)) {
assignments += 1;
if (daily.group.assignedUsersDetail[assignedUser].completed) {
completions += 1;
daily.group.assignedUsersDetail[assignedUser].completed = false;
}
}
}
if (completions > 0) daily.markModified('group.assignedUsersDetail');
if (daily.completed) {
processChecklist = true;
daily.completed = false;
} else if (shouldDo(team.cron.lastProcessed, daily, teamLeader.preferences)) {
processChecklist = true;
const delta = TASK_VALUE_CHANGE_FACTOR ** daily.value;
if (assignments > 0) {
daily.value -= ((completions / assignments) * delta);
}
if (daily.value < MIN_TASK_VALUE) daily.value = MIN_TASK_VALUE;
}
daily.isDue = shouldDo(new Date(), daily, teamLeader.preferences);
if (processChecklist && daily.checklist.length > 0) {
daily.checklist.forEach(i => { i.completed = false; });
}
toSave.push(daily.save());
});
if (!team.cron) team.cron = {};
team.cron.lastProcessed = new Date();
toSave.push(team.save());
}
return Promise.all(toSave);
}
export default async function processTeamsCron () {
const activeTeams = await Group.find({
'purchased.plan.customerId': { $exists: true },
$or: [
{ 'purchased.plan.dateTerminated': { $exists: false } },
{ 'purchased.plan.dateTerminated': null },
{ 'purchased.plan.dateTerminated': { $gt: new Date() } },
],
}).exec();
const cronPromises = activeTeams.map(updateTeamTasks);
return Promise.all(cronPromises);
}

View File

@@ -1,5 +1,3 @@
/* eslint-disable import/no-commonjs */
module.exports = {
extends: [
'habitrpg/lib/mocha',
@@ -9,9 +7,6 @@ module.exports = {
chai: true,
expect: true,
sinon: true,
sandbox: true,
sandbox: true
},
rules: {
'import/no-extraneous-dependencies': 'off',
},
};
}

Some files were not shown because too many files have changed in this diff Show More