feat(content): mystery items 2018-11
110
migrations/archive/mystery-items-old.js
Normal file
@@ -0,0 +1,110 @@
|
||||
import monk from 'monk';
|
||||
import nconf from 'nconf';
|
||||
|
||||
const migrationName = 'mystery-items-201808.js'; // Update per month
|
||||
const authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
const authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done
|
||||
|
||||
/*
|
||||
* Award this month's mystery items to subscribers
|
||||
*/
|
||||
const MYSTERY_ITEMS = ['armor_mystery_201810', 'head_mystery_201810'];
|
||||
const CONNECTION_STRING = nconf.get('MIGRATION_CONNECT_STRING');
|
||||
|
||||
let dbUsers = monk(CONNECTION_STRING).get('users', { castIds: false });
|
||||
let UserNotification = require('../../website/server/models/userNotification').model;
|
||||
|
||||
function processUsers (lastId) {
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
let query = {
|
||||
migration: {$ne: migrationName},
|
||||
'purchased.plan.customerId': { $ne: null },
|
||||
$or: [
|
||||
{ 'purchased.plan.dateTerminated': { $gte: new Date() } },
|
||||
{ 'purchased.plan.dateTerminated': { $exists: false } },
|
||||
{ 'purchased.plan.dateTerminated': { $eq: null } },
|
||||
],
|
||||
};
|
||||
|
||||
if (lastId) {
|
||||
query._id = {
|
||||
$gt: lastId,
|
||||
};
|
||||
}
|
||||
|
||||
dbUsers.find(query, {
|
||||
sort: {_id: 1},
|
||||
limit: 250,
|
||||
fields: [
|
||||
], // 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++;
|
||||
|
||||
const addToSet = {
|
||||
'purchased.plan.mysteryItems': {
|
||||
$each: MYSTERY_ITEMS,
|
||||
},
|
||||
};
|
||||
const push = {
|
||||
notifications: (new UserNotification({
|
||||
type: 'NEW_MYSTERY_ITEMS',
|
||||
data: {
|
||||
MYSTERY_ITEMS,
|
||||
},
|
||||
})).toJSON(),
|
||||
};
|
||||
|
||||
dbUsers.update({_id: user._id}, {$addToSet: addToSet, $push: push});
|
||||
|
||||
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;
|
||||
@@ -1,70 +1,13 @@
|
||||
import monk from 'monk';
|
||||
import nconf from 'nconf';
|
||||
/* eslint-disable no-console */
|
||||
const MIGRATION_NAME = 'mystery_items_201811';
|
||||
const MYSTERY_ITEMS = ['head_mystery_201811', 'weapon_mystery_201811'];
|
||||
import { model as User } from '../../website/server/models/user';
|
||||
import { model as UserNotification } from '../../website/server/models/userNotification';
|
||||
|
||||
const migrationName = 'mystery-items-201808.js'; // Update per month
|
||||
const authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
const authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done
|
||||
|
||||
/*
|
||||
* Award this month's mystery items to subscribers
|
||||
*/
|
||||
const MYSTERY_ITEMS = ['armor_mystery_201810', 'head_mystery_201810'];
|
||||
const CONNECTION_STRING = nconf.get('MIGRATION_CONNECT_STRING');
|
||||
|
||||
let dbUsers = monk(CONNECTION_STRING).get('users', { castIds: false });
|
||||
let UserNotification = require('../../website/server/models/userNotification').model;
|
||||
|
||||
function processUsers (lastId) {
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
let query = {
|
||||
migration: {$ne: migrationName},
|
||||
'purchased.plan.customerId': { $ne: null },
|
||||
$or: [
|
||||
{ 'purchased.plan.dateTerminated': { $gte: new Date() } },
|
||||
{ 'purchased.plan.dateTerminated': { $exists: false } },
|
||||
{ 'purchased.plan.dateTerminated': { $eq: null } },
|
||||
],
|
||||
};
|
||||
|
||||
if (lastId) {
|
||||
query._id = {
|
||||
$gt: lastId,
|
||||
};
|
||||
}
|
||||
|
||||
dbUsers.find(query, {
|
||||
sort: {_id: 1},
|
||||
limit: 250,
|
||||
fields: [
|
||||
], // 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;
|
||||
const 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) {
|
||||
async function updateUser (user) {
|
||||
count++;
|
||||
|
||||
const addToSet = {
|
||||
@@ -80,31 +23,49 @@ function updateUser (user) {
|
||||
},
|
||||
})).toJSON(),
|
||||
};
|
||||
|
||||
dbUsers.update({_id: user._id}, {$addToSet: addToSet, $push: push});
|
||||
const set = {
|
||||
migration: MIGRATION_NAME,
|
||||
};
|
||||
|
||||
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||
if (user._id === authorUuid) console.warn(`${authorName } processed`);
|
||||
|
||||
return await User.update({_id: user._id}, {$set: set, $push: push, $addToSet: addToSet}).exec();
|
||||
}
|
||||
|
||||
function displayData () {
|
||||
module.exports = async function processUsers () {
|
||||
let query = {
|
||||
migration: {$ne: MIGRATION_NAME},
|
||||
'purchased.plan.customerId': { $ne: null },
|
||||
$or: [
|
||||
{ 'purchased.plan.dateTerminated': { $gte: new Date() } },
|
||||
{ 'purchased.plan.dateTerminated': { $exists: false } },
|
||||
{ 'purchased.plan.dateTerminated': { $eq: null } },
|
||||
],
|
||||
};
|
||||
|
||||
const fields = {
|
||||
_id: 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`);
|
||||
return exiting(0);
|
||||
}
|
||||
|
||||
function exiting (code, msg) {
|
||||
code = code || 0; // 0 = success
|
||||
if (code && !msg) {
|
||||
msg = 'ERROR!';
|
||||
}
|
||||
if (msg) {
|
||||
if (code) {
|
||||
console.error(msg);
|
||||
break;
|
||||
} else {
|
||||
console.log(msg);
|
||||
}
|
||||
}
|
||||
process.exit(code);
|
||||
query._id = {
|
||||
$gt: users[users.length - 1],
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = processUsers;
|
||||
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
};
|
||||
|
||||
@@ -313,6 +313,8 @@
|
||||
"weaponMystery201611Notes": "All manner of delicious and wholesome foods spill forth from this horn. Enjoy the feast! Confers no benefit. November 2016 Subscriber Item.",
|
||||
"weaponMystery201708Text": "Lava Sword",
|
||||
"weaponMystery201708Notes": "The fiery glow of this sword will make quick work of even dark red Tasks! Confers no benefit. August 2017 Subscriber Item.",
|
||||
"weaponMystery201811Text": "Splendid Sorcerer's Staff",
|
||||
"weaponMystery201811Notes": "This magical stave is as powerful as it is elegant. Confers no benefit. November 2018 Subscriber Item.",
|
||||
"weaponMystery301404Text": "Steampunk Cane",
|
||||
"weaponMystery301404Notes": "Excellent for taking a turn about town. March 3015 Subscriber Item. Confers no benefit.",
|
||||
|
||||
@@ -1209,6 +1211,8 @@
|
||||
"headMystery201809Notes": "The last flowers of autumn's warm days are a reminder of the beauty of the season. Confers no benefit. September 2018 Subscriber Item.",
|
||||
"headMystery201810Text": "Dark Forest Helm",
|
||||
"headMystery201810Notes": "If you find yourself traveling through a spooky place, the glowing red eyes of this helm will surely scare away any enemies in your path. Confers no benefit. October 2018 Subscriber Item.",
|
||||
"headMystery201811Text": "Splendid Sorcerer's Hat",
|
||||
"headMystery201811Notes": "Wear this feathered hat to stand out at even the fanciest wizardly gatherings! Confers no benefit. November 2018 Subscriber Item.",
|
||||
"headMystery301404Text": "Fancy Top Hat",
|
||||
"headMystery301404Notes": "A fancy top hat for the finest of gentlefolk! January 3015 Subscriber Item. Confers no benefit.",
|
||||
"headMystery301405Text": "Basic Top Hat",
|
||||
|
||||
@@ -150,6 +150,7 @@
|
||||
"mysterySet201808": "Lava Dragon Set",
|
||||
"mysterySet201809": "Autumnal Armor Set",
|
||||
"mysterySet201810": "Dark Forest Set",
|
||||
"mysterySet201811": "Splendid Sorcerer Set",
|
||||
"mysterySet301404": "Steampunk Standard Set",
|
||||
"mysterySet301405": "Steampunk Accessories Set",
|
||||
"mysterySet301703": "Peacock Steampunk Set",
|
||||
|
||||
@@ -643,6 +643,12 @@ let head = {
|
||||
mystery: '201810',
|
||||
value: 0,
|
||||
},
|
||||
201811: {
|
||||
text: t('headMystery201811Text'),
|
||||
notes: t('headMystery201811Notes'),
|
||||
mystery: '201811',
|
||||
value: 0,
|
||||
},
|
||||
301404: {
|
||||
text: t('headMystery301404Text'),
|
||||
notes: t('headMystery301404Notes'),
|
||||
@@ -796,6 +802,12 @@ let weapon = {
|
||||
mystery: '201708',
|
||||
value: 0,
|
||||
},
|
||||
201811: {
|
||||
text: t('weaponMystery201811Text'),
|
||||
notes: t('weaponMystery201811Notes'),
|
||||
mystery: '201811',
|
||||
value: 0,
|
||||
},
|
||||
301404: {
|
||||
text: t('weaponMystery301404Text'),
|
||||
notes: t('weaponMystery301404Notes'),
|
||||
|
||||
@@ -230,6 +230,10 @@ let mysterySets = {
|
||||
start: '2018-10-25',
|
||||
end: '2018-11-02',
|
||||
},
|
||||
201811: {
|
||||
start: '2018-11-28',
|
||||
end: '2018-12-4',
|
||||
},
|
||||
301404: {
|
||||
start: '3014-03-24',
|
||||
end: '3014-04-02',
|
||||
|
||||
|
After Width: | Height: | Size: 1006 B |
|
After Width: | Height: | Size: 931 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 465 B |
|
After Width: | Height: | Size: 508 B |
|
Before Width: | Height: | Size: 26 KiB |
BIN
website/raw_sprites/spritesmith_large/promo_mystery_201811.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
website/raw_sprites/spritesmith_large/promo_piyo.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
@@ -3,7 +3,7 @@ import { authWithHeaders } from '../../middlewares/auth';
|
||||
let api = {};
|
||||
|
||||
// @TODO export this const, cannot export it from here because only routes are exported from controllers
|
||||
const LAST_ANNOUNCEMENT_TITLE = 'HAPPY THANKSGIVING!';
|
||||
const LAST_ANNOUNCEMENT_TITLE = 'NOVEMBER SUBSCRIBER ITEMS AND STAFF SPOTLIGHT!';
|
||||
const worldDmg = { // @TODO
|
||||
bailey: false,
|
||||
};
|
||||
@@ -30,17 +30,19 @@ api.getNews = {
|
||||
<div class="mr-3 ${baileyClass}"></div>
|
||||
<div class="media-body">
|
||||
<h1 class="align-self-center">${res.t('newStuff')}</h1>
|
||||
<h2>11/22/2018 - ${LAST_ANNOUNCEMENT_TITLE}</h2>
|
||||
<h2>11/28/2018 - ${LAST_ANNOUNCEMENT_TITLE}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="npc_matt center-block"></div>
|
||||
<p>It's Thanksgiving in Habitica! On this day Habiticans celebrate by spending time with loved ones, giving thanks, and riding their glorious turkeys into the magnificent sunset. Some of the NPCs are celebrating the occasion!</p>
|
||||
<h3>Turkey Pet, Mount, and Costumes!</h3>
|
||||
<p>In celebration of Turkey Day, everyone has received an adorable Turkey! What kind of Turkey? It all depends on how many Habitica Thanksgivings you've celebrated with us. Each Thanksgiving, you'll get a new and exciting Turkey variety!</p>
|
||||
<p>Thank you for using Habitica - we really love you all <3</p>
|
||||
<div class="small mb-3">by Lemoness and Beffymaroo</div>
|
||||
<div class="promo_turkey_day_2018 center-block"></div>
|
||||
<div class="promo_mystery_201811 center-block"></div>
|
||||
<h3>November Subscriber Items Revealed!</h3>
|
||||
<p>The November Subscriber Items have been revealed: the Splendid Sorcerer Item Set! You'll receive this item set when you <a href='/user/settings/subscription' target='_blank'>subscribe</a> by December 3. If you're already an active subscriber, reload the site and then head to Inventory > Items to claim your gear!</p>
|
||||
<p>Subscribers also receive the ability to buy Gems for Gold -- the longer you subscribe, the more Gems you can buy per month! There are other perks as well, such as longer access to uncompressed data and a cute Jackalope pet. Best of all, subscriptions let us keep Habitica running. Thank you very much for your support -- it means a lot to us.</p>
|
||||
<div class="small mb-3">by Beffymaroo</div>
|
||||
<div class="promo_piyo center-block"></div>
|
||||
<h3>Staff Spotlight: Sara, aka Piyo</h3>
|
||||
<p>There's a new <a href='https://habitica.wordpress.com/2018/11/20/staff-spotlight-sara-aka-piyo/' target='_blank'>Staff Spotlight</a> on the blog! Come meet Sara, aka Piyo, and learn how our favorite onesie-wearing cryptid balances her design work for Habitica with her enthusiasm for baking and video games.</p>
|
||||
<div class="small mb-3">by Beffymaroo</div>
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
|
||||