src/utils/discord.ts (145 lines of code) (raw):

import { AttachmentBuilder, EmbedBuilder, BaseMessageOptions, ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedImageData, ActionRow, MessageActionRowComponent, } from "discord.js"; import { phrases } from "../phrases/phrases.json"; import { createLogo, createTiledComposite } from "./image"; import { Action, CustomIdContext } from "../Action"; import { extractImagesFromComposite } from "./image"; import { Actions } from "../Actions"; import { LOG_ERRORS } from "./constants"; import axios from "axios"; export async function createResponse( prompt: string, imageBuffers: Buffer[], buttonActions: Action[], context: CustomIdContext ): Promise<BaseMessageOptions> { const logo = await createLogo(); const composite = await createTiledComposite(imageBuffers, context.width, context.height); const files = [ new AttachmentBuilder(logo, { name: "logo.png" }), new AttachmentBuilder(composite, { name: "DALL-E.png" }), ]; const randomPhrase = phrases[Math.floor(Math.random() * phrases.length)]; const embed = new EmbedBuilder() .setImage("attachment://DALL-E.png") .setColor("#2ee66b") .setTitle(randomPhrase) .setDescription(prompt) // this is always the prompt, other objects read from this directly .setFooter({ text: "Generated with DALL-E API", iconURL: "attachment://logo.png", }); const row = rowFromActions(buttonActions, context); if (row) { return { embeds: [embed], files: files, components: [row] }; } else { return { embeds: [embed], files: files, components: [] }; } } export function actionsFromRow( row: ActionRow<MessageActionRowComponent> ): Action[] { var actions = []; for (const component of row.components) { const customId = component.customId; if (customId) { const action = Actions.find((c) => c.isAction(customId)); if (action) { actions.push(action); } } } return actions; } export function rowFromActions( actions: Action[], context: CustomIdContext ): ActionRowBuilder<ButtonBuilder> | null { if (actions.length == 0) { return null; } var row = new ActionRowBuilder<ButtonBuilder>(); for (const action of actions) { const button = new ButtonBuilder() .setCustomId(action.customId(context)) .setLabel(action.displayText) .setStyle(ButtonStyle.Secondary); row = row.addComponents(button); } return row; } export function processOpenAIError( error: any, prompt: string ): BaseMessageOptions { var result = {}; const response = error.response; if (response) { if (response.status == 429) { result = { content: `**Something went wrong!** I am slightly overworked.😮‍💨 Please wait a few minutes and I\'ll be good to go!\n Your prompt was: ${prompt}`, }; } else if (response.status >= 500 && response.status < 600) { result = { content: `**Something went wrong!** The server is experiencing issues. Please try again later.\n Your prompt was: ${prompt}`, }; } else if (response.data && response.data.error) { // custom error keys from the openai api result = { content: `**Something went wrong!** ${response.data.error.message} (${response.data.error.type}) \n Your prompt was: ${prompt}`, }; } else { result = { content: `**Something went wrong!** ${response.statusText} (${response.status}) \n Your prompt was: ${prompt}`, }; } } else { result = { content: `**Something went wrong!** ${error} \n Your prompt was: ${prompt}`, }; } return result; } export async function fetchImagesFromComposite( compositeImageData: EmbedImageData | null, count: number, imageWidth: number, imageHeight: number ): Promise<Buffer[] | null> { if (!compositeImageData || count == 0) { return null; } const width = compositeImageData.width; const height = compositeImageData.height; if (!width || !height) { return null; } try { const { data, status } = await axios.get(compositeImageData.url, { responseType: "arraybuffer", }); let compositeBuffer = Buffer.from(data); const images = await extractImagesFromComposite( compositeBuffer, width, height, count, imageWidth, imageHeight ); return images; } catch (e) { if (LOG_ERRORS) { console.log(`Save encountered an error ${e}`); } return null; } }