log armoire, quoest response and cron events to history

This commit is contained in:
Phillip Thelen
2024-08-22 15:20:02 +02:00
parent 62f5b9698a
commit 38cad7102f
7 changed files with 157 additions and 7 deletions

View File

@@ -19,6 +19,7 @@ import common from '../../../common';
import { sendNotification as sendPushNotification } from '../../libs/pushNotifications'; import { sendNotification as sendPushNotification } from '../../libs/pushNotifications';
import { apiError } from '../../libs/apiError'; import { apiError } from '../../libs/apiError';
import { questActivityWebhook } from '../../libs/webhook'; import { questActivityWebhook } from '../../libs/webhook';
import { model as UserHistory } from '../../models/userHistory';
const analytics = getAnalyticsServiceByEnvironment(); const analytics = getAnalyticsServiceByEnvironment();
@@ -227,6 +228,10 @@ api.acceptQuest = {
uuid: user._id, uuid: user._id,
headers: req.headers, headers: req.headers,
}); });
await UserHistory.beginUserHistoryUpdate(user._id)
.withQuestInviteResponse(group.quest.key, 'accept')
.commit();
}, },
}; };
@@ -288,6 +293,10 @@ api.rejectQuest = {
uuid: user._id, uuid: user._id,
headers: req.headers, headers: req.headers,
}); });
await UserHistory.beginUserHistoryUpdate(user._id)
.withQuestInviteResponse(group.quest.key, 'reject')
.commit();
}, },
}; };

View File

@@ -22,6 +22,7 @@ import {
} from '../../libs/email'; } from '../../libs/email';
import * as inboxLib from '../../libs/inbox'; import * as inboxLib from '../../libs/inbox';
import * as userLib from '../../libs/user'; import * as userLib from '../../libs/user';
import { model as UserHistory } from '../../models/userHistory';
const OFFICIAL_PLATFORMS = ['habitica-web', 'habitica-ios', 'habitica-android']; const OFFICIAL_PLATFORMS = ['habitica-web', 'habitica-ios', 'habitica-android'];
const TECH_ASSISTANCE_EMAIL = nconf.get('EMAILS_TECH_ASSISTANCE_EMAIL'); const TECH_ASSISTANCE_EMAIL = nconf.get('EMAILS_TECH_ASSISTANCE_EMAIL');
@@ -501,6 +502,13 @@ api.buy = {
const buyRes = await common.ops.buy(user, req, res.analytics); const buyRes = await common.ops.buy(user, req, res.analytics);
await user.save(); await user.save();
if (type === 'armoire') {
await UserHistory.beginUserHistoryUpdate(user._id)
.withArmoire(buyRes[0].armoire.dropKey || 'experience')
.commit();
}
res.respond(200, ...buyRes); res.respond(200, ...buyRes);
}, },
}; };
@@ -593,6 +601,9 @@ api.buyArmoire = {
} }
const buyArmoireResponse = await common.ops.buy(user, req, res.analytics); const buyArmoireResponse = await common.ops.buy(user, req, res.analytics);
await user.save(); await user.save();
await UserHistory.beginUserHistoryUpdate(user._id)
.withArmoire(buyArmoireResponse[1].data.armoire.dropKey)
.commit();
res.respond(200, ...buyArmoireResponse); res.respond(200, ...buyArmoireResponse);
}, },
}; };

View File

@@ -7,6 +7,7 @@ import common from '../../common';
import { preenUserHistory } from './preening'; import { preenUserHistory } from './preening';
import { sleep } from './sleep'; import { sleep } from './sleep';
import { revealMysteryItems } from './payments/subscriptions'; import { revealMysteryItems } from './payments/subscriptions';
import { model as UserHistory } from '../models/userHistory';
const CRON_SAFE_MODE = nconf.get('CRON_SAFE_MODE') === 'true'; const CRON_SAFE_MODE = nconf.get('CRON_SAFE_MODE') === 'true';
const CRON_SEMI_SAFE_MODE = nconf.get('CRON_SEMI_SAFE_MODE') === 'true'; const CRON_SEMI_SAFE_MODE = nconf.get('CRON_SEMI_SAFE_MODE') === 'true';
@@ -523,5 +524,9 @@ export async function cron (options = {}) {
user.flags.cronCount += 1; user.flags.cronCount += 1;
trackCronAnalytics(analytics, user, _progress, options); trackCronAnalytics(analytics, user, _progress, options);
await UserHistory.beginUserHistoryUpdate(user._id)
.withCron()
.commit();
return _progress; return _progress;
} }

View File

