diff --git a/app/imports/api/creature/log/CreatureLogs.js b/app/imports/api/creature/log/CreatureLogs.js index 8d6d9e6b..4474ee69 100644 --- a/app/imports/api/creature/log/CreatureLogs.js +++ b/app/imports/api/creature/log/CreatureLogs.js @@ -1,4 +1,11 @@ import SimpleSchema from 'simpl-schema'; +import Creatures from '/imports/api/creature/Creatures.js'; +import { ValidatedMethod } from 'meteor/mdg:validated-method'; +import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; +import {assertEditPermission} from '/imports/api/creature/creaturePermissions.js'; +import { parse, CompilationContext } from '/imports/parser/parser.js'; +const PER_CREATURE_LOG_LIMIT = 100; + if (Meteor.isServer){ var sendWebhookAsCreature = require('/imports/server/discord/sendWebhook.js').sendWebhookAsCreature; } @@ -36,11 +43,28 @@ CreatureLogs.attachSchema(CreatureLogSchema); // This function should only be called by trusted code. No permission checks const insertCreatureLog = function({log, creature}){ + const creatureId = creature._id; + // Build the new log if (typeof log === 'string'){ log = {text: log}; } - log.creatureId = creature._id; + log.creatureId = creatureId; + // Insert it let id = CreatureLogs.insert(log); + // Find the first log that is over the limit + let firstExpiredLog = CreatureLogs.find({ + creatureId + }, { + sort: {date: -1}, + skip: PER_CREATURE_LOG_LIMIT, + }); + // Remove all logs older than the one over the limit + CreatureLogs.remove({ + creatureId, + date: {$lte: firstExpiredLog.date}, + }); + //TODO unblock before sending webhooks + // Send webhooks if (Meteor.isServer){ sendWebhookAsCreature({ creature, @@ -50,5 +74,55 @@ const insertCreatureLog = function({log, creature}){ return id; }; +function equalIgnoringWhitespace(a, b){ + if (typeof a !== 'string' || typeof b !== 'string') return a === b; + return a.replace(/\s/g,'') === b.replace(/\s/g, ''); +} + +const logRoll = new ValidatedMethod({ + name: 'creatureLogs.methods.logForCreature', + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 5, + timeInterval: 5000, + }, + validate: new SimpleSchema({ + roll: { + type: String, + }, + creatureId: { + type: String, + regEx: SimpleSchema.RegEx.Id, + }, + }).validator(), + run({roll, creatureId}){ + const creature = Creatures.findOne(creatureId); + assertEditPermission(creature, this.userId); + let parsedResult = parse(roll); + let log; + if (parsedResult === null) { + log = 'Unexpected end of input'; + } + else try { + let logText = []; + let rollContext = new CompilationContext(); + let compiled = parsedResult.compile(creature.variables, rollContext); + let compiledString = compiled.toString(); + if (!equalIgnoringWhitespace(compiledString, roll)) logText.push(roll); + logText.push(compiledString); + let rolled = compiled.roll(creature.variables, rollContext); + let rolledString = rolled.toString(); + if (rolledString !== compiledString) logText.push(rolled.toString()); + let result = rolled.reduce(creature.variables, rollContext); + let resultString = result.toString(); + if (resultString !== rolledString) logText.push(resultString); + log = logText.join('\n\n'); + } catch (e){ + log = 'Calculation error'; + } + return insertCreatureLog({log, creature}); + }, +}); + export default CreatureLogs; -export { CreatureLogSchema, insertCreatureLog}; +export { CreatureLogSchema, insertCreatureLog, logRoll}; diff --git a/app/imports/ui/creature/character/CharacterSheet.vue b/app/imports/ui/creature/character/CharacterSheet.vue index c3eaaa38..cb6c3734 100644 --- a/app/imports/ui/creature/character/CharacterSheet.vue +++ b/app/imports/ui/creature/character/CharacterSheet.vue @@ -64,7 +64,7 @@ {{ snackbar.text }}
-
-
+ - {{ log.text }} -
+ + + +
-