mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 22:57:21 +01:00
feat(event): April Foolin
This commit is contained in:
@@ -1,70 +1,11 @@
|
||||
import _ from 'lodash';
|
||||
import fsCallback from 'fs';
|
||||
import path from 'path';
|
||||
import util from 'util';
|
||||
import logger from '../../libs/logger';
|
||||
import nconf from 'nconf';
|
||||
import { langCodes } from '../../libs/i18n';
|
||||
import common from '../../../common';
|
||||
import { CONTENT_CACHE_PATH, getLocalizedContent } from '../../libs/content';
|
||||
|
||||
// Transform fs methods that accept callbacks in ones that return promises
|
||||
const fs = {
|
||||
readFile: util.promisify(fsCallback.readFile).bind(fsCallback),
|
||||
writeFile: util.promisify(fsCallback.writeFile).bind(fsCallback),
|
||||
stat: util.promisify(fsCallback.stat).bind(fsCallback),
|
||||
mkdir: util.promisify(fsCallback.mkdir).bind(fsCallback),
|
||||
};
|
||||
const IS_PROD = nconf.get('IS_PROD');
|
||||
|
||||
const api = {};
|
||||
|
||||
function walkContent (obj, lang) {
|
||||
_.each(obj, (item, key, source) => {
|
||||
if (_.isPlainObject(item) || _.isArray(item)) {
|
||||
walkContent(item, lang);
|
||||
} else if (_.isFunction(item) && item.i18nLangFunc) {
|
||||
source[key] = item(lang);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// After the getContent route is called the first time for a certain language
|
||||
// the response is saved on disk and subsequentially served
|
||||
// directly from there to reduce computation.
|
||||
// Example: if `cachedContentResponses.en` is true it means that the response is cached
|
||||
const cachedContentResponses = {};
|
||||
|
||||
// Language key set to true while the cache file is being written
|
||||
const cacheBeingWritten = {};
|
||||
|
||||
_.each(langCodes, code => {
|
||||
cachedContentResponses[code] = false;
|
||||
cacheBeingWritten[code] = false;
|
||||
});
|
||||
|
||||
|
||||
const CONTENT_CACHE_PATH = path.join(__dirname, '/../../../../content_cache/');
|
||||
|
||||
async function saveContentToDisk (language, content) {
|
||||
try {
|
||||
cacheBeingWritten[language] = true;
|
||||
|
||||
// check if the directory exists, if it doesn't an error is thrown
|
||||
await fs.stat(CONTENT_CACHE_PATH);
|
||||
await fs.writeFile(`${CONTENT_CACHE_PATH}${language}.json`, content, 'utf8');
|
||||
|
||||
cacheBeingWritten[language] = false;
|
||||
cachedContentResponses[language] = true;
|
||||
} catch (err) {
|
||||
// the directory doesn't exists, create it and retry
|
||||
if (err.code === 'ENOENT' && err.syscall === 'stat') {
|
||||
await fs.mkdir(CONTENT_CACHE_PATH);
|
||||
saveContentToDisk(language, content);
|
||||
} else {
|
||||
cacheBeingWritten[language] = false;
|
||||
logger.error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /api/v3/content Get all available content objects
|
||||
* @apiDescription Does not require authentication.
|
||||
@@ -118,33 +59,21 @@ api.getContent = {
|
||||
noLanguage: true,
|
||||
async handler (req, res) {
|
||||
let language = 'en';
|
||||
const proposedLang = req.query.language && req.query.language.toString();
|
||||
const proposedLang = req.query.language;
|
||||
|
||||
if (proposedLang in cachedContentResponses) {
|
||||
if (proposedLang && langCodes.includes(proposedLang)) {
|
||||
language = proposedLang;
|
||||
}
|
||||
|
||||
let content;
|
||||
if (IS_PROD) {
|
||||
res.sendFile(`${CONTENT_CACHE_PATH}${language}.json`);
|
||||
} else {
|
||||
res.set({
|
||||
'Content-Type': 'application/json',
|
||||
});
|
||||
|
||||
// is the content response for this language cached?
|
||||
if (cachedContentResponses[language] === true) {
|
||||
content = await fs.readFile(`${CONTENT_CACHE_PATH}${language}.json`, 'utf8');
|
||||
} else { // generate the response
|
||||
content = _.cloneDeep(common.content);
|
||||
walkContent(content, language);
|
||||
content = JSON.stringify(content);
|
||||
}
|
||||
|
||||
res.set({
|
||||
'Content-Type': 'application/json',
|
||||
});
|
||||
|
||||
const jsonResString = `{"success": true, "data": ${content}}`;
|
||||
res.status(200).send(jsonResString);
|
||||
|
||||
// save the file in background unless it's already cached or being written right now
|
||||
if (cachedContentResponses[language] !== true && cacheBeingWritten[language] !== true) {
|
||||
saveContentToDisk(language, content);
|
||||
const jsonResString = getLocalizedContent(language);
|
||||
res.status(200).send(jsonResString);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@ const api = {};
|
||||
|
||||
// @TODO export this const, cannot export it from here because only routes are exported from
|
||||
// controllers
|
||||
const LAST_ANNOUNCEMENT_TITLE = 'LAST CHANCE FOR MARCH SUBSCRIBER ITEMS AND HUG A BUG BUNDLE!';
|
||||
const LAST_ANNOUNCEMENT_TITLE = 'HAPPY APRIL FOOLS DAY! (ALSO SUBSCRIBER MYSTERY ITEMS)';
|
||||
const worldDmg = { // @TODO
|
||||
bailey: false,
|
||||
};
|
||||
@@ -31,27 +31,53 @@ api.getNews = {
|
||||
<div class="mr-3 ${baileyClass}"></div>
|
||||
<div class="media-body">
|
||||
<h1 class="align-self-center">${res.t('newStuff')}</h1>
|
||||
<h2>3/30/2020 - ${LAST_ANNOUNCEMENT_TITLE}</h2>
|
||||
<h2>4/1/2020 - ${LAST_ANNOUNCEMENT_TITLE}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="promo_mystery_202003 center-block"></div>
|
||||
<h3>Last Chance for Barbed Battler Set</h3>
|
||||
<div class="promo_april_fools_2020 center-block"></div>
|
||||
<h3>Dessert Pets!</h3>
|
||||
<p>
|
||||
Reminder: this is the last day to receive the Barbed Battler Set when you <a
|
||||
href='/user/settings/subscription'>sign up for a new Habitica subscription</a>!
|
||||
Subscribing also lets you buy Gems with Gold. The longer your subscription, the more Gems
|
||||
you can get!
|
||||
The April Fool rushes into the Tavern, clearly out of breath, with a gigantic picnic
|
||||
basket in tow. The heavenly smell of icing and chocolate wafts from the open basket into
|
||||
the crowd.
|
||||
</p>
|
||||
<p>
|
||||
"HABITICANS!" he cries. "I have heard you are all in need of some good cheer and comfort,
|
||||
and here I am! I hope you don't mind, in rather a turnaround from last year, I've brought
|
||||
along some treats to brighten your day."
|
||||
</p>
|
||||
<p>
|
||||
"Looks like this year he's turned our pets into desserts!" says Piyo. She picks up her
|
||||
new cinnamon bun companion gently. "Awww, they're so perfect and sweet!"
|
||||
</p>
|
||||
<p>
|
||||
Equipping different pets will show different desserts. Have fun discovering them all!
|
||||
</p>
|
||||
<div class="npc_aprilFool center-block"></div>
|
||||
<h3>Special April Fool's Social Media Challenge!</h3>
|
||||
<p>
|
||||
For even more fun, check out the <a href='/challenges/'>official Challenge</a> posted
|
||||
especially for today! Share your avatar featuring your new dessert pet on social media
|
||||
between now and April 3, and you'll have a chance to win Gems and have your avatar
|
||||
featured on the Habitica Blog!
|
||||
</p>
|
||||
<div class="small mb-3">by Beffymaroo, SabreCat, Piyo, and Viirus</div>
|
||||
<div class="promo_mystery_202004 center-block"></div>
|
||||
<h3>April Subscriber Items Revealed!</h3>
|
||||
<p>
|
||||
The April Subscriber Items have been revealed: the Majestic Monarch Item Set! <a
|
||||
href='/user/settings/subscription'>Subscribe to Habitica</a> by April 30 to receive this
|
||||
exciting set! 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 with 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>
|
||||
<p>Thanks so much for your support! You help keep Habitica running.</p>
|
||||
<div class="small mb-3">by Beffymaroo</div>
|
||||
<div class="promo_hugabug_bundle center-block"></div>
|
||||
<h3>Last Chance for Hug a Bug Pet Quest Bundle</h3>
|
||||
<p>
|
||||
This is also the final day to buy the discounted Hug a Bug Pet Quest Bundle, featuring
|
||||
the Snail, Beetle, and Butterfly quests, all for seven Gems! Be sure to take your bug-
|
||||
catching net over to the <a href='/shops/quests'>Quest Shop</a> before it bugs out!
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user