diff --git a/commands/chat.js b/commands/chat.js index 496956a..ed2f585 100644 --- a/commands/chat.js +++ b/commands/chat.js @@ -37,7 +37,7 @@ module.exports = { const maxTokens = interaction.options.getNumber('tokens') ?? undefined; const discordAccountId = interaction.member.id; try { - submitTextPromptTransaction(promptText, temperature, maxTokens, discordAccountId, async (err, result) => { + submitTextPromptTransaction(promptText, temperature, maxTokens, discordAccountId, interaction, this, async (err, result) => { if (err) throw err; const gptEmbed = new EmmeliaEmbedBuilder() diff --git a/commands/imagine.js b/commands/imagine.js index 2acd929..a754765 100644 --- a/commands/imagine.js +++ b/commands/imagine.js @@ -42,7 +42,7 @@ module.exports = { const size = interaction.options.getString('size') ?? undefined; const discordAccountId = interaction.member.id; try { - submitImagePromptTransaction(promptText, discordAccountId, images, size, async (err, imageResults) => { + submitImagePromptTransaction(promptText, discordAccountId, images, size, interaction, this, async (err, imageResults) => { if (err) throw err; log.DEBUG("Image Results: ", imageResults) @@ -51,7 +51,7 @@ module.exports = { .setColor(0x0099FF) .setTitle(`New Image Result`) .setDescription(`${interaction.member.user} sent the prompt: '${promptText}'`) - .addFields({ name: 'Tokens Used', value: `${this.defaultTokenUsage}`, inline: true }) + .addFields({ name: 'Tokens Used', value: `${imageResults.totalTokens}`, inline: true }) const imagesInResult = Array(imageResults.results.data).length diff --git a/controllers/openAiController.js b/controllers/openAiController.js index 0cf6a64..723cc57 100644 --- a/controllers/openAiController.js +++ b/controllers/openAiController.js @@ -3,6 +3,7 @@ const log = new DebugBuilder("server", "openAiController"); const crypto = require('crypto') const { createTransaction } = require("./transactionController"); +const { authorizeTokenUsage } = require("../middleware/balanceAuthorization"); const { encode } = require("gpt-3-encoder") const { Configuration, OpenAIApi } = require('openai'); @@ -86,7 +87,7 @@ async function getTextGeneration(_prompt, callback, { _model = "text-davinci-003 * @param {*} param1 Default parameters can be modified * @returns */ -exports.submitTextPromptTransaction = async (prompt, temperature, max_tokens, discord_account_id, callback) => { +exports.submitTextPromptTransaction = async (prompt, temperature, max_tokens, discord_account_id, interaction, command, callback) => { getTextGeneration(prompt, (err, gptResult) => { if (err) callback(err, undefined); @@ -116,29 +117,59 @@ exports.submitTextPromptTransaction = async (prompt, temperature, max_tokens, di * @param {*} image_size The size of the image ["256x256" | "512x512" | "1024x1024"] * @param {*} callback */ - exports.submitImagePromptTransaction = async (prompt, discord_account_id, images_to_generate, image_size, callback) => { + exports.submitImagePromptTransaction = async (prompt, discord_account_id, images_to_generate, image_size, interaction, command, callback) => { + let pricePerImage = 800; + log.DEBUG(image_size) + switch(image_size){ + case "1024x1024": + log.DEBUG("1024 selected"); + pricePerImage = 1000; + break; + case "512x512": + pricePerImage = 900; + log.DEBUG("512 selected"); + break; + case "256x256": + log.DEBUG("256 selected"); + pricePerImage = 800; + break; + default: + log.DEBUG("256px defaulted"); + pricePerImage = 800; + break; + } + + if (!images_to_generate) images_to_generate = 1; + if (!image_size) images_to_generate = "256x256"; - getImageGeneration(prompt, { - _image_size: image_size, - _images_to_generate: images_to_generate - }, (err, dalleResult) => { - if (err) callback(err, undefined); + totalTokensToBeUsed = pricePerImage * images_to_generate; - // TODO - Use the pricing table to calculate discord tokens - log.DEBUG("DALL-E Result", dalleResult); - const discordTokensUsed = 100; - const providerTokensUsed = 100; - const dalleResultHash = crypto.createHash('sha1').update(JSON.stringify({ discord_account_id : prompt, images_to_generate: image_size })).digest('hex') + log.DEBUG("Total tokens to be used", totalTokensToBeUsed, pricePerImage, images_to_generate); - if (dalleResult){ - createTransaction(dalleResultHash, discord_account_id, discordTokensUsed, providerTokensUsed, 2, async (err, transactionResult) => { + authorizeTokenUsage(interaction, command, totalTokensToBeUsed, (isAuthorized) => { + if (isAuthorized) { + getImageGeneration(prompt, { + _image_size: image_size, + _images_to_generate: images_to_generate + }, (err, dalleResult) => { if (err) callback(err, undefined); - - if (transactionResult){ - log.DEBUG("Transaction Created: ", transactionResult); - callback(undefined, ({ results: dalleResult, totalTokens: discordTokensUsed})); - } - }); - } - }); + + // TODO - Use the pricing table to calculate discord tokens + log.DEBUG("DALL-E Result", dalleResult); + + const dalleResultHash = crypto.createHash('sha1').update(JSON.stringify({ discord_account_id : prompt, images_to_generate: image_size })).digest('hex') + + if (dalleResult){ + createTransaction(dalleResultHash, discord_account_id, totalTokensToBeUsed, totalTokensToBeUsed, 2, async (err, transactionResult) => { + if (err) callback(err, undefined); + + if (transactionResult){ + log.DEBUG("Transaction Created: ", transactionResult); + callback(undefined, ({ results: dalleResult, totalTokens: totalTokensToBeUsed})); + } + }); + } + }); + } + }) } \ No newline at end of file diff --git a/events/interactionCreate.js b/events/interactionCreate.js index 8e3981e..eaa8f0f 100644 --- a/events/interactionCreate.js +++ b/events/interactionCreate.js @@ -20,7 +20,7 @@ module.exports = { log.DEBUG(`${interaction.member.user} is running '${interaction.commandName}'`); await authorizeCommand(interaction, command, async () => { - await authorizeTokenUsage(interaction, command, async () => { + await authorizeTokenUsage(interaction, command, undefined, async () => { try { if (command.deferInitialReply) { try { diff --git a/libStorage.js b/libStorage.js index 8974c00..960bb3c 100644 --- a/libStorage.js +++ b/libStorage.js @@ -186,6 +186,7 @@ exports.UserStorage = class UserStorage extends Storage { */ checkBalance(_tokensToBeUsed, _account_id, callback) { if (!_account_id) return callback(new Error("Account not specified when checking account balance"), undefined); + log.DEBUG("Tokens to verify against balance", _tokensToBeUsed, _account_id); if (!_tokensToBeUsed && !_tokensToBeUsed >= 0) return callback(new Error("Specified tokens are invalid when checking account balance"), undefined); this.getRecordBy('account_id', _account_id, (err, record) => { if (err) return callback(err, undefined); @@ -214,11 +215,11 @@ exports.UserStorage = class UserStorage extends Storage { switch(_updateType){ case "withdraw": // Code here to withdraw funds - sqlQuery = `UPDATE ${this.dbTable} SET balance=balance-${_updateAmount} WHERE discord_account_id = ${_account_id};`; + sqlQuery = `UPDATE ${this.dbTable} SET balance=balance-${_updateAmount} WHERE discord_account_id = ${_discord_account_id};`; break; case "deposit": // Code here to withdraw funds - sqlQuery = `UPDATE ${this.dbTable} SET balance=balance+${_updateAmount} WHERE discord_account_id = ${_account_id};`; + sqlQuery = `UPDATE ${this.dbTable} SET balance=balance+${_updateAmount} WHERE discord_account_id = ${_discord_account_id};`; break; default: log.ERROR('Update type not valid: ', _updateType); diff --git a/middleware/balanceAuthorization.js b/middleware/balanceAuthorization.js index 9c34a1e..5014650 100644 --- a/middleware/balanceAuthorization.js +++ b/middleware/balanceAuthorization.js @@ -12,13 +12,29 @@ const { welcomeResponse } = require("../controllers/accountController"); -exports.authorizeTokenUsage = async (interaction, command, next) => { +/** + * Authorize a transaction amount for an account + * + * @param {*} interaction + * @param {*} command + * @param {undefined|number} _tokens The amount of tokens to authorize, set to undefined if the value is in the interaction + * @param {*} next + * @returns + */ +exports.authorizeTokenUsage = async (interaction, command, _tokens = undefined, next) => { log.DEBUG("Command requires tokens? ", command.isPrivileged) if(!command.requiresTokens) return next(true); - if(!interaction.member && (!interaction.options.getNumber("tokens") || !command.defaultTokenUsage)) throw new Error("No member or tokens specified before attempting to authorize"); + if(!interaction.member && (!_tokens || !interaction.options.getNumber("tokens") || !command.defaultTokenUsage)) throw new Error("No member or tokens specified before attempting to authorize"); const memberId = interaction.member.id; - const tokensToBeUsed = interaction.options.getNumber("tokens") ?? command.defaultTokenUsage + var tokensToBeUsed; + + if (!_tokens || _tokens && isNaN(_tokens)){ + if (interaction.options.getNumber("tokens")) tokensToBeUsed = interaction.options.getNumber("tokens"); + else tokensToBeUsed = command.defaultTokenUsage; + } + else tokensToBeUsed = _tokens; + log.DEBUG(`Authorizing ${memberId} for a purchase worth ${tokensToBeUsed} tokens`) log.DEBUG("Checking for account associated with discord ID: ", memberId); await checkForAccount(memberId, async (err, results) => {