Add Roll Distribution flag
This commit is contained in:
parent
308f897eb7
commit
babc57497e
|
@ -5,7 +5,7 @@ meta {
|
|||
}
|
||||
|
||||
get {
|
||||
url: http://localhost:8166/api/roll?user=[discord-user-id]&channel=[discord-channel-id]&rollstr=[artificer-roll-cmd]&documentation=All items below are optional. Flags do not need values.&nd=[no-details-flag]&snd=[super-no-details-flag]&hr=[hide-raw-roll-details-flag]&s=[spoiler-results-flag]&m-or-max=[max-roll-flag, cannot be used with n flag]&min=[[min-roll-flag, cannot be used with n or max]&n=[nominal-roll-flag, cannot be used with max or min flag]&gms=[csv-of-discord-user-ids-to-be-dmed-results]&o=[order-rolls, must be a or d]&c=[count-flag]&cc=[confirm-crit-flag]
|
||||
url: http://localhost:8166/api/roll?user=[discord-user-id]&channel=[discord-channel-id]&rollstr=[artificer-roll-cmd]&documentation=All items below are optional. Flags do not need values.&nd=[no-details-flag]&snd=[super-no-details-flag]&hr=[hide-raw-roll-details-flag]&s=[spoiler-results-flag]&m-or-max=[max-roll-flag, cannot be used with n flag]&min=[[min-roll-flag, cannot be used with n or max]&n=[nominal-roll-flag, cannot be used with max or min flag]&gms=[csv-of-discord-user-ids-to-be-dmed-results]&o=[order-rolls, must be a or d]&c=[count-flag]&cc=[confirm-crit-flag]&rd=[roll-distribution-flag]
|
||||
body: none
|
||||
auth: inherit
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
"CWOD",
|
||||
"DEVMODE",
|
||||
"Discordeno",
|
||||
"Dists",
|
||||
"dkdk",
|
||||
"EMDAS",
|
||||
"funciton",
|
||||
|
|
|
@ -132,6 +132,8 @@ The Artificer comes with a few supplemental commands to the main rolling command
|
|||
* `-gm @user1 @user2 ... @userN` - GM Roll - Rolls the requested roll in GM mode, suppressing all publicly shown results and details and sending the results directly to the specified GMs
|
||||
* `-o a` or `-o d` - Order Roll - Rolls the requested roll and orders the results in the requested direction
|
||||
* `-ct` - Comma Totals - Adds commas to totals for readability
|
||||
* `-cc` - Confirm Critical Hits - Automatically rerolls whenever a crit hits
|
||||
- `-rd` - Roll Distribution - Shows a raw roll distribution of all dice in roll
|
||||
* The results have some formatting applied on them to provide details on what happened during this roll.
|
||||
* Critical successes will be **bolded**
|
||||
* Critical fails will be <ins>underlined</ins>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
export const config = {
|
||||
name: 'The Artificer', // Name of the bot
|
||||
maxFileSize: 8388290, // Max file size bot can send
|
||||
version: '3.0.0', // Version of the bot
|
||||
token: 'the_bot_token', // Discord API Token for this bot
|
||||
localtoken: 'local_testing_token', // Discord API Token for a secondary OPTIONAL testing bot, THIS MUST BE DIFFERENT FROM "token"
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { CountDetails } from 'artigen/dice/dice.d.ts';
|
||||
import { Embed, FileContent } from '@discordeno';
|
||||
|
||||
import { CountDetails, RollDistributionMap } from 'artigen/dice/dice.d.ts';
|
||||
|
||||
// ReturnData is the temporary internal type used before getting turned into SolvedRoll
|
||||
export interface ReturnData {
|
||||
|
@ -20,4 +22,19 @@ export interface SolvedRoll {
|
|||
line2: string;
|
||||
line3: string;
|
||||
counts: CountDetails;
|
||||
rollDistributions: RollDistributionMap;
|
||||
}
|
||||
|
||||
interface basicArtigenEmbed {
|
||||
charCount: number;
|
||||
embed: Embed;
|
||||
}
|
||||
|
||||
export interface ArtigenEmbedNoAttachment extends basicArtigenEmbed {
|
||||
hasAttachment: false;
|
||||
}
|
||||
|
||||
export interface ArtigenEmbedWithAttachment extends basicArtigenEmbed {
|
||||
hasAttachment: true;
|
||||
attachment: FileContent;
|
||||
}
|
||||
|
|
|
@ -6,12 +6,13 @@ import { tokenizeCmd } from 'artigen/cmdTokenizer.ts';
|
|||
import { loopCountCheck } from 'artigen/managers/loopManager.ts';
|
||||
import { QueuedRoll } from 'artigen/managers/manager.d.ts';
|
||||
|
||||
import { reduceCountDetails } from 'artigen/utils/counter.ts';
|
||||
import { cmdSplitRegex, escapeCharacters } from 'artigen/utils/escape.ts';
|
||||
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
|
||||
import { assertPrePostBalance } from 'artigen/utils/parenBalance.ts';
|
||||
import { reduceRollDistMaps } from 'artigen/utils/rollDist.ts';
|
||||
import { compareTotalRolls, compareTotalRollsReverse } from 'artigen/utils/sortFuncs.ts';
|
||||
import { translateError } from 'artigen/utils/translateError.ts';
|
||||
import { reduceCountDetails } from 'artigen/utils/counter.ts';
|
||||
|
||||
// runCmd(rollRequest)
|
||||
// runCmd handles converting rollRequest into a computer readable format for processing, and finally executes the solving
|
||||
|
@ -31,6 +32,7 @@ export const runCmd = (rollRequest: QueuedRoll): SolvedRoll => {
|
|||
dropped: 0,
|
||||
exploded: 0,
|
||||
},
|
||||
rollDistributions: new Map<string, number[]>(),
|
||||
};
|
||||
|
||||
// Whole processor lives in a try-catch to catch artigen's intentional error conditions
|
||||
|
@ -43,8 +45,8 @@ export const runCmd = (rollRequest: QueuedRoll): SolvedRoll => {
|
|||
assertPrePostBalance(sepCmds);
|
||||
|
||||
// Send the split roll into the command tokenizer to get raw response data
|
||||
const [tempReturnData, tempCountDetails] = tokenizeCmd(sepCmds, rollRequest.modifiers, true);
|
||||
loggingEnabled && log(LT.LOG, `Return data is back ${JSON.stringify(tempReturnData)}`);
|
||||
const [tempReturnData, tempCountDetails, tempRollDists] = tokenizeCmd(sepCmds, rollRequest.modifiers, true);
|
||||
loggingEnabled && log(LT.LOG, `Return data is back ${JSON.stringify(tempReturnData)} ${JSON.stringify(tempCountDetails)} ${JSON.stringify(tempRollDists)}`);
|
||||
|
||||
// Remove any floating spaces from originalCommand
|
||||
// Escape any | and ` chars in originalCommand to prevent spoilers and code blocks from acting up
|
||||
|
@ -120,6 +122,9 @@ export const runCmd = (rollRequest: QueuedRoll): SolvedRoll => {
|
|||
|
||||
// Reduce counts to a single object
|
||||
returnMsg.counts = reduceCountDetails(tempCountDetails);
|
||||
|
||||
// Reduce rollDist maps into a single map
|
||||
returnMsg.rollDistributions = reduceRollDistMaps(tempRollDists);
|
||||
} catch (e) {
|
||||
// Fill in the return block
|
||||
const solverError = e as Error;
|
||||
|
|
|
@ -4,23 +4,29 @@ import config from '~config';
|
|||
|
||||
import { ReturnData } from 'artigen/artigen.d.ts';
|
||||
|
||||
import { CountDetails, RollModifiers } from 'artigen/dice/dice.d.ts';
|
||||
import { CountDetails, RollDistributionMap, RollModifiers } from 'artigen/dice/dice.d.ts';
|
||||
|
||||
import { loopCountCheck } from 'artigen/managers/loopManager.ts';
|
||||
|
||||
import { tokenizeMath } from 'artigen/math/mathTokenizer.ts';
|
||||
|
||||
import { reduceCountDetails } from 'artigen/utils/counter.ts';
|
||||
import { closeInternal, internalWrapRegex, openInternal } from 'artigen/utils/escape.ts';
|
||||
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
|
||||
import { getMatchingInternalIdx, getMatchingPostfixIdx } from 'artigen/utils/parenBalance.ts';
|
||||
import { reduceCountDetails } from 'artigen/utils/counter.ts';
|
||||
|
||||
// tokenizeCmd expects a string[] of items that are either config.prefix/config.postfix or some text that contains math and/or dice rolls
|
||||
export const tokenizeCmd = (cmd: string[], modifiers: RollModifiers, topLevel: boolean, previousResults: number[] = []): [ReturnData[], CountDetails[]] => {
|
||||
export const tokenizeCmd = (
|
||||
cmd: string[],
|
||||
modifiers: RollModifiers,
|
||||
topLevel: boolean,
|
||||
previousResults: number[] = [],
|
||||
): [ReturnData[], CountDetails[], RollDistributionMap[]] => {
|
||||
loggingEnabled && log(LT.LOG, `Tokenizing command ${JSON.stringify(cmd)}`);
|
||||
|
||||
const returnData: ReturnData[] = [];
|
||||
const countDetails: CountDetails[] = [];
|
||||
const rollDists: RollDistributionMap[] = [];
|
||||
|
||||
// Wrapped commands still exist, unwrap them
|
||||
while (cmd.includes(config.prefix)) {
|
||||
|
@ -34,7 +40,7 @@ export const tokenizeCmd = (cmd: string[], modifiers: RollModifiers, topLevel: b
|
|||
loggingEnabled && log(LT.LOG, `Setting previous results: topLevel:${topLevel} ${topLevel ? returnData.map((rd) => rd.rollTotal) : previousResults}`);
|
||||
|
||||
// Handle any nested commands
|
||||
const [tempData, tempCounts] = tokenizeCmd(currentCmd, modifiers, false, topLevel ? returnData.map((rd) => rd.rollTotal) : previousResults);
|
||||
const [tempData, tempCounts, tempDists] = tokenizeCmd(currentCmd, modifiers, false, topLevel ? returnData.map((rd) => rd.rollTotal) : previousResults);
|
||||
const data = tempData[0];
|
||||
|
||||
if (topLevel) {
|
||||
|
@ -53,14 +59,22 @@ export const tokenizeCmd = (cmd: string[], modifiers: RollModifiers, topLevel: b
|
|||
// Store results
|
||||
returnData.push(data);
|
||||
countDetails.push(...tempCounts);
|
||||
rollDists.push(...tempDists);
|
||||
|
||||
// Handle ConfirmCrit if its on
|
||||
if (topLevel && modifiers.confirmCrit && reduceCountDetails(tempCounts).successful) {
|
||||
loggingEnabled && log(LT.LOG, `ConfirmCrit on ${JSON.stringify(currentCmd)}`);
|
||||
let done = false;
|
||||
while (!done) {
|
||||
loopCountCheck();
|
||||
|
||||
const [ccTempData, ccTempCounts] = tokenizeCmd(currentCmd, modifiers, false, topLevel ? returnData.map((rd) => rd.rollTotal) : previousResults);
|
||||
// Keep running the same roll again until its not successful
|
||||
const [ccTempData, ccTempCounts, ccTempDists] = tokenizeCmd(
|
||||
currentCmd,
|
||||
modifiers,
|
||||
false,
|
||||
topLevel ? returnData.map((rd) => rd.rollTotal) : previousResults,
|
||||
);
|
||||
const ccData = ccTempData[0];
|
||||
ccData.rollPreFormat = '\nAuto-Confirming Crit: ';
|
||||
|
||||
|
@ -69,6 +83,7 @@ export const tokenizeCmd = (cmd: string[], modifiers: RollModifiers, topLevel: b
|
|||
// Store CC results
|
||||
returnData.push(ccData);
|
||||
countDetails.push(...ccTempCounts);
|
||||
rollDists.push(...ccTempDists);
|
||||
|
||||
done = reduceCountDetails(ccTempCounts).successful === 0;
|
||||
}
|
||||
|
@ -80,17 +95,19 @@ export const tokenizeCmd = (cmd: string[], modifiers: RollModifiers, topLevel: b
|
|||
loggingEnabled && log(LT.LOG, `Adding leftover formatting to last returnData ${JSON.stringify(cmd)}`);
|
||||
returnData[returnData.length - 1].rollPostFormat = cmd.join('');
|
||||
}
|
||||
return [returnData, countDetails];
|
||||
return [returnData, countDetails, rollDists];
|
||||
} else {
|
||||
loggingEnabled && log(LT.LOG, `Tokenizing math ${JSON.stringify(cmd)}`);
|
||||
|
||||
// Solve the math and rolls for this cmd
|
||||
const [tempData, tempCounts] = tokenizeMath(cmd.join(''), modifiers, previousResults);
|
||||
const [tempData, tempCounts, tempDists] = tokenizeMath(cmd.join(''), modifiers, previousResults);
|
||||
const data = tempData[0];
|
||||
loggingEnabled && log(LT.LOG, `Solved math is back ${JSON.stringify(data)} | ${JSON.stringify(returnData)}`);
|
||||
loggingEnabled &&
|
||||
log(LT.LOG, `Solved math is back ${JSON.stringify(data)} | ${JSON.stringify(returnData)} ${JSON.stringify(tempCounts)} ${JSON.stringify(tempDists)}`);
|
||||
|
||||
// Merge counts
|
||||
countDetails.push(...tempCounts);
|
||||
rollDists.push(...tempDists);
|
||||
|
||||
// Handle merging returnData into tempData
|
||||
const initConf = data.initConfig.split(internalWrapRegex).filter((x) => x);
|
||||
|
@ -112,6 +129,6 @@ export const tokenizeCmd = (cmd: string[], modifiers: RollModifiers, topLevel: b
|
|||
// Join all parts/remainders
|
||||
data.initConfig = initConf.join('');
|
||||
loggingEnabled && log(LT.LOG, `ReturnData merged into solved math ${JSON.stringify(data)} | ${JSON.stringify(countDetails)}`);
|
||||
return [[data], countDetails];
|
||||
return [[data], countDetails, rollDists];
|
||||
}
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@ export interface RollSet {
|
|||
type: RollType;
|
||||
origIdx: number;
|
||||
roll: number;
|
||||
size: number;
|
||||
dropped: boolean;
|
||||
rerolled: boolean;
|
||||
exploding: boolean;
|
||||
|
@ -25,10 +26,15 @@ export interface CountDetails {
|
|||
exploded: number;
|
||||
}
|
||||
|
||||
// RollDistribution is used for storing the raw roll distribution
|
||||
// use rollDistKey to generate the key
|
||||
export type RollDistributionMap = Map<string, number[]>;
|
||||
|
||||
// RollFormat is the return structure for the rollFormatter
|
||||
export interface RollFormat {
|
||||
export interface FormattedRoll {
|
||||
solvedStep: SolvedStep;
|
||||
countDetails: CountDetails;
|
||||
rollDistributions: RollDistributionMap;
|
||||
}
|
||||
|
||||
// RollModifiers is the structure to keep track of the decorators applied to a roll command
|
||||
|
@ -46,6 +52,7 @@ export interface RollModifiers {
|
|||
count: boolean;
|
||||
commaTotals: boolean;
|
||||
confirmCrit: boolean;
|
||||
rollDist: boolean;
|
||||
apiWarn: string;
|
||||
valid: boolean;
|
||||
error: Error;
|
||||
|
|
|
@ -54,6 +54,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[
|
|||
type: rollConf.type,
|
||||
origIdx: 0,
|
||||
roll: 0,
|
||||
size: 0,
|
||||
dropped: false,
|
||||
rerolled: false,
|
||||
exploding: false,
|
||||
|
@ -71,6 +72,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[
|
|||
const rolling = getTemplateRoll();
|
||||
// If maximizeRoll is on, set the roll to the dieSize, else if nominalRoll is on, set the roll to the average roll of dieSize, else generate a new random roll
|
||||
rolling.roll = rollConf.type === 'fate' ? genFateRoll(modifiers) : genRoll(rollConf.dieSize, modifiers, rollConf.dPercent);
|
||||
rolling.size = rollConf.dieSize;
|
||||
// Set origIdx of roll
|
||||
rolling.origIdx = i;
|
||||
|
||||
|
@ -110,6 +112,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[
|
|||
|
||||
// Copy the template to fill out for this iteration
|
||||
const newReroll = getTemplateRoll();
|
||||
newReroll.size = rollConf.dieSize;
|
||||
if (modifiers.maxRoll && !minMaxOverride) {
|
||||
// If maximizeRoll is on and we've entered the reroll code, dieSize is not allowed, determine the next best option and always return that
|
||||
mmMaxLoop: for (let m = rollConf.dieSize - 1; m > 0; m--) {
|
||||
|
@ -167,6 +170,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[
|
|||
const newExplodingRoll = getTemplateRoll();
|
||||
// If maximizeRoll is on, set the roll to the dieSize, else if nominalRoll is on, set the roll to the average roll of dieSize, else generate a new random roll
|
||||
newExplodingRoll.roll = genRoll(rollConf.dieSize, modifiers, rollConf.dPercent);
|
||||
newExplodingRoll.size = rollConf.dieSize;
|
||||
// Always mark this roll as exploding
|
||||
newExplodingRoll.exploding = true;
|
||||
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
import { log, LogTypes as LT } from '@Log4Deno';
|
||||
|
||||
import { RollFormat, RollModifiers } from 'artigen/dice/dice.d.ts';
|
||||
import { FormattedRoll, RollModifiers } from 'artigen/dice/dice.d.ts';
|
||||
import { executeRoll } from 'artigen/dice/executeRoll.ts';
|
||||
|
||||
import { loopCountCheck } from 'artigen/managers/loopManager.ts';
|
||||
|
||||
import { rollCounter } from 'artigen/utils/counter.ts';
|
||||
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
|
||||
import { createRollDistMap } from 'artigen/utils/rollDist.ts';
|
||||
|
||||
// generateFormattedRoll(rollConf, modifiers) returns one SolvedStep
|
||||
// generateFormattedRoll handles creating and formatting the completed rolls into the SolvedStep format
|
||||
export const generateFormattedRoll = (rollConf: string, modifiers: RollModifiers): RollFormat => {
|
||||
export const generateFormattedRoll = (rollConf: string, modifiers: RollModifiers): FormattedRoll => {
|
||||
let tempTotal = 0;
|
||||
let tempDetails = '[';
|
||||
let tempCrit = false;
|
||||
|
@ -85,5 +86,6 @@ export const generateFormattedRoll = (rollConf: string, modifiers: RollModifiers
|
|||
containsFail: tempFail,
|
||||
},
|
||||
countDetails: modifiers.count || modifiers.confirmCrit ? rollCounter(tempRollSet) : rollCounter([]),
|
||||
rollDistributions: modifiers.rollDist ? createRollDistMap(tempRollSet) : new Map<string, number[]>(),
|
||||
};
|
||||
};
|
||||
|
|
|
@ -16,6 +16,7 @@ export const Modifiers = Object.freeze({
|
|||
Order: '-o',
|
||||
CommaTotals: '-ct',
|
||||
ConfirmCrit: '-cc',
|
||||
RollDistribution: '-rd',
|
||||
});
|
||||
|
||||
export const getModifiers = (args: string[]): [RollModifiers, string[]] => {
|
||||
|
@ -33,6 +34,7 @@ export const getModifiers = (args: string[]): [RollModifiers, string[]] => {
|
|||
count: false,
|
||||
commaTotals: false,
|
||||
confirmCrit: false,
|
||||
rollDist: false,
|
||||
apiWarn: '',
|
||||
valid: false,
|
||||
error: new Error(),
|
||||
|
@ -104,6 +106,9 @@ export const getModifiers = (args: string[]): [RollModifiers, string[]] => {
|
|||
case Modifiers.CommaTotals:
|
||||
modifiers.commaTotals = true;
|
||||
break;
|
||||
case Modifiers.RollDistribution:
|
||||
modifiers.rollDist = true;
|
||||
break;
|
||||
default:
|
||||
// Default case should not mess with the array
|
||||
defaultCase = true;
|
||||
|
|
|
@ -72,7 +72,7 @@ export const getRollConf = (rollStr: string): RollConf => {
|
|||
const rawDC = dPts.shift() || '1';
|
||||
if (rawDC.includes('.')) {
|
||||
throw new Error('WholeDieCountSizeOnly');
|
||||
} else if (rawDC.match(/\D/)) {
|
||||
} else if (!rawDC.endsWith('cwo') && !rawDC.endsWith('ova') && rawDC.match(/\D/)) {
|
||||
throw new Error(`CannotParseDieCount_${rawDC}`);
|
||||
}
|
||||
const tempDC = rawDC.replace(/\D/g, '');
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { DiscordenoMessage, sendDirectMessage, sendMessage } from '@discordeno';
|
||||
import { botId, DiscordenoMessage, Embed, FileContent, sendDirectMessage, sendMessage } from '@discordeno';
|
||||
import { log, LogTypes as LT } from '@Log4Deno';
|
||||
|
||||
import config from '~config';
|
||||
|
@ -6,10 +6,12 @@ import { DEVMODE } from '~flags';
|
|||
|
||||
import { SolvedRoll } from 'artigen/artigen.d.ts';
|
||||
|
||||
import { RollModifiers } from 'artigen/dice/dice.d.ts';
|
||||
|
||||
import { removeWorker } from 'artigen/managers/countManager.ts';
|
||||
import { QueuedRoll } from 'artigen/managers/manager.d.ts';
|
||||
|
||||
import { generateCountDetailsEmbed, generateDMFailed, generateRollEmbed } from 'artigen/utils/embeds.ts';
|
||||
import { generateCountDetailsEmbed, generateDMFailed, generateRollDistsEmbed, generateRollEmbed } from 'artigen/utils/embeds.ts';
|
||||
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
|
||||
|
||||
import dbClient from 'db/client.ts';
|
||||
|
@ -18,6 +20,8 @@ import { queries } from 'db/common.ts';
|
|||
import stdResp from 'endpoints/stdResponses.ts';
|
||||
|
||||
import utils from 'utils/utils.ts';
|
||||
import { infoColor1 } from 'embeds/colors.ts';
|
||||
import { basicReducer } from 'artigen/utils/reducers.ts';
|
||||
|
||||
export const onWorkerComplete = async (workerMessage: MessageEvent<SolvedRoll>, workerTimeout: number, rollRequest: QueuedRoll) => {
|
||||
let apiErroredOut = false;
|
||||
|
@ -28,24 +32,57 @@ export const onWorkerComplete = async (workerMessage: MessageEvent<SolvedRoll>,
|
|||
const returnMsg = workerMessage.data;
|
||||
loggingEnabled && log(LT.LOG, `Roll came back from worker: ${returnMsg.line1.length} |&| ${returnMsg.line2.length} |&| ${returnMsg.line3.length} `);
|
||||
loggingEnabled && log(LT.LOG, `Roll came back from worker: ${returnMsg.line1} |&| ${returnMsg.line2} |&| ${returnMsg.line3} `);
|
||||
const pubEmbedDetails = await generateRollEmbed(
|
||||
const pubEmbedDetails = generateRollEmbed(
|
||||
rollRequest.apiRoll ? rollRequest.api.userId : rollRequest.dd.originalMessage.authorId,
|
||||
returnMsg,
|
||||
rollRequest.modifiers,
|
||||
);
|
||||
const gmEmbedDetails = await generateRollEmbed(rollRequest.apiRoll ? rollRequest.api.userId : rollRequest.dd.originalMessage.authorId, returnMsg, {
|
||||
const gmEmbedDetails = generateRollEmbed(rollRequest.apiRoll ? rollRequest.api.userId : rollRequest.dd.originalMessage.authorId, returnMsg, {
|
||||
...rollRequest.modifiers,
|
||||
gmRoll: false,
|
||||
});
|
||||
const countEmbed = generateCountDetailsEmbed(returnMsg.counts);
|
||||
loggingEnabled && log(LT.LOG, `Embeds are generated: ${JSON.stringify(pubEmbedDetails)} |&| ${JSON.stringify(gmEmbedDetails)}`);
|
||||
|
||||
let pubRespCharCount = pubEmbedDetails.charCount;
|
||||
let gmRespCharCount = gmEmbedDetails.charCount;
|
||||
const pubEmbeds: Embed[] = [pubEmbedDetails.embed];
|
||||
const gmEmbeds: Embed[] = [gmEmbedDetails.embed];
|
||||
const pubAttachments: FileContent[] = pubEmbedDetails.hasAttachment ? [pubEmbedDetails.attachment] : [];
|
||||
const gmAttachments: FileContent[] = gmEmbedDetails.hasAttachment ? [gmEmbedDetails.attachment] : [];
|
||||
|
||||
// Handle adding count embed to correct list
|
||||
if (rollRequest.modifiers.count) {
|
||||
const countEmbed = generateCountDetailsEmbed(returnMsg.counts);
|
||||
if (rollRequest.modifiers.gmRoll) {
|
||||
gmEmbeds.push(countEmbed.embed);
|
||||
gmRespCharCount += countEmbed.charCount;
|
||||
} else {
|
||||
pubEmbeds.push(countEmbed.embed);
|
||||
pubRespCharCount += countEmbed.charCount;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle adding rollDist embed to correct list
|
||||
if (rollRequest.modifiers.rollDist) {
|
||||
const rollDistEmbed = generateRollDistsEmbed(returnMsg.rollDistributions);
|
||||
if (rollRequest.modifiers.gmRoll) {
|
||||
gmEmbeds.push(rollDistEmbed.embed);
|
||||
rollDistEmbed.hasAttachment && gmAttachments.push(rollDistEmbed.attachment);
|
||||
gmRespCharCount += rollDistEmbed.charCount;
|
||||
} else {
|
||||
pubEmbeds.push(rollDistEmbed.embed);
|
||||
rollDistEmbed.hasAttachment && pubAttachments.push(rollDistEmbed.attachment);
|
||||
pubRespCharCount += rollDistEmbed.charCount;
|
||||
}
|
||||
}
|
||||
|
||||
loggingEnabled && log(LT.LOG, `Embeds are generated: ${pubRespCharCount} ${JSON.stringify(pubEmbeds)} |&| ${gmRespCharCount} ${JSON.stringify(gmEmbeds)}`);
|
||||
|
||||
// If there was an error, report it to the user in hopes that they can determine what they did wrong
|
||||
if (returnMsg.error) {
|
||||
if (rollRequest.apiRoll) {
|
||||
rollRequest.api.resolve(stdResp.InternalServerError(returnMsg.errorMsg));
|
||||
} else {
|
||||
rollRequest.dd.myResponse.edit({ embeds: [pubEmbedDetails.embed] });
|
||||
rollRequest.dd.myResponse.edit({ embeds: pubEmbeds });
|
||||
}
|
||||
|
||||
if (rollRequest.apiRoll || (DEVMODE && config.logRolls)) {
|
||||
|
@ -58,103 +95,142 @@ export const onWorkerComplete = async (workerMessage: MessageEvent<SolvedRoll>,
|
|||
])
|
||||
.catch((e) => utils.commonLoggers.dbError('rollQueue.ts:82', 'insert into', e));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let newMsg: DiscordenoMessage | void = undefined;
|
||||
// Determine if we are to send a GM roll or a normal roll
|
||||
if (rollRequest.modifiers.gmRoll) {
|
||||
if (rollRequest.apiRoll) {
|
||||
newMsg = await sendMessage(rollRequest.api.channelId, {
|
||||
content: rollRequest.modifiers.apiWarn,
|
||||
embeds: pubEmbeds,
|
||||
}).catch(() => {
|
||||
apiErroredOut = true;
|
||||
rollRequest.api.resolve(stdResp.InternalServerError('Message failed to send - location 0.'));
|
||||
});
|
||||
} else {
|
||||
// Send the public embed to correct channel
|
||||
rollRequest.dd.myResponse.edit({ embeds: pubEmbeds });
|
||||
}
|
||||
|
||||
if (!apiErroredOut) {
|
||||
// And message the full details to each of the GMs, alerting roller of every GM that could not be messaged
|
||||
rollRequest.modifiers.gms.forEach(async (gm) => {
|
||||
const gmId: bigint = BigInt(gm.startsWith('<') ? gm.substring(2, gm.length - 1) : gm);
|
||||
log(LT.LOG, `Messaging GM ${gm} | ${gmId}`);
|
||||
// Attempt to DM the GM and send a warning if it could not DM a GM
|
||||
await sendDirectMessage(gmId, {
|
||||
content: `Original GM Roll Request: ${rollRequest.apiRoll ? newMsg && newMsg.link : rollRequest.dd.myResponse.link}`,
|
||||
embeds: gmEmbeds,
|
||||
})
|
||||
.then(async () => {
|
||||
// Check if we need to attach a file and send it after the initial details sent
|
||||
if (gmAttachments.length) {
|
||||
await sendDirectMessage(gmId, {
|
||||
file: gmAttachments,
|
||||
}).catch(() => {
|
||||
if (newMsg && rollRequest.apiRoll) {
|
||||
newMsg.reply(generateDMFailed(gmId));
|
||||
} else if (!rollRequest.apiRoll) {
|
||||
rollRequest.dd.originalMessage.reply(generateDMFailed(gmId));
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (rollRequest.apiRoll && newMsg) {
|
||||
newMsg.reply(generateDMFailed(gmId));
|
||||
} else if (!rollRequest.apiRoll) {
|
||||
rollRequest.dd.originalMessage.reply(generateDMFailed(gmId));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
let newMsg: DiscordenoMessage | void = undefined;
|
||||
// Determine if we are to send a GM roll or a normal roll
|
||||
if (rollRequest.modifiers.gmRoll) {
|
||||
if (rollRequest.apiRoll) {
|
||||
newMsg = await sendMessage(rollRequest.api.channelId, {
|
||||
content: rollRequest.modifiers.apiWarn,
|
||||
embeds: [pubEmbedDetails.embed],
|
||||
}).catch(() => {
|
||||
apiErroredOut = true;
|
||||
rollRequest.api.resolve(stdResp.InternalServerError('Message failed to send - location 0.'));
|
||||
// Not a gm roll, so just send normal embed to correct channel
|
||||
if (rollRequest.apiRoll) {
|
||||
newMsg = await sendMessage(rollRequest.api.channelId, {
|
||||
content: rollRequest.modifiers.apiWarn,
|
||||
embeds: pubEmbeds,
|
||||
}).catch(() => {
|
||||
apiErroredOut = true;
|
||||
rollRequest.api.resolve(stdResp.InternalServerError('Message failed to send - location 1.'));
|
||||
});
|
||||
} else {
|
||||
newMsg = await rollRequest.dd.myResponse.edit({
|
||||
embeds: pubEmbeds,
|
||||
});
|
||||
}
|
||||
|
||||
if (pubAttachments.length && newMsg) {
|
||||
// Attachment requires you to send a new message
|
||||
const respMessage: Embed[] = [
|
||||
{
|
||||
color: infoColor1,
|
||||
description: `This message contains information for a previous roll.\nPlease click on "<@${botId}> *Click to see attachment*" above this message to see the previous roll.`,
|
||||
},
|
||||
];
|
||||
|
||||
if (pubAttachments.map((file) => file.blob.size).reduce(basicReducer) < config.maxFileSize) {
|
||||
// All attachments will fit in one message
|
||||
newMsg.reply({
|
||||
embeds: respMessage,
|
||||
file: pubAttachments,
|
||||
});
|
||||
} else {
|
||||
// Send the public embed to correct channel
|
||||
rollRequest.dd.myResponse.edit({ embeds: [pubEmbedDetails.embed] });
|
||||
}
|
||||
|
||||
if (!apiErroredOut) {
|
||||
// And message the full details to each of the GMs, alerting roller of every GM that could not be messaged
|
||||
rollRequest.modifiers.gms.forEach(async (gm) => {
|
||||
const gmId: bigint = BigInt(gm.startsWith('<') ? gm.substring(2, gm.length - 1) : gm);
|
||||
log(LT.LOG, `Messaging GM ${gm} | ${gmId}`);
|
||||
// Attempt to DM the GM and send a warning if it could not DM a GM
|
||||
await sendDirectMessage(gmId, {
|
||||
content: `Original GM Roll Request: ${rollRequest.apiRoll ? newMsg && newMsg.link : rollRequest.dd.myResponse.link}`,
|
||||
embeds: rollRequest.modifiers.count ? [gmEmbedDetails.embed, countEmbed] : [gmEmbedDetails.embed],
|
||||
})
|
||||
.then(async () => {
|
||||
// Check if we need to attach a file and send it after the initial details sent
|
||||
if (gmEmbedDetails.hasAttachment) {
|
||||
await sendDirectMessage(gmId, {
|
||||
file: gmEmbedDetails.attachment,
|
||||
}).catch(() => {
|
||||
if (newMsg && rollRequest.apiRoll) {
|
||||
newMsg.reply(generateDMFailed(gmId));
|
||||
} else if (!rollRequest.apiRoll) {
|
||||
rollRequest.dd.originalMessage.reply(generateDMFailed(gmId));
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (rollRequest.apiRoll && newMsg) {
|
||||
newMsg.reply(generateDMFailed(gmId));
|
||||
} else if (!rollRequest.apiRoll) {
|
||||
rollRequest.dd.originalMessage.reply(generateDMFailed(gmId));
|
||||
}
|
||||
pubAttachments.forEach((file) => {
|
||||
newMsg &&
|
||||
newMsg.reply({
|
||||
embeds: respMessage,
|
||||
file,
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Not a gm roll, so just send normal embed to correct channel
|
||||
if (rollRequest.apiRoll) {
|
||||
newMsg = await sendMessage(rollRequest.api.channelId, {
|
||||
content: rollRequest.modifiers.apiWarn,
|
||||
embeds: rollRequest.modifiers.count ? [pubEmbedDetails.embed, countEmbed] : [pubEmbedDetails.embed],
|
||||
}).catch(() => {
|
||||
apiErroredOut = true;
|
||||
rollRequest.api.resolve(stdResp.InternalServerError('Message failed to send - location 1.'));
|
||||
});
|
||||
} else {
|
||||
newMsg = await rollRequest.dd.myResponse.edit({
|
||||
embeds: rollRequest.modifiers.count ? [pubEmbedDetails.embed, countEmbed] : [pubEmbedDetails.embed],
|
||||
});
|
||||
}
|
||||
|
||||
if (pubEmbedDetails.hasAttachment && newMsg) {
|
||||
// Attachment requires you to send a new message
|
||||
newMsg.reply({
|
||||
file: pubEmbedDetails.attachment,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rollRequest.apiRoll && !apiErroredOut) {
|
||||
dbClient
|
||||
.execute(queries.insertRollLogCmd(1, 0), [rollRequest.originalCommand, returnMsg.errorCode, newMsg ? newMsg.id : null])
|
||||
.catch((e) => utils.commonLoggers.dbError('rollQueue.ts:155', 'insert into', e));
|
||||
if (rollRequest.apiRoll && !apiErroredOut) {
|
||||
dbClient
|
||||
.execute(queries.insertRollLogCmd(1, 0), [rollRequest.originalCommand, returnMsg.errorCode, newMsg ? newMsg.id : null])
|
||||
.catch((e) => utils.commonLoggers.dbError('rollQueue.ts:155', 'insert into', e));
|
||||
|
||||
rollRequest.api.resolve(
|
||||
stdResp.OK(
|
||||
JSON.stringify(
|
||||
rollRequest.modifiers.count
|
||||
? {
|
||||
counts: countEmbed,
|
||||
details: pubEmbedDetails,
|
||||
}
|
||||
: {
|
||||
details: pubEmbedDetails,
|
||||
},
|
||||
),
|
||||
rollRequest.api.resolve(
|
||||
stdResp.OK(
|
||||
JSON.stringify(
|
||||
rollRequest.modifiers.count
|
||||
? {
|
||||
counts: returnMsg.counts,
|
||||
details: pubEmbedDetails,
|
||||
}
|
||||
: {
|
||||
details: pubEmbedDetails,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
log(LT.ERROR, `Unhandled rollRequest Error: ${JSON.stringify(e)}`);
|
||||
if (!rollRequest.apiRoll) {
|
||||
rollRequest.dd.myResponse.edit({
|
||||
embeds: [
|
||||
(
|
||||
await generateRollEmbed(
|
||||
rollRequest.dd.originalMessage.authorId,
|
||||
<SolvedRoll> {
|
||||
error: true,
|
||||
errorMsg:
|
||||
`Something weird went wrong, likely the requested roll is too complex and caused the response to be too large for Discord. Try breaking the request down into smaller messages and try again.\n\nIf this error continues to come up, please \`${config.prefix}report\` this to my developer.`,
|
||||
errorCode: 'UnhandledWorkerComplete',
|
||||
},
|
||||
<RollModifiers> {},
|
||||
)
|
||||
).embed,
|
||||
],
|
||||
});
|
||||
}
|
||||
if (rollRequest.apiRoll && !apiErroredOut) {
|
||||
rollRequest.api.resolve(stdResp.InternalServerError(JSON.stringify(e)));
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import { ReturnData } from 'artigen/artigen.d.ts';
|
|||
|
||||
import { MathConf, SolvedStep } from 'artigen/math/math.d.ts';
|
||||
|
||||
import { CountDetails, RollModifiers } from 'artigen/dice/dice.d.ts';
|
||||
import { CountDetails, RollDistributionMap, RollModifiers } from 'artigen/dice/dice.d.ts';
|
||||
import { generateFormattedRoll } from 'artigen/dice/generateFormattedRoll.ts';
|
||||
|
||||
import { loopCountCheck } from 'artigen/managers/loopManager.ts';
|
||||
|
@ -20,8 +20,9 @@ import { assertParenBalance } from 'artigen/utils/parenBalance.ts';
|
|||
const minusOps = ['(', '^', '*', '/', '%', '+', '-'];
|
||||
const allOps = [...minusOps, ')'];
|
||||
|
||||
export const tokenizeMath = (cmd: string, modifiers: RollModifiers, previousResults: number[]): [ReturnData[], CountDetails[]] => {
|
||||
export const tokenizeMath = (cmd: string, modifiers: RollModifiers, previousResults: number[]): [ReturnData[], CountDetails[], RollDistributionMap[]] => {
|
||||
const countDetails: CountDetails[] = [];
|
||||
const rollDists: RollDistributionMap[] = [];
|
||||
|
||||
loggingEnabled && log(LT.LOG, `Parsing roll ${cmd} | ${JSON.stringify(modifiers)} | ${JSON.stringify(previousResults)}`);
|
||||
|
||||
|
@ -146,6 +147,7 @@ export const tokenizeMath = (cmd: string, modifiers: RollModifiers, previousResu
|
|||
const formattedRoll = generateFormattedRoll(curMathConfStr, modifiers);
|
||||
mathConf[i] = formattedRoll.solvedStep;
|
||||
countDetails.push(formattedRoll.countDetails);
|
||||
rollDists.push(formattedRoll.rollDistributions);
|
||||
}
|
||||
|
||||
// Identify if we are in a state where the current number is a negative number
|
||||
|
@ -185,5 +187,6 @@ export const tokenizeMath = (cmd: string, modifiers: RollModifiers, previousResu
|
|||
},
|
||||
],
|
||||
countDetails,
|
||||
rollDists,
|
||||
];
|
||||
};
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
import { CreateMessage, EmbedField } from '@discordeno';
|
||||
import { log, LogTypes as LT } from '@Log4Deno';
|
||||
|
||||
import config from '~config';
|
||||
|
||||
import { SolvedRoll } from 'artigen/artigen.d.ts';
|
||||
import { ArtigenEmbedNoAttachment, ArtigenEmbedWithAttachment, SolvedRoll } from 'artigen/artigen.d.ts';
|
||||
|
||||
import { CountDetails, RollModifiers } from 'artigen/dice/dice.d.ts';
|
||||
import { CountDetails, RollDistributionMap, RollModifiers } from 'artigen/dice/dice.d.ts';
|
||||
|
||||
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
|
||||
import { failColor, infoColor1, infoColor2 } from 'embeds/colors.ts';
|
||||
import { basicReducer } from 'artigen/utils/reducers.ts';
|
||||
|
||||
export const rollingEmbed = {
|
||||
export const rollingEmbed: CreateMessage = {
|
||||
embeds: [
|
||||
{
|
||||
color: infoColor1,
|
||||
|
@ -18,7 +20,7 @@ export const rollingEmbed = {
|
|||
],
|
||||
};
|
||||
|
||||
export const generateDMFailed = (user: bigint) => ({
|
||||
export const generateDMFailed = (user: bigint): CreateMessage => ({
|
||||
embeds: [
|
||||
{
|
||||
color: failColor,
|
||||
|
@ -28,7 +30,7 @@ export const generateDMFailed = (user: bigint) => ({
|
|||
],
|
||||
});
|
||||
|
||||
export const generateRollError = (errorType: string, errorName: string, errorMsg: string) => ({
|
||||
export const generateRollError = (errorType: string, errorName: string, errorMsg: string): CreateMessage => ({
|
||||
embeds: [
|
||||
{
|
||||
color: failColor,
|
||||
|
@ -46,10 +48,9 @@ export const generateRollError = (errorType: string, errorName: string, errorMsg
|
|||
],
|
||||
});
|
||||
|
||||
export const generateCountDetailsEmbed = (counts: CountDetails) => ({
|
||||
color: infoColor1,
|
||||
title: 'Roll Count Details:',
|
||||
fields: [
|
||||
export const generateCountDetailsEmbed = (counts: CountDetails): ArtigenEmbedNoAttachment => {
|
||||
const title = 'Roll Count Details:';
|
||||
const fields: EmbedField[] = [
|
||||
{
|
||||
name: 'Total Rolls:',
|
||||
value: `${counts.total}`,
|
||||
|
@ -80,104 +81,191 @@ export const generateCountDetailsEmbed = (counts: CountDetails) => ({
|
|||
value: `${counts.exploded}`,
|
||||
inline: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
];
|
||||
|
||||
export const generateRollEmbed = async (authorId: bigint, returnDetails: SolvedRoll, modifiers: RollModifiers) => {
|
||||
return {
|
||||
charCount: title.length + fields.map((field) => field.name.length + field.value.length).reduce(basicReducer),
|
||||
embed: {
|
||||
color: infoColor1,
|
||||
title,
|
||||
fields,
|
||||
},
|
||||
hasAttachment: false,
|
||||
};
|
||||
};
|
||||
|
||||
const getDistName = (key: string) => {
|
||||
const [type, size] = key.split('-');
|
||||
switch (type) {
|
||||
case 'fate':
|
||||
return 'Fate dice';
|
||||
case 'cwod':
|
||||
return `CWOD d${size}`;
|
||||
case 'ova':
|
||||
return `OVA d${size}`;
|
||||
case 'roll20':
|
||||
default:
|
||||
return `d${size}`;
|
||||
}
|
||||
};
|
||||
|
||||
export const generateRollDistsEmbed = (rollDists: RollDistributionMap): ArtigenEmbedNoAttachment | ArtigenEmbedWithAttachment => {
|
||||
const fields = rollDists
|
||||
.entries()
|
||||
.toArray()
|
||||
.slice(0, 25)
|
||||
.map(([key, distArr]) => {
|
||||
const total = distArr.reduce(basicReducer);
|
||||
return {
|
||||
name: `${getDistName(key)} (Total rolls: ${total}):`,
|
||||
value: distArr.map((cnt, dieIdx) => `${key.startsWith('fate') ? dieIdx - 1 : dieIdx + 1}: ${cnt} (${((cnt / total) * 100).toFixed(1)}%)`).join('\n'),
|
||||
inline: true,
|
||||
};
|
||||
});
|
||||
const rollDistTitle = 'Roll Distributions:';
|
||||
|
||||
const totalSize = fields.map((field) => field.name.length + field.value.length).reduce(basicReducer);
|
||||
if (totalSize > 4000 || fields.some((field) => field.name.length > 256 || field.value.length > 1024)) {
|
||||
const rollDistBlob = new Blob([fields.map((field) => `# ${field.name}\n${field.value}`).join('\n\n') as BlobPart], { type: 'text' });
|
||||
if (rollDistBlob.size > config.maxFileSize) {
|
||||
const rollDistErrDesc =
|
||||
'The roll distribution was too large to be included and could not be attached below. If you would like to see the roll distribution details, please send the rolls in multiple messages.';
|
||||
return {
|
||||
charCount: rollDistTitle.length + rollDistErrDesc.length,
|
||||
embed: {
|
||||
color: failColor,
|
||||
title: rollDistTitle,
|
||||
description: rollDistErrDesc,
|
||||
},
|
||||
hasAttachment: false,
|
||||
};
|
||||
} else {
|
||||
const rollDistErrDesc = 'The roll distribution was too large to be included and has been attached below.';
|
||||
return {
|
||||
charCount: rollDistTitle.length + rollDistErrDesc.length,
|
||||
embed: {
|
||||
color: failColor,
|
||||
title: rollDistTitle,
|
||||
description: rollDistErrDesc,
|
||||
},
|
||||
hasAttachment: true,
|
||||
attachment: {
|
||||
name: 'rollDistributions.md',
|
||||
blob: rollDistBlob,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
charCount: rollDistTitle.length + totalSize,
|
||||
embed: {
|
||||
color: infoColor1,
|
||||
title: rollDistTitle,
|
||||
fields,
|
||||
},
|
||||
hasAttachment: false,
|
||||
};
|
||||
};
|
||||
|
||||
export const generateRollEmbed = (
|
||||
authorId: bigint,
|
||||
returnDetails: SolvedRoll,
|
||||
modifiers: RollModifiers,
|
||||
): ArtigenEmbedNoAttachment | ArtigenEmbedWithAttachment => {
|
||||
if (returnDetails.error) {
|
||||
// Roll had an error, send error embed
|
||||
const errTitle = 'Roll failed:';
|
||||
const errDesc = `${returnDetails.errorMsg}`;
|
||||
const errCode = `Code: ${returnDetails.errorCode}`;
|
||||
|
||||
return {
|
||||
charCount: errTitle.length + errDesc.length + errCode.length,
|
||||
embed: {
|
||||
color: failColor,
|
||||
title: 'Roll failed:',
|
||||
description: `${returnDetails.errorMsg}`,
|
||||
title: errTitle,
|
||||
description: errDesc,
|
||||
footer: {
|
||||
text: `Code: ${returnDetails.errorCode}`,
|
||||
text: errCode,
|
||||
},
|
||||
},
|
||||
hasAttachment: false,
|
||||
attachment: {
|
||||
blob: await new Blob(['' as BlobPart], { type: 'text' }),
|
||||
name: 'rollDetails.txt',
|
||||
},
|
||||
};
|
||||
} else {
|
||||
const line1Details = modifiers.hideRaw ? '' : `<@${authorId}>${returnDetails.line1}\n`;
|
||||
if (modifiers.gmRoll) {
|
||||
// Roll is a GM Roll, send this in the pub channel (this funciton will be ran again to get details for the GMs)
|
||||
return {
|
||||
embed: {
|
||||
color: infoColor2,
|
||||
description: `${line1Details}${line1Details ? '\n' : ''}Results have been messaged to the following GMs: ${
|
||||
modifiers.gms
|
||||
.map((gm) => (gm.startsWith('<') ? gm : `<@${gm}>`))
|
||||
.join(' ')
|
||||
}`,
|
||||
},
|
||||
hasAttachment: false,
|
||||
attachment: {
|
||||
blob: await new Blob(['' as BlobPart], { type: 'text' }),
|
||||
name: 'rollDetails.txt',
|
||||
},
|
||||
};
|
||||
} else {
|
||||
// Roll is normal, make normal embed
|
||||
const line2Details = returnDetails.line2.split(': ');
|
||||
let details = '';
|
||||
|
||||
if (!modifiers.superNoDetails) {
|
||||
details = `**Details:**\n${modifiers.spoiler}${returnDetails.line3}${modifiers.spoiler}`;
|
||||
loggingEnabled && log(LT.LOG, `${returnDetails.line3} |&| ${details}`);
|
||||
}
|
||||
|
||||
const baseDesc = `${line1Details}**${line2Details.shift()}:**\n${line2Details.join(': ')}`;
|
||||
|
||||
// Embed desc limit is 4096
|
||||
if (baseDesc.length + details.length < 4090) {
|
||||
return {
|
||||
embed: {
|
||||
color: infoColor2,
|
||||
description: `${baseDesc}\n\n${details}`,
|
||||
},
|
||||
hasAttachment: false,
|
||||
attachment: {
|
||||
blob: await new Blob(['' as BlobPart], { type: 'text' }),
|
||||
name: 'rollDetails.txt',
|
||||
},
|
||||
};
|
||||
} else {
|
||||
// If its too big, collapse it into a .txt file and send that instead.
|
||||
const b = await new Blob([`${baseDesc}\n\n${details}` as BlobPart], { type: 'text' });
|
||||
details = 'Details have been omitted from this message for being over 4000 characters.';
|
||||
if (b.size > 8388290) {
|
||||
details +=
|
||||
'\n\nFull details could not be attached to this messaged as a `.txt` file as the file would be too large for Discord to handle. If you would like to see the details of rolls, please send the rolls in multiple messages instead of bundled into one.';
|
||||
return {
|
||||
embed: {
|
||||
color: infoColor2,
|
||||
description: `${baseDesc}\n\n${details}`,
|
||||
},
|
||||
hasAttachment: false,
|
||||
attachment: {
|
||||
blob: await new Blob(['' as BlobPart], { type: 'text' }),
|
||||
name: 'rollDetails.txt',
|
||||
},
|
||||
};
|
||||
} else {
|
||||
details += '\n\nFull details have been attached to this messaged as a `.txt` file for verification purposes.';
|
||||
return {
|
||||
embed: {
|
||||
color: infoColor2,
|
||||
description: `${baseDesc}\n\n${details}`,
|
||||
},
|
||||
hasAttachment: true,
|
||||
attachment: {
|
||||
blob: b,
|
||||
name: 'rollDetails.txt',
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const line1Details = modifiers.hideRaw ? '' : `<@${authorId}>${returnDetails.line1}\n`;
|
||||
if (modifiers.gmRoll) {
|
||||
// Roll is a GM Roll, send this in the pub channel (this funciton will be ran again to get details for the GMs)
|
||||
const desc = `${line1Details}${line1Details ? '\n' : ''}Results have been messaged to the following GMs: ${
|
||||
modifiers.gms
|
||||
.map((gm) => (gm.startsWith('<') ? gm : `<@${gm}>`))
|
||||
.join(' ')
|
||||
}`;
|
||||
|
||||
return {
|
||||
charCount: desc.length,
|
||||
embed: {
|
||||
color: infoColor2,
|
||||
description: desc,
|
||||
},
|
||||
hasAttachment: false,
|
||||
};
|
||||
}
|
||||
|
||||
// Roll is normal, make normal embed
|
||||
const line2Details = returnDetails.line2.split(': ');
|
||||
let details = '';
|
||||
|
||||
if (!modifiers.superNoDetails) {
|
||||
details = `**Details:**\n${modifiers.spoiler}${returnDetails.line3}${modifiers.spoiler}`;
|
||||
loggingEnabled && log(LT.LOG, `${returnDetails.line3} |&| ${details}`);
|
||||
}
|
||||
|
||||
const baseDesc = `${line1Details}**${line2Details.shift()}:**\n${line2Details.join(': ')}`;
|
||||
|
||||
// Embed desc limit is 4096
|
||||
if (baseDesc.length + details.length < 4000) {
|
||||
// Response is valid size
|
||||
const desc = `${baseDesc}\n\n${details}`;
|
||||
return {
|
||||
charCount: desc.length,
|
||||
embed: {
|
||||
color: infoColor2,
|
||||
description: desc,
|
||||
},
|
||||
hasAttachment: false,
|
||||
};
|
||||
}
|
||||
|
||||
// Response is too big, collapse it into a .txt file and send that instead.
|
||||
const b = new Blob([`${baseDesc}\n\n${details}` as BlobPart], { type: 'text' });
|
||||
details = `${baseDesc}\n\nDetails have been omitted from this message for being over 4000 characters.`;
|
||||
if (b.size > config.maxFileSize) {
|
||||
// blob is too big, don't attach it
|
||||
details +=
|
||||
'\n\nFull details could not be attached to this messaged as a `.txt` file as the file would be too large for Discord to handle. If you would like to see the details of rolls, please send the rolls in multiple messages.';
|
||||
return {
|
||||
charCount: details.length,
|
||||
embed: {
|
||||
color: infoColor2,
|
||||
description: details,
|
||||
},
|
||||
hasAttachment: false,
|
||||
};
|
||||
}
|
||||
|
||||
// blob is small enough, attach it
|
||||
details += '\n\nFull details have been attached to this messaged as a `.txt` file for verification purposes.';
|
||||
return {
|
||||
charCount: details.length,
|
||||
embed: {
|
||||
color: infoColor2,
|
||||
description: details,
|
||||
},
|
||||
hasAttachment: true,
|
||||
attachment: {
|
||||
blob: b,
|
||||
name: 'rollDetails.txt',
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export const basicReducer = (prev: number, cur: number) => prev + cur;
|
|
@ -0,0 +1,42 @@
|
|||
import { RollDistributionMap, RollSet, RollType } from 'artigen/dice/dice.d.ts';
|
||||
|
||||
import { loopCountCheck } from 'artigen/managers/loopManager.ts';
|
||||
|
||||
// Used to generate consistent keys for rollDistributions
|
||||
export const rollDistKey = (type: RollType, size: number) => `${type}-${size}`;
|
||||
|
||||
// Converts a RollSet into a RollDistMap
|
||||
export const createRollDistMap = (rollSet: RollSet[]): RollDistributionMap => {
|
||||
const rollDistMap = new Map<string, number[]>();
|
||||
|
||||
rollSet.forEach((roll) => {
|
||||
loopCountCheck();
|
||||
const tempArr: number[] = rollDistMap.get(rollDistKey(roll.type, roll.size)) ?? new Array<number>(roll.type === 'fate' ? roll.size + 2 : roll.size).fill(0);
|
||||
tempArr[roll.type === 'fate' ? roll.roll + 1 : roll.roll - 1]++;
|
||||
rollDistMap.set(rollDistKey(roll.type, roll.size), tempArr);
|
||||
});
|
||||
|
||||
return rollDistMap;
|
||||
};
|
||||
|
||||
// Collapses an array of RollDistMaps into a single RollDistMap
|
||||
export const reduceRollDistMaps = (rollDistArr: RollDistributionMap[]): RollDistributionMap =>
|
||||
rollDistArr.reduce((acc, cur) => {
|
||||
loopCountCheck();
|
||||
|
||||
cur
|
||||
.entries()
|
||||
.toArray()
|
||||
.forEach(([key, value]) => {
|
||||
loopCountCheck();
|
||||
|
||||
const tempArr = acc.get(key) ?? new Array<number>(value.length).fill(0);
|
||||
for (let i = 0; i < tempArr.length; i++) {
|
||||
loopCountCheck();
|
||||
tempArr[i] += value[i];
|
||||
}
|
||||
|
||||
acc.set(key, tempArr);
|
||||
});
|
||||
return acc;
|
||||
}, new Map<string, number[]>());
|
|
@ -140,7 +140,7 @@ Please see attached file for audit details on cached guilds and members.`,
|
|||
},
|
||||
],
|
||||
file: {
|
||||
blob: b.size > 8388290 ? tooBig : b,
|
||||
blob: b.size > config.maxFileSize ? tooBig : b,
|
||||
name: 'auditDetails.txt',
|
||||
},
|
||||
})
|
||||
|
|
|
@ -95,6 +95,7 @@ export const apiRoll = async (query: Map<string, string>, apiUserid: bigint): Pr
|
|||
count: query.has('c'),
|
||||
commaTotals: query.has('ct'),
|
||||
confirmCrit: query.has('cc'),
|
||||
rollDist: query.has('rd'),
|
||||
apiWarn: hideWarn ? '' : apiWarning,
|
||||
valid: true,
|
||||
error: new Error(),
|
||||
|
|
Loading…
Reference in New Issue