Implement new scheduling for time travelers shop

This commit is contained in:
Phillip Thelen
2024-01-31 17:27:03 +01:00
committed by Sabe Jones
parent ce796fa1d9
commit 278d9b74f9
6 changed files with 378 additions and 224 deletions

View File

@@ -6,23 +6,105 @@ import timeTravelers from '../../website/common/script/content/time-travelers';
describe('time-travelers store', () => { describe('time-travelers store', () => {
let user; let user;
let date;
beforeEach(() => { beforeEach(() => {
user = generateUser(); user = generateUser();
}); });
it('removes owned sets from the time travelers store', () => { describe('on january 15th', () => {
user.items.gear.owned.head_mystery_201602 = true; // eslint-disable-line camelcase beforeEach(() => {
expect(timeTravelers.timeTravelerStore(user)['201602']).to.not.exist; date = new Date('2024-01-15');
expect(timeTravelers.timeTravelerStore(user)['201603']).to.exist; });
it('returns the correct gear', () => {
const items = timeTravelers.timeTravelerStore(user, date);
for (const [key] of Object.entries(items)) {
if (key.startsWith('20')) {
expect(key).to.match(/20[0-9]{2}(01|07)/);
}
}
});
it('removes owned sets from the time travelers store', () => {
user.items.gear.owned.head_mystery_201601 = true; // eslint-disable-line camelcase
const items = timeTravelers.timeTravelerStore(user, date);
expect(items['201601']).to.not.exist;
expect(items['201801']).to.exist;
expect(items['202207']).to.exist;
});
it('removes unopened mystery item sets from the time travelers store', () => {
user.purchased = {
plan: {
mysteryItems: ['head_mystery_201601'],
},
};
const items = timeTravelers.timeTravelerStore(user, date);
expect(items['201601']).to.not.exist;
expect(items['201607']).to.exist;
});
}); });
it('removes unopened mystery item sets from the time travelers store', () => { describe('on may 1st', () => {
user.purchased = { beforeEach(() => {
plan: { date = new Date('2024-05-01');
mysteryItems: ['head_mystery_201602'], });
}, it('returns the correct gear', () => {
}; const items = timeTravelers.timeTravelerStore(user, date);
expect(timeTravelers.timeTravelerStore(user)['201602']).to.not.exist; for (const [key] of Object.entries(items)) {
expect(timeTravelers.timeTravelerStore(user)['201603']).to.exist; if (key.startsWith('20')) {
expect(key).to.match(/20[0-9]{2}(05|11)/);
}
}
});
it('removes owned sets from the time travelers store', () => {
user.items.gear.owned.head_mystery_201705 = true; // eslint-disable-line camelcase
const items = timeTravelers.timeTravelerStore(user, date);
expect(items['201705']).to.not.exist;
expect(items['201805']).to.exist;
expect(items['202211']).to.exist;
});
it('removes unopened mystery item sets from the time travelers store', () => {
user.purchased = {
plan: {
mysteryItems: ['head_mystery_201705'],
},
};
const items = timeTravelers.timeTravelerStore(user, date);
expect(items['201705']).to.not.exist;
expect(items['201611']).to.exist;
});
});
describe('on october 21st', () => {
beforeEach(() => {
date = new Date('2024-10-21');
});
it('returns the correct gear', () => {
const items = timeTravelers.timeTravelerStore(user, date);
for (const [key] of Object.entries(items)) {
if (key.startsWith('20')) {
expect(key).to.match(/20[0-9]{2}(10|04)/);
}
}
});
it('removes owned sets from the time travelers store', () => {
user.items.gear.owned.head_mystery_201810 = true; // eslint-disable-line camelcase
user.items.gear.owned.armor_mystery_201810 = true; // eslint-disable-line camelcase
const items = timeTravelers.timeTravelerStore(user, date);
expect(items['201810']).to.not.exist;
expect(items['201910']).to.exist;
expect(items['202204']).to.exist;
});
it('removes unopened mystery item sets from the time travelers store', () => {
user.purchased = {
plan: {
mysteryItems: ['armor_mystery_201710'],
},
};
const items = timeTravelers.timeTravelerStore(user, date);
expect(items['201710']).to.not.exist;
expect(items['201604']).to.exist;
});
}); });
}); });

