import { _fs } from 'fs'; import { _path } from 'path'; import { _MessageFlags } from 'discord-api-types/v10'; import { SlashCommandBuilder, PermissionFlagsBits, ModalBuilder, TextInputBuilder, TextInputStyle, ActionRowBuilder } from 'discord.js'; // Placeholder info for template variables const TEMPLATE_KEYS_INFO = 'Available keys: userName, userId, locationName, locationId, date, time, datetime, clientId'; // Modal text input limits const MAX_LEN = 4000; const MAX_FIELDS = 5; /** * responsesPrompt module * Implements `/prompt [version]` to edit the current or historical prompt in a single PocketBase collection. * responses_prompts collection holds all versions; newest record per client is the live prompt. */ export const commands = [ { data: new SlashCommandBuilder() .setName('prompt') .setDescription('Edit the AI response prompt (current or past version)') .setDefaultMemberPermissions(PermissionFlagsBits.Administrator) .setDMPermission(false) .addStringOption(opt => opt.setName('version') .setDescription('ID of a past prompt version to load') .setRequired(false) .setAutocomplete(true) ), async execute(interaction, client) { const _clientId = client.config.id; const versionId = interaction.options.getString('version'); // Fetch prompt: live latest or selected historic let promptText = client.responsesPrompt || ''; if (versionId) { try { const rec = await client.pb.getOne('responses_prompts', versionId); if (rec?.prompt) promptText = rec.prompt; } catch (err) { client.logger.error(`Failed to load prompt version ${versionId}: ${err.message}`); } } // Prepare modal fields: one SHORT help, then paragraph chunks // Help field const helpField = new TextInputBuilder() .setCustomId('template_help') .setLabel('Template variables (no edits)') .setStyle(TextInputStyle.Short) .setRequired(false) // prefill with the list of usable keys .setValue(TEMPLATE_KEYS_INFO); const modal = new ModalBuilder() .setCustomId(`promptModal-${versionId || 'current'}`) .setTitle('Edit AI Prompt') .addComponents(new ActionRowBuilder().addComponents(helpField)); // Prompt chunks const chunks = []; for (let off = 0; off < promptText.length && chunks.length < MAX_FIELDS - 1; off += MAX_LEN) { chunks.push(promptText.slice(off, off + MAX_LEN)); } chunks.forEach((text, idx) => { const input = new TextInputBuilder() .setCustomId(`prompt_${idx}`) .setLabel(`Part ${idx + 1}`) .setStyle(TextInputStyle.Paragraph) .setRequired(idx === 0) .setMaxLength(MAX_LEN) .setValue(text); modal.addComponents(new ActionRowBuilder().addComponents(input)); }); // Empty fields to fill out to MAX_FIELDS for (let i = chunks.length; i < MAX_FIELDS - 1; i++) { modal.addComponents(new ActionRowBuilder().addComponents( new TextInputBuilder() .setCustomId(`prompt_${i}`) .setLabel(`Part ${i + 1}`) .setStyle(TextInputStyle.Paragraph) .setRequired(false) .setMaxLength(MAX_LEN) )); } await interaction.showModal(modal); } } ]; // Store clients for event hooks const _clients = []; export async function init(client, clientConfig) { const _clientId = client.config.id; client.logger.info('[module:responsesPrompt] initialized'); // Load live prompt (latest version) try { const { items } = await client.pb.collection('responses_prompts') .getList(1, 1, { filter: `clientId="${_clientId}"`, sort: '-created' }); client.responsesPrompt = items[0]?.prompt || ''; } catch (err) { client.logger.error(`Error loading current prompt: ${err.message}`); client.responsesPrompt = ''; } _clients.push({ client, clientConfig }); // Autocomplete versions client.on('interactionCreate', async interaction => { if (!interaction.isAutocomplete() || interaction.commandName !== 'prompt') return; const focused = interaction.options.getFocused(true); if (focused.name === 'version') { try { const { items } = await client.pb.collection('responses_prompts') .getList(1, 25, { filter: `clientId="${_clientId}"`, sort: '-created' }); const choices = items.map(r => ({ name: new Date(r.created).toLocaleString(), value: r.id })); await interaction.respond(choices); } catch (err) { client.logger.error(`Prompt autocomplete error: ${err.message}`); await interaction.respond([]); } } }); // Modal submission: save new version & prune old client.on('interactionCreate', async interaction => { if (!interaction.isModalSubmit()) return; const id = interaction.customId; if (!id.startsWith('promptModal-')) return; const parts = []; for (let i = 0; i < MAX_FIELDS; i++) { try { const v = interaction.fields.getTextInputValue(`prompt_${i}`) || ''; if (v.trim()) parts.push(v); } catch {} } const newPrompt = parts.join('\n'); // Persist new version let _newRec; try { _newRec = await client.pb.createOne('responses_prompts', { clientId: _clientId, prompt: newPrompt, updatedBy: interaction.user.id }); client.responsesPrompt = newPrompt; } catch (err) { client.logger.error(`Failed to save prompt: ${err.message}`); return interaction.reply({ content: `Error saving prompt: ${err.message}`, ephemeral: true }); } // Prune older versions beyond the 10 most recent try { const { items } = await client.pb.collection('responses_prompts') .getList(1, 100, { filter: `clientId="${_clientId}"`, sort: '-created' }); const toDelete = items.map(r => r.id).slice(10); for (const id of toDelete) { await client.pb.deleteOne('responses_prompts', id); } } catch (err) { client.logger.error(`Failed to prune old prompts: ${err.message}`); } await interaction.reply({ content: 'Prompt saved!', ephemeral: true }); }); }