@@ -15,6 +15,9 @@ import {
import { import {
model as NewsPost, model as NewsPost,
} from '../newsPost'; } from '../newsPost';
import {
model as UserHistory,
} from '../userHistory';
import { // eslint-disable-line import/no-cycle import { // eslint-disable-line import/no-cycle
userActivityWebhook, userActivityWebhook,
} from '../../libs/webhook'; } from '../../libs/webhook';
@@ -237,7 +240,7 @@ schema.pre('validate', function preValidateUser (next) {
next(); next();
}); });
schema.pre('save', true, function preSaveUser (next, done) { schema.pre('save', true, async function preSaveUser (next, done) {
next(); next();
// VERY IMPORTANT NOTE: when only some fields from an user document are selected // VERY IMPORTANT NOTE: when only some fields from an user document are selected
@@ -360,6 +363,13 @@ schema.pre('save', true, function preSaveUser (next, done) {
// Unset the field so this is run only once // Unset the field so this is run only once
this.flags.lastWeeklyRecapDiscriminator = undefined; this.flags.lastWeeklyRecapDiscriminator = undefined;
} }
if (!this.flags.initializedUserHistory) {
this.flags.initializedUserHistory = true;
const history = UserHistory();
history.userId = this._id;
await history.save();
console.log('Initialized user history');
}
} }
// Enforce min/max values without displaying schema errors to end user // Enforce min/max values without displaying schema errors to end user
@@ -396,12 +406,9 @@ schema.pre('save', true, function preSaveUser (next, done) {
// Populate new users with default content // Populate new users with default content
if (this.isNew) { if (this.isNew) {
_setUpNewUser(this) await _setUpNewUser(this);
.then(() => done())
.catch(done);
} else {
done();
} }
done();
}); });
schema.pre('updateOne', function preUpdateUser () { schema.pre('updateOne', function preUpdateUser () {

View File

@@ -313,6 +313,7 @@ export const UserSchema = new Schema({
warnedLowHealth: { $type: Boolean, default: false }, warnedLowHealth: { $type: Boolean, default: false },
verifiedUsername: { $type: Boolean, default: false }, verifiedUsername: { $type: Boolean, default: false },
thirdPartyTools: { $type: Date }, thirdPartyTools: { $type: Date },
initializedUserHistory: { $type: Boolean, default: false },
}, },
history: { history: {

View File

@@ -0,0 +1,117 @@
import mongoose from 'mongoose';
import validator from 'validator';
import baseModel from '../libs/baseModel';
const { Schema } = mongoose;
export const schema = new Schema({
userId: {
$type: String,
ref: 'User',
required: true,
validate: [v => validator.isUUID(v), 'Invalid uuid for userhistory.'],
index: true,
unique: true,
},
armoire: [
{
_id: false,
timestamp: { $type: Date, required: true },
reward: { $type: String, required: true },
},
],
questInviteResponses: [
{
_id: false,
timestamp: { $type: Date, required: true },
quest: { $type: String, required: true },
response: { $type: String, required: true },
},
],
cron: [
{
_id: false,
timestamp: { $type: Date, required: true },
},
],
}, {
strict: true,
minimize: false, // So empty objects are returned
typeKey: '$type', // So that we can use fields named `type`
});
schema.plugin(baseModel, {
noSet: ['id', '_id', 'userId'],
timestamps: true,
_id: false, // using custom _id
});
export const model = mongoose.model('UserHistory', schema);
const commitUserHistoryUpdate = function commitUserHistoryUpdate (update) {
const data = {
$push: {
},
};
if (update.data.armoire.length) {
data.$push.armoire = {
$each: update.data.armoire,
$sort: { timestamp: -1 },
$slice: 10,
};
}
if (update.data.questInviteResponses.length) {
data.$push.questInviteResponses = {
$each: update.data.questInviteResponses,
$sort: { timestamp: -1 },
$slice: 10,
};
}
if (update.data.cron.length > 0) {
data.$push.cron = {
$each: update.data.cron,
$sort: { timestamp: -1 },
$slice: 10,
};
}
return model.updateOne(
{ userId: update.userId },
data,
).exec();
};
model.beginUserHistoryUpdate = function beginUserHistoryUpdate (userID) {
return {
userId: userID,
data: {
armoire: [],
questInviteResponses: [],
cron: [],
},
withArmoire: function withArmoire (reward) {
this.data.armoire.push({
timestamp: new Date(),
reward,
});
return this;
},
withQuestInviteResponse: function withQuestInviteResponse (quest, response) {
this.data.questInviteResponses.push({
timestamp: new Date(),
quest,
response,
});
return this;
},
withCron: function withCron () {
this.data.cron.push({
timestamp: new Date(),
});
return this;
},
commit: function commit () {
commitUserHistoryUpdate(this);
},
};
};