View File

@@ -1,18 +1,26 @@
import moment from 'moment'; import moment from 'moment';
function backgroundMatcher(month, oddYear) { function backgroundMatcher (month1, month2, oddYear) {
return function (background) { return function call (key) {
const key = background.set.key; if (!key.startsWith('backgrounds')) return true;
const keyLength = key.length; const keyLength = key.length;
return parseInt(key.substring(keyLength-6, keyLength-4)) === month && parseInt(key.subtring(keyLength-2, keyLength)) % 2 === oddYear; const month = parseInt(key.substring(keyLength - 6, keyLength - 4), 10);
} return (month === month1 || month === month2)
&& parseInt(key.substring(keyLength - 2, keyLength), 10) % 2 === (oddYear ? 1 : 0);
};
} }
function timeTravelersMatcher(month1, month2) { function timeTravelersMatcher (month1, month2) {
return function (item) { return function call (item) {
console.log(item, month1, month2) const itemMonth = parseInt(item.substring(4, 6), 10);
return item; return itemMonth === month1 || itemMonth === month2;
} };
}
function inListMatcher (list) {
return function call (item) {
return list.indexOf(item) !== -1;
};
} }
export const FIRST_RELEASE_DAY = 1; export const FIRST_RELEASE_DAY = 1;
@@ -21,204 +29,262 @@ export const THIRD_RELEASE_DAY = 14;
export const FOURTH_RELEASE_DAY = 21; export const FOURTH_RELEASE_DAY = 21;
export const MONTHLY_SCHEDULE = { export const MONTHLY_SCHEDULE = {
0: { 0: {
[FIRST_RELEASE_DAY]: [ [FIRST_RELEASE_DAY]: [
{ {
"type": "timeTravelers", type: 'timeTravelers',
"matcher": timeTravelersMatcher(1, 7), matcher: timeTravelersMatcher(1, 7),
} },
], ],
[SECOND_RELEASE_DAY]: [ [SECOND_RELEASE_DAY]: [
], {
[THIRD_RELEASE_DAY]: [ type: 'backgrounds',
], matcher: backgroundMatcher(1, 7, false),
[FOURTH_RELEASE_DAY]: [ },
], ],
}, [THIRD_RELEASE_DAY]: [
1: { {
[FIRST_RELEASE_DAY]: [ type: 'petQuests',
{ matcher: inListMatcher([
"type": "timeTravelers", 'ghost_stag',
"matcher": timeTravelersMatcher(2, 8), 'trex',
} 'harpy',
], 'sabretooth',
[SECOND_RELEASE_DAY]: [ 'dolphin',
], ]),
[THIRD_RELEASE_DAY]: [ },
], ],
[FOURTH_RELEASE_DAY]: [ [FOURTH_RELEASE_DAY]: [
], ],
}, },
2: { 1: {
[FIRST_RELEASE_DAY]: [ [FIRST_RELEASE_DAY]: [
{ {
"type": "timeTravelers", type: 'timeTravelers',
"matcher": timeTravelersMatcher(3, 9), matcher: timeTravelersMatcher(2, 8),
} },
], ],
[SECOND_RELEASE_DAY]: [ [SECOND_RELEASE_DAY]: [
], {
[THIRD_RELEASE_DAY]: [ type: 'backgrounds',
], matcher: backgroundMatcher(2, 8, false),
[FOURTH_RELEASE_DAY]: [ },
], ],
}, [THIRD_RELEASE_DAY]: [
3: { ],
[FIRST_RELEASE_DAY]: [ [FOURTH_RELEASE_DAY]: [
{ ],
"type": "timeTravelers", },
"matcher": timeTravelersMatcher(4, 10), 2: {
} [FIRST_RELEASE_DAY]: [
], {
[SECOND_RELEASE_DAY]: [ type: 'timeTravelers',
], matcher: timeTravelersMatcher(3, 9),
[THIRD_RELEASE_DAY]: [ },
], ],
[FOURTH_RELEASE_DAY]: [ [SECOND_RELEASE_DAY]: [
], {
}, type: 'backgrounds',
4: { matcher: backgroundMatcher(3, 9, false),
[FIRST_RELEASE_DAY]: [ },
{ ],
"type": "timeTravelers", [THIRD_RELEASE_DAY]: [
"matcher": timeTravelersMatcher(5, 11), ],
} [FOURTH_RELEASE_DAY]: [
], ],
[SECOND_RELEASE_DAY]: [ },
], 3: {
[THIRD_RELEASE_DAY]: [ [FIRST_RELEASE_DAY]: [
], {
[FOURTH_RELEASE_DAY]: [ type: 'timeTravelers',
], matcher: timeTravelersMatcher(4, 10),
}, },
5: { ],
[FIRST_RELEASE_DAY]: [ [SECOND_RELEASE_DAY]: [
{ {
"type": "timeTravelers", type: 'backgrounds',
"matcher": timeTravelersMatcher(6, 12), matcher: backgroundMatcher(4, 10, false),
} },
], ],
[SECOND_RELEASE_DAY]: [ [THIRD_RELEASE_DAY]: [
], ],
[THIRD_RELEASE_DAY]: [ [FOURTH_RELEASE_DAY]: [
], ],
[FOURTH_RELEASE_DAY]: [ },
], 4: {
}, [FIRST_RELEASE_DAY]: [
6: { {
[FIRST_RELEASE_DAY]: [ type: 'timeTravelers',
{ matcher: timeTravelersMatcher(5, 11),
"type": "timeTravelers", },
"matcher": timeTravelersMatcher(7, 1), ],
} [SECOND_RELEASE_DAY]: [
], {
[SECOND_RELEASE_DAY]: [ type: 'backgrounds',
], matcher: backgroundMatcher(5, 11, false),
[THIRD_RELEASE_DAY]: [ },
], ],
[FOURTH_RELEASE_DAY]: [ [THIRD_RELEASE_DAY]: [
], ],
}, [FOURTH_RELEASE_DAY]: [
7: { ],
[FIRST_RELEASE_DAY]: [ },
{ 5: {
"type": "timeTravelers", [FIRST_RELEASE_DAY]: [
"matcher": timeTravelersMatcher(8, 2), {
} type: 'timeTravelers',
], matcher: timeTravelersMatcher(6, 12),
[SECOND_RELEASE_DAY]: [ },
], ],
[THIRD_RELEASE_DAY]: [ [SECOND_RELEASE_DAY]: [
], {
[FOURTH_RELEASE_DAY]: [ type: 'backgrounds',
], matcher: backgroundMatcher(6, 12, false),
}, },
8: { ],
[FIRST_RELEASE_DAY]: [ [THIRD_RELEASE_DAY]: [
{ ],
"type": "timeTravelers", [FOURTH_RELEASE_DAY]: [
"matcher": timeTravelersMatcher(9, 3), ],
} },
], 6: {
[SECOND_RELEASE_DAY]: [ [FIRST_RELEASE_DAY]: [
], {
[THIRD_RELEASE_DAY]: [ type: 'timeTravelers',
], matcher: timeTravelersMatcher(7, 1),
[FOURTH_RELEASE_DAY]: [ },
], ],
}, [SECOND_RELEASE_DAY]: [
9: { {
[FIRST_RELEASE_DAY]: [ type: 'backgrounds',
{ matcher: backgroundMatcher(7, 1, true),
"type": "timeTravelers", },
"matcher": timeTravelersMatcher(10, 4), ],
} [THIRD_RELEASE_DAY]: [
], ],
[SECOND_RELEASE_DAY]: [ [FOURTH_RELEASE_DAY]: [
], ],
[THIRD_RELEASE_DAY]: [ },
], 7: {
[FOURTH_RELEASE_DAY]: [ [FIRST_RELEASE_DAY]: [
], {
}, type: 'timeTravelers',
10: { matcher: timeTravelersMatcher(8, 2),
[FIRST_RELEASE_DAY]: [ },
{ ],
"type": "timeTravelers", [SECOND_RELEASE_DAY]: [
"matcher": timeTravelersMatcher(11, 5), {
} type: 'backgrounds',
], matcher: backgroundMatcher(8, 2, true),
[SECOND_RELEASE_DAY]: [ },
], ],
[THIRD_RELEASE_DAY]: [ [THIRD_RELEASE_DAY]: [
], ],
[FOURTH_RELEASE_DAY]: [ [FOURTH_RELEASE_DAY]: [
], ],
}, },
11: { 8: {
[FIRST_RELEASE_DAY]: [ [FIRST_RELEASE_DAY]: [
{ {
"type": "timeTravelers", type: 'timeTravelers',
"matcher": timeTravelersMatcher(12, 6), matcher: timeTravelersMatcher(9, 3),
} },
], ],
[SECOND_RELEASE_DAY]: [ [SECOND_RELEASE_DAY]: [
], {
[THIRD_RELEASE_DAY]: [ type: 'backgrounds',
], matcher: backgroundMatcher(9, 3, true),
[FOURTH_RELEASE_DAY]: [ },
], ],
}, [THIRD_RELEASE_DAY]: [
],
[FOURTH_RELEASE_DAY]: [
],
},
9: {
[FIRST_RELEASE_DAY]: [
{
type: 'timeTravelers',
matcher: timeTravelersMatcher(10, 4),
},
],
[SECOND_RELEASE_DAY]: [
{
type: 'backgrounds',
matcher: backgroundMatcher(10, 4, true),
},
],
[THIRD_RELEASE_DAY]: [
],
[FOURTH_RELEASE_DAY]: [
],
},
10: {
[FIRST_RELEASE_DAY]: [
{
type: 'timeTravelers',
matcher: timeTravelersMatcher(11, 5),
},
],
[SECOND_RELEASE_DAY]: [
{
type: 'backgrounds',
matcher: backgroundMatcher(11, 5, true),
},
],
[THIRD_RELEASE_DAY]: [
],
[FOURTH_RELEASE_DAY]: [
],
},
11: {
[FIRST_RELEASE_DAY]: [
{
type: 'timeTravelers',
matcher: timeTravelersMatcher(12, 6),
},
],
[SECOND_RELEASE_DAY]: [
{
type: 'backgrounds',
matcher: backgroundMatcher(12, 6, true),
},
],
[THIRD_RELEASE_DAY]: [
],
[FOURTH_RELEASE_DAY]: [
],
},
}; };
export const GALA_SWITCHOVER_DAY = 21; export const GALA_SWITCHOVER_DAY = 21;
export const GALA_SCHEDULE = { export const GALA_SCHEDULE = {
0: [], 0: [],
1: [], 1: [],
2: [], 2: [],
3: [], 3: [],
}; };
export function assembleScheduledMatchers(date) { export function assembleScheduledMatchers (date) {
const items = []; const items = [];
const month = date instanceof moment ? date.month() : date.getMonth(); const month = date instanceof moment ? date.month() : date.getMonth();
const todayDay = date instanceof moment ? date.date() : date.getDate(); const todayDay = date instanceof moment ? date.date() : date.getDate();
const previousMonth = month === 0 ? 11 : month - 1; const previousMonth = month === 0 ? 11 : month - 1;
for (const [day, value] of Object.entries(MONTHLY_SCHEDULE[previousMonth])) { for (const [day, value] of Object.entries(MONTHLY_SCHEDULE[previousMonth])) {
if (day > todayDay) { if (day > todayDay) {
items.push(...value); items.push(...value);
}
} }
for (const [day, value] of Object.entries(MONTHLY_SCHEDULE[month])) { }
if (day <= todayDay) { for (const [day, value] of Object.entries(MONTHLY_SCHEDULE[month])) {
items.push(...value); if (day <= todayDay) {
} items.push(...value);
} }
let galaMonth = month; }
const galaCount = Object.keys(GALA_SCHEDULE).length; let galaMonth = month;
if (todayDay >= GALA_SWITCHOVER_DAY) { const galaCount = Object.keys(GALA_SCHEDULE).length;
galaMonth += 1; if (todayDay >= GALA_SWITCHOVER_DAY) {
} galaMonth += 1;
items.push(...GALA_SCHEDULE[parseInt((galaCount / 12) * galaMonth)]); }
return items; items.push(...GALA_SCHEDULE[parseInt((galaCount / 12) * galaMonth, 10)]);
return items;
} }

View File

@@ -33,6 +33,8 @@ import gemsBlock from './gems';
import faq from './faq'; import faq from './faq';
import timeTravelers from './time-travelers'; import timeTravelers from './time-travelers';
import { assembleScheduledMatchers } from './constants/schedule';
import loginIncentives from './loginIncentives'; import loginIncentives from './loginIncentives';
import officialPinnedItems from './officialPinnedItems'; import officialPinnedItems from './officialPinnedItems';
@@ -728,4 +730,6 @@ api.faq = faq;
api.loginIncentives = loginIncentives(api); api.loginIncentives = loginIncentives(api);
api.assembleScheduledMatchers = assembleScheduledMatchers;
export default api; export default api;

View File

@@ -7,6 +7,7 @@ import moment from 'moment';
import mysterySets from './mystery-sets'; import mysterySets from './mystery-sets';
import gear from './gear'; import gear from './gear';
import { assembleScheduledMatchers } from './constants/schedule';
const mystery = mysterySets; const mystery = mysterySets;
@@ -17,7 +18,8 @@ each(mystery, (v, k) => {
if (v.items.length === 0) delete mystery[k]; if (v.items.length === 0) delete mystery[k];
}); });
const timeTravelerStore = user => { const timeTravelerStore = (user, date) => {
const availabilityMatchers = assembleScheduledMatchers(date).filter(matcher => matcher.type === 'timeTravelers').map(matcher => matcher.matcher);
let ownedKeys; let ownedKeys;
const { owned } = user.items.gear; const { owned } = user.items.gear;
const { mysteryItems } = user.purchased.plan; const { mysteryItems } = user.purchased.plan;
@@ -26,13 +28,13 @@ const timeTravelerStore = user => {
ownedKeys = union(ownedKeys, unopenedGifts); ownedKeys = union(ownedKeys, unopenedGifts);
return reduce(mystery, (m, v, k) => { return reduce(mystery, (m, v, k) => {
if ( if (
k === 'wondercon' k !== 'wondercon'
|| ownedKeys.indexOf(v.items[0].key) !== -1 && ownedKeys.indexOf(v.items[0].key) === -1
|| (moment(k).add(1, 'months').isAfter() && moment(k).isBefore('3000-01-01')) && (moment(k).isAfter('3000-01-01')
|| availabilityMatchers.map(matcher => matcher(k)).every(matcher => matcher === true))
) { ) {
return m; m[k] = v;
} }
m[k] = v;
return m; return m;
}, {}); }, {});
}; };

View File

@@ -369,7 +369,7 @@ shops.getTimeTravelersCategories = function getTimeTravelersCategories (user, la
} }
} }
const sets = content.timeTravelerStore(user); const sets = content.timeTravelerStore(user, new Date());
for (const setKey of Object.keys(sets)) { for (const setKey of Object.keys(sets)) {
const set = sets[setKey]; const set = sets[setKey];
const category = { const category = {

View File

@@ -20,7 +20,7 @@ export default async function buyMysterySet (user, req = {}, analytics) {
throw new NotAuthorized(i18n.t('notEnoughHourglasses', req.language)); throw new NotAuthorized(i18n.t('notEnoughHourglasses', req.language));
} }
const ref = content.timeTravelerStore(user); const ref = content.timeTravelerStore(user, new Date());
const mysterySet = ref ? ref[key] : undefined; const mysterySet = ref ? ref[key] : undefined;
if (!mysterySet) { if (!mysterySet) {