Spaces:
Sleeping
Sleeping
PLUTON\igor.kreyda commited on
Commit ·
66e3a81
0
Parent(s):
Initial commit
Browse files- .gitignore +30 -0
- app/bot/actions.js +50 -0
- app/bot/bot.js +195 -0
- app/bot/userSession.js +108 -0
- app/miniApp/app.js +35 -0
- app/miniApp/index.html +81 -0
- app/miniApp/settings.js +132 -0
- app/miniApp/storage.js +13 -0
- app/miniApp/styles.css +211 -0
- index.js +96 -0
- package-lock.json +1149 -0
- package.json +20 -0
- prompts/imgSystemPrompt.js +3 -0
- prompts/promptBuilder.js +98 -0
- prompts/storySystemPrompt.js +4 -0
- prompts/stylePresets.js +60 -0
- services/imageService.js +131 -0
- services/llmService.js +67 -0
- services/settings.js +23 -0
- test-llm.js +30 -0
.gitignore
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Dependencies
|
| 2 |
+
node_modules/
|
| 3 |
+
.pnp
|
| 4 |
+
.pnp.js
|
| 5 |
+
|
| 6 |
+
# Testing
|
| 7 |
+
coverage/
|
| 8 |
+
|
| 9 |
+
# Production
|
| 10 |
+
build/
|
| 11 |
+
dist/
|
| 12 |
+
|
| 13 |
+
# Misc
|
| 14 |
+
.DS_Store
|
| 15 |
+
.env
|
| 16 |
+
.env.local
|
| 17 |
+
.env.development.local
|
| 18 |
+
.env.test.local
|
| 19 |
+
.env.production.local
|
| 20 |
+
|
| 21 |
+
# Logs
|
| 22 |
+
npm-debug.log*
|
| 23 |
+
yarn-debug.log*
|
| 24 |
+
yarn-error.log*
|
| 25 |
+
|
| 26 |
+
# Editor directories and files
|
| 27 |
+
.idea
|
| 28 |
+
.vscode
|
| 29 |
+
*.swp
|
| 30 |
+
*.swo
|
app/bot/actions.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* Bot Action Handlers
|
| 3 |
+
* Handles specific bot commands and logic to keep bot.js clean.
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
const { getUserSession, setAwaitingEdit } = require('./userSession');
|
| 7 |
+
|
| 8 |
+
/**
|
| 9 |
+
* Handles the /edit_story command.
|
| 10 |
+
* @param {Object} ctx - Telegram context.
|
| 11 |
+
*/
|
| 12 |
+
async function handleEditCommand(ctx) {
|
| 13 |
+
const userId = ctx.from.id;
|
| 14 |
+
const session = getUserSession(userId);
|
| 15 |
+
|
| 16 |
+
if (!session.lastPrompt || !session.lastStory) {
|
| 17 |
+
console.log(`[Actions] User ${userId} tried to edit without history.`);
|
| 18 |
+
return ctx.reply("Сначала сгенерируйте историю. Просто отправьте мне тему для поста.");
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
// Set state: user is now expected to send edit instructions
|
| 22 |
+
setAwaitingEdit(userId, true);
|
| 23 |
+
|
| 24 |
+
await ctx.reply("Что исправить?");
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
/**
|
| 28 |
+
* Handles the /edit_image command.
|
| 29 |
+
* @param {Object} ctx - Telegram context.
|
| 30 |
+
*/
|
| 31 |
+
async function handleEditImageCommand(ctx) {
|
| 32 |
+
const userId = ctx.from.id;
|
| 33 |
+
const session = getUserSession(userId);
|
| 34 |
+
|
| 35 |
+
// Check if we have a last image to edit
|
| 36 |
+
if (!session.lastImage) {
|
| 37 |
+
console.log(`[Actions] User ${userId} tried to edit image without history.`);
|
| 38 |
+
return ctx.reply("Сначала сгенерируйте изображение.");
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
const { setAwaitingImageEdit } = require('./userSession');
|
| 42 |
+
setAwaitingImageEdit(userId, true);
|
| 43 |
+
|
| 44 |
+
await ctx.reply("Опишите, как изменить изображение?");
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
module.exports = {
|
| 48 |
+
handleEditCommand,
|
| 49 |
+
handleEditImageCommand
|
| 50 |
+
};
|
app/bot/bot.js
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* Main Bot Logic Module
|
| 3 |
+
* Initializes Telegraf bot and defines commands/events.
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
const { Telegraf, Markup } = require('telegraf');
|
| 7 |
+
|
| 8 |
+
// This function will be called from index.js
|
| 9 |
+
function setupBot(token, webAppUrl) {
|
| 10 |
+
if (!token) {
|
| 11 |
+
throw new Error('BOT_TOKEN is missing!');
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
const bot = new Telegraf(token);
|
| 15 |
+
|
| 16 |
+
// Initial setup for the menu button
|
| 17 |
+
// Note: setChatMenuButton might need to be called on specific user interaction or globally once logic is decided.
|
| 18 |
+
// For now, let's put it in the start command to ensure it updates for the user.
|
| 19 |
+
|
| 20 |
+
bot.start(async (ctx) => {
|
| 21 |
+
const welcomeMessage = `
|
| 22 |
+
Привет! Я AI Post Generator Bot. 🤖
|
| 23 |
+
|
| 24 |
+
Я помогу тебе создавать уникальные посты и изображения.
|
| 25 |
+
Нажми кнопку ниже (или "Меню" -> "Настройки"), чтобы настроить параметры генерации.
|
| 26 |
+
`;
|
| 27 |
+
|
| 28 |
+
try {
|
| 29 |
+
// Set the persistent Menu Button (bottom left of chat input)
|
| 30 |
+
// This button opens the Web App directly.
|
| 31 |
+
await ctx.setChatMenuButton({
|
| 32 |
+
type: 'web_app',
|
| 33 |
+
text: 'Настройки',
|
| 34 |
+
web_app: { url: `${webAppUrl}/app/index.html` }
|
| 35 |
+
});
|
| 36 |
+
console.log('Menu button set for user:', ctx.from.id);
|
| 37 |
+
} catch (e) {
|
| 38 |
+
console.error('Failed to set menu button:', e);
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
// Send a message with an inline button as well
|
| 42 |
+
await ctx.reply(welcomeMessage, Markup.inlineKeyboard([
|
| 43 |
+
Markup.button.webApp('Открыть настройки ⚙️', `${webAppUrl}/app/index.html`)
|
| 44 |
+
]));
|
| 45 |
+
});
|
| 46 |
+
|
| 47 |
+
// Imports for logic
|
| 48 |
+
const { buildStoryPrompt, buildImagePrompt, buildEditPrompt, buildImageRegenerationPrompt } = require('../../prompts/promptBuilder');
|
| 49 |
+
const { generateText } = require('../../services/llmService');
|
| 50 |
+
const { generateImage, regenerateImage } = require('../../services/imageService');
|
| 51 |
+
const { storySystemPrompt } = require('../../prompts/storySystemPrompt');
|
| 52 |
+
const { stylePresets } = require('../../prompts/stylePresets');
|
| 53 |
+
const { getCurrentPreset, getUserSession, updateLastGeneration, updateLastImage, setAwaitingEdit, setAwaitingImageEdit } = require('./userSession');
|
| 54 |
+
const { handleEditCommand, handleEditImageCommand } = require('./actions');
|
| 55 |
+
|
| 56 |
+
bot.help((ctx) => ctx.reply('Send /start to open the settings app.'));
|
| 57 |
+
|
| 58 |
+
// Command Handlers
|
| 59 |
+
bot.command('edit_story', handleEditCommand);
|
| 60 |
+
bot.command('edit_image', handleEditImageCommand);
|
| 61 |
+
|
| 62 |
+
// Text Message Handler
|
| 63 |
+
bot.on('text', async (ctx) => {
|
| 64 |
+
const userMessage = ctx.message.text;
|
| 65 |
+
|
| 66 |
+
// Ignore commands
|
| 67 |
+
if (userMessage.startsWith('/')) return;
|
| 68 |
+
|
| 69 |
+
try {
|
| 70 |
+
// Show typing status
|
| 71 |
+
await ctx.sendChatAction('typing');
|
| 72 |
+
|
| 73 |
+
// 1. Get Settings from session
|
| 74 |
+
const userId = ctx.from.id;
|
| 75 |
+
const session = getUserSession(userId);
|
| 76 |
+
const selectedPreset = getCurrentPreset(userId);
|
| 77 |
+
|
| 78 |
+
console.log(`[Bot] Active Preset: ${selectedPreset.preset_name}, Awaiting Edit: ${session.awaitingEdit}, Awaiting Image Edit: ${session.awaitingImageEdit}`);
|
| 79 |
+
|
| 80 |
+
// --- 2a. Check for Image Edit State ---
|
| 81 |
+
if (session.awaitingImageEdit && session.lastImage) {
|
| 82 |
+
console.log(`[Bot] Processing IMAGE EDIT request.`);
|
| 83 |
+
const regenPrompt = buildImageRegenerationPrompt(userMessage);
|
| 84 |
+
|
| 85 |
+
// Regenerate
|
| 86 |
+
const newBase64 = await regenerateImage(regenPrompt, session.lastImage);
|
| 87 |
+
|
| 88 |
+
// Send and Save
|
| 89 |
+
await ctx.replyWithPhoto({ source: Buffer.from(newBase64, 'base64') });
|
| 90 |
+
updateLastImage(userId, newBase64);
|
| 91 |
+
setAwaitingImageEdit(userId, false);
|
| 92 |
+
return; // Stop here, don't generate story
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
let currentPrompt = "";
|
| 96 |
+
|
| 97 |
+
// 2. Build Prompt based on state (Text Edit)
|
| 98 |
+
if (session.awaitingEdit && session.lastPrompt && session.lastStory) {
|
| 99 |
+
console.log(`[Bot] Processing TEXT EDIT request from user ${userId}`);
|
| 100 |
+
currentPrompt = buildEditPrompt(userMessage, session.lastPrompt, session.lastStory);
|
| 101 |
+
} else {
|
| 102 |
+
currentPrompt = buildStoryPrompt(
|
| 103 |
+
userMessage,
|
| 104 |
+
storySystemPrompt,
|
| 105 |
+
selectedPreset
|
| 106 |
+
);
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
// 3. Call LLM Service for Story
|
| 110 |
+
let responseText = await generateText(currentPrompt);
|
| 111 |
+
|
| 112 |
+
// --- Tags Logic ---
|
| 113 |
+
const userTagsString = session.tags || "";
|
| 114 |
+
// Split user tags by space/newline, filter empty, ensure they start with #
|
| 115 |
+
const userTagsArray = userTagsString.split(/[\s\n]+/).filter(t => t.startsWith('#'));
|
| 116 |
+
|
| 117 |
+
// Regex to capture hashtags at the end of the text
|
| 118 |
+
const tagRegex = /((?:#[\w\u0590-\u05ff]+(?:\s+|$))+)$/u;
|
| 119 |
+
|
| 120 |
+
let storyText = responseText.trim();
|
| 121 |
+
const llmTagsArray = [];
|
| 122 |
+
|
| 123 |
+
const match = storyText.match(tagRegex);
|
| 124 |
+
if (match) {
|
| 125 |
+
const tagsPart = match[1];
|
| 126 |
+
storyText = storyText.substring(0, storyText.length - tagsPart.length).trim();
|
| 127 |
+
const extracted = tagsPart.match(/#[\w\u0590-\u05ff]+/gu);
|
| 128 |
+
if (extracted) {
|
| 129 |
+
llmTagsArray.push(...extracted);
|
| 130 |
+
}
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
// Merge unique tags
|
| 134 |
+
const allTags = [...new Set([...llmTagsArray, ...userTagsArray])];
|
| 135 |
+
|
| 136 |
+
// Format: Story text + double newline + all tags in one line
|
| 137 |
+
const finalMessage = `${storyText}\n\n${allTags.join(' ')}`;
|
| 138 |
+
|
| 139 |
+
// 4. Send Story Response to user
|
| 140 |
+
await ctx.reply(finalMessage);
|
| 141 |
+
|
| 142 |
+
// --- Save History and Reset State ---
|
| 143 |
+
updateLastGeneration(userId, currentPrompt, storyText);
|
| 144 |
+
setAwaitingEdit(userId, false);
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
// 5. Generate Image Prompt based on the Story
|
| 148 |
+
await ctx.sendChatAction('upload_photo');
|
| 149 |
+
|
| 150 |
+
// Keep "upload_photo" status alive
|
| 151 |
+
const statusInterval = setInterval(() => {
|
| 152 |
+
ctx.sendChatAction('upload_photo').catch(e => console.error('[Bot] Action error:', e));
|
| 153 |
+
}, 4000);
|
| 154 |
+
|
| 155 |
+
try {
|
| 156 |
+
// If this was an edit, we might want to regenerate image for the new story OR just keep it.
|
| 157 |
+
// Current workflow: Always generate new image for new story/edited story.
|
| 158 |
+
|
| 159 |
+
// If it's a Text Edit, we probably want a new image too?
|
| 160 |
+
// Let's assume yes for now, or we can make it optional.
|
| 161 |
+
// The user requested to uncomment the block, implying we want images back.
|
| 162 |
+
|
| 163 |
+
const imagePromptLayout = buildImagePrompt(
|
| 164 |
+
responseText,
|
| 165 |
+
selectedPreset.image_style_suffix
|
| 166 |
+
);
|
| 167 |
+
|
| 168 |
+
// Get refined prompt from LLM
|
| 169 |
+
const refinedImagePrompt = await generateText(imagePromptLayout);
|
| 170 |
+
console.log('[Bot] Refined Image Prompt:', refinedImagePrompt);
|
| 171 |
+
|
| 172 |
+
// 6. Generate Image
|
| 173 |
+
const base64Image = await generateImage(refinedImagePrompt);
|
| 174 |
+
|
| 175 |
+
// 7. Send Photo
|
| 176 |
+
await ctx.replyWithPhoto({ source: Buffer.from(base64Image, 'base64') });
|
| 177 |
+
|
| 178 |
+
// Save for future edits
|
| 179 |
+
updateLastImage(userId, base64Image);
|
| 180 |
+
|
| 181 |
+
} finally {
|
| 182 |
+
clearInterval(statusInterval);
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
} catch (error) {
|
| 187 |
+
console.error('Error handling message:', error);
|
| 188 |
+
await ctx.reply('Произошла ошибка при генерации ответа. Попробуйте позже.');
|
| 189 |
+
}
|
| 190 |
+
});
|
| 191 |
+
|
| 192 |
+
return bot;
|
| 193 |
+
}
|
| 194 |
+
|
| 195 |
+
module.exports = { setupBot };
|
app/bot/userSession.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* User Session Store
|
| 3 |
+
* Simple in-memory storage for user settings (active preset, etc).
|
| 4 |
+
* In a real app, this should be a database (Postgres, MongoDB).
|
| 5 |
+
*/
|
| 6 |
+
|
| 7 |
+
const userSessions = {};
|
| 8 |
+
const { stylePresets } = require('../../prompts/stylePresets');
|
| 9 |
+
|
| 10 |
+
// Default preset (usually the first one)
|
| 11 |
+
const DEFAULT_PRESET_NAME = stylePresets[0].preset_name;
|
| 12 |
+
|
| 13 |
+
/**
|
| 14 |
+
* Returns the user session or initializes it with default values.
|
| 15 |
+
* @param {number|string} userId
|
| 16 |
+
*/
|
| 17 |
+
function getUserSession(userId) {
|
| 18 |
+
if (!userSessions[userId]) {
|
| 19 |
+
userSessions[userId] = {
|
| 20 |
+
currentPreset: DEFAULT_PRESET_NAME,
|
| 21 |
+
creativity: 0.7,
|
| 22 |
+
tags: "#fblifestyle #kindnessmatters #kindness",
|
| 23 |
+
lastPrompt: null,
|
| 24 |
+
lastStory: null,
|
| 25 |
+
lastImage: null,
|
| 26 |
+
awaitingEdit: false,
|
| 27 |
+
awaitingImageEdit: false
|
| 28 |
+
};
|
| 29 |
+
}
|
| 30 |
+
return userSessions[userId];
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
/**
|
| 34 |
+
* Updates various user settings in the session.
|
| 35 |
+
*/
|
| 36 |
+
function updateUserSession(userId, data) {
|
| 37 |
+
const session = getUserSession(userId);
|
| 38 |
+
|
| 39 |
+
// Update Preset if provided
|
| 40 |
+
if (data.presetName) {
|
| 41 |
+
const preset = stylePresets.find(p => p.preset_name === data.presetName);
|
| 42 |
+
if (preset) {
|
| 43 |
+
session.currentPreset = data.presetName;
|
| 44 |
+
}
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
// Update Creativity if provided
|
| 48 |
+
if (data.creativity !== undefined) {
|
| 49 |
+
session.creativity = parseFloat(data.creativity);
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
// Update Tags if provided
|
| 53 |
+
if (data.tags !== undefined) {
|
| 54 |
+
session.tags = data.tags;
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
return true;
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
/**
|
| 61 |
+
* Saves the last generation context for editing.
|
| 62 |
+
*/
|
| 63 |
+
function updateLastGeneration(userId, prompt, story) {
|
| 64 |
+
const session = getUserSession(userId);
|
| 65 |
+
session.lastPrompt = prompt;
|
| 66 |
+
session.lastStory = story;
|
| 67 |
+
return true;
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
/**
|
| 71 |
+
* Saves the last generated image.
|
| 72 |
+
*/
|
| 73 |
+
function updateLastImage(userId, imageBase64) {
|
| 74 |
+
const session = getUserSession(userId);
|
| 75 |
+
session.lastImage = imageBase64;
|
| 76 |
+
return true;
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
/**
|
| 80 |
+
* Sets the "awaiting edit message" state.
|
| 81 |
+
*/
|
| 82 |
+
function setAwaitingEdit(userId, value) {
|
| 83 |
+
const session = getUserSession(userId);
|
| 84 |
+
session.awaitingEdit = !!value;
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
function setAwaitingImageEdit(userId, value) {
|
| 88 |
+
const session = getUserSession(userId);
|
| 89 |
+
session.awaitingImageEdit = !!value;
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
/**
|
| 93 |
+
* Returns the full preset object for the user's current choice.
|
| 94 |
+
*/
|
| 95 |
+
function getCurrentPreset(userId) {
|
| 96 |
+
const session = getUserSession(userId);
|
| 97 |
+
return stylePresets.find(p => p.preset_name === session.currentPreset) || stylePresets[0];
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
module.exports = {
|
| 101 |
+
getUserSession,
|
| 102 |
+
updateUserSession,
|
| 103 |
+
getCurrentPreset,
|
| 104 |
+
updateLastGeneration,
|
| 105 |
+
updateLastImage,
|
| 106 |
+
setAwaitingEdit,
|
| 107 |
+
setAwaitingImageEdit
|
| 108 |
+
};
|
app/miniApp/app.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* Main Mini App Logic
|
| 3 |
+
* Handles tab switching and initialization of sub-components.
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 7 |
+
// Initialize Tabs
|
| 8 |
+
const tabs = document.querySelectorAll('.nav-item');
|
| 9 |
+
const contents = document.querySelectorAll('.tab-content');
|
| 10 |
+
|
| 11 |
+
tabs.forEach(tab => {
|
| 12 |
+
tab.addEventListener('click', () => {
|
| 13 |
+
// Remove active class from all
|
| 14 |
+
tabs.forEach(t => t.classList.remove('active'));
|
| 15 |
+
contents.forEach(c => c.classList.remove('active'));
|
| 16 |
+
|
| 17 |
+
// Add active class to clicked
|
| 18 |
+
tab.classList.add('active');
|
| 19 |
+
|
| 20 |
+
// Show corresponding content
|
| 21 |
+
const targetId = tab.dataset.tab;
|
| 22 |
+
document.getElementById(targetId).classList.add('active');
|
| 23 |
+
});
|
| 24 |
+
});
|
| 25 |
+
|
| 26 |
+
// Initialize individual tab logic
|
| 27 |
+
if (window.settingsTab) window.settingsTab.init();
|
| 28 |
+
if (window.storageTab) window.storageTab.init();
|
| 29 |
+
|
| 30 |
+
// Telegram WebApp expansion
|
| 31 |
+
if (window.Telegram && window.Telegram.WebApp) {
|
| 32 |
+
window.Telegram.WebApp.ready();
|
| 33 |
+
window.Telegram.WebApp.expand();
|
| 34 |
+
}
|
| 35 |
+
});
|
app/miniApp/index.html
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
|
| 4 |
+
<head>
|
| 5 |
+
<meta charset="UTF-8">
|
| 6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, maximum-scale=1">
|
| 7 |
+
<title>Configurator</title>
|
| 8 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
|
| 9 |
+
<link rel="stylesheet" href="styles.css">
|
| 10 |
+
<!-- Telegram Web App Script -->
|
| 11 |
+
<script src="https://telegram.org/js/telegram-web-app.js"></script>
|
| 12 |
+
</head>
|
| 13 |
+
|
| 14 |
+
<body>
|
| 15 |
+
|
| 16 |
+
<div class="main-content">
|
| 17 |
+
<!-- Settings Tab Content (Default) -->
|
| 18 |
+
<div id="settings-tab" class="tab-content active">
|
| 19 |
+
<h2>Настройки</h2>
|
| 20 |
+
|
| 21 |
+
<div class="form-group">
|
| 22 |
+
<label for="tags-input">Теги (разделяйте пробелом)</label>
|
| 23 |
+
<textarea id="tags-input" class="tags-box"
|
| 24 |
+
placeholder="#tag1 #tag2">#fblifestyle #kindnessmatters #kindness</textarea>
|
| 25 |
+
</div>
|
| 26 |
+
|
| 27 |
+
<div class="form-group">
|
| 28 |
+
<label for="creativity">Креативность (Температура)</label>
|
| 29 |
+
<input type="range" id="creativity" min="0" max="1" step="0.1" value="0.7">
|
| 30 |
+
<div class="slider-value" id="creativity-val">0.7</div>
|
| 31 |
+
</div>
|
| 32 |
+
|
| 33 |
+
<div class="form-group">
|
| 34 |
+
<label for="preset-select">Пресет</label>
|
| 35 |
+
<select id="preset-select">
|
| 36 |
+
<!-- Options populated by JS -->
|
| 37 |
+
</select>
|
| 38 |
+
</div>
|
| 39 |
+
|
| 40 |
+
<div class="form-group">
|
| 41 |
+
<label for="preset-description">Описание пресета</label>
|
| 42 |
+
<textarea id="preset-description" class="description-box" readonly></textarea>
|
| 43 |
+
</div>
|
| 44 |
+
</div>
|
| 45 |
+
|
| 46 |
+
<!-- Storage Tab Content -->
|
| 47 |
+
<div id="storage-tab" class="tab-content">
|
| 48 |
+
<h2>Хранилище</h2>
|
| 49 |
+
<div class="form-group">
|
| 50 |
+
<p style="color: #888; text-align: center;">Здесь будут сохраненные материалы.</p>
|
| 51 |
+
</div>
|
| 52 |
+
</div>
|
| 53 |
+
</div>
|
| 54 |
+
|
| 55 |
+
<!-- Bottom Navigation -->
|
| 56 |
+
<nav class="bottom-nav">
|
| 57 |
+
<div class="nav-item active" data-tab="settings-tab">
|
| 58 |
+
<svg class="nav-icon" viewBox="0 0 24 24">
|
| 59 |
+
<path
|
| 60 |
+
d="M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.85,9.49l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z" />
|
| 61 |
+
</svg>
|
| 62 |
+
<span class="nav-label">Настройки</span>
|
| 63 |
+
</div>
|
| 64 |
+
<div class="nav-item" data-tab="storage-tab">
|
| 65 |
+
<svg class="nav-icon" viewBox="0 0 24 24">
|
| 66 |
+
<path
|
| 67 |
+
d="M20,6h-8l-2-2H4C2.9,4,2.01,4.9,2.01,6L2,18c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8C22,6.9,21.1,6,20,6z M14,16H6v-2h8V16z M18,12H6v-2h12V12z" />
|
| 68 |
+
</svg>
|
| 69 |
+
<span class="nav-label">Хранилище</span>
|
| 70 |
+
</div>
|
| 71 |
+
</nav>
|
| 72 |
+
|
| 73 |
+
<!-- Shared Data -->
|
| 74 |
+
<script src="../../prompts/stylePresets.js"></script>
|
| 75 |
+
|
| 76 |
+
<script src="settings.js"></script>
|
| 77 |
+
<script src="storage.js"></script>
|
| 78 |
+
<script src="app.js"></script>
|
| 79 |
+
</body>
|
| 80 |
+
|
| 81 |
+
</html>
|
app/miniApp/settings.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* Settings Tab Logic
|
| 3 |
+
* Handles interaction within the Settings tab.
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
const settingsTab = {
|
| 7 |
+
init: function () {
|
| 8 |
+
console.log("Settings Tab Initialized");
|
| 9 |
+
this.cacheDOM();
|
| 10 |
+
this.bindEvents();
|
| 11 |
+
this.loadPresets();
|
| 12 |
+
},
|
| 13 |
+
|
| 14 |
+
cacheDOM: function () {
|
| 15 |
+
this.creativitySlider = document.getElementById('creativity');
|
| 16 |
+
this.creativityValue = document.getElementById('creativity-val');
|
| 17 |
+
this.presetSelect = document.getElementById('preset-select');
|
| 18 |
+
this.presetDesc = document.getElementById('preset-description');
|
| 19 |
+
this.tagsInput = document.getElementById('tags-input');
|
| 20 |
+
},
|
| 21 |
+
|
| 22 |
+
bindEvents: function () {
|
| 23 |
+
if (this.creativitySlider) {
|
| 24 |
+
this.creativitySlider.addEventListener('input', (e) => {
|
| 25 |
+
this.creativityValue.textContent = e.target.value;
|
| 26 |
+
// Optional: Save on change
|
| 27 |
+
// this.saveSettings();
|
| 28 |
+
});
|
| 29 |
+
this.creativitySlider.addEventListener('change', () => this.saveSettings());
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
if (this.presetSelect) {
|
| 33 |
+
this.presetSelect.addEventListener('change', (e) => {
|
| 34 |
+
this.updateDescription(e.target.value);
|
| 35 |
+
// updateDescription calls saveSettings, so we are good.
|
| 36 |
+
});
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
if (this.tagsInput) {
|
| 40 |
+
this.tagsInput.addEventListener('change', () => {
|
| 41 |
+
this.saveSettings();
|
| 42 |
+
});
|
| 43 |
+
// Also save on blur to be safe
|
| 44 |
+
this.tagsInput.addEventListener('blur', () => {
|
| 45 |
+
this.saveSettings();
|
| 46 |
+
});
|
| 47 |
+
}
|
| 48 |
+
},
|
| 49 |
+
|
| 50 |
+
loadPresets: function () {
|
| 51 |
+
// Load presets from the shared global variable (loaded via script tag)
|
| 52 |
+
if (typeof window.stylePresets === 'undefined') {
|
| 53 |
+
console.error("Error: stylePresets not loaded. Ensure stylePresets.js is included.");
|
| 54 |
+
this.presetSelect.innerHTML = '<option disabled>Presets not available</option>';
|
| 55 |
+
return;
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
const presets = window.stylePresets;
|
| 59 |
+
|
| 60 |
+
// Clear existing options
|
| 61 |
+
this.presetSelect.innerHTML = '';
|
| 62 |
+
|
| 63 |
+
presets.forEach(preset => {
|
| 64 |
+
const option = document.createElement('option');
|
| 65 |
+
// Using preset_name as value.
|
| 66 |
+
option.value = preset.preset_name;
|
| 67 |
+
option.textContent = preset.preset_name.replace(/_/g, ' '); // Beautify display name
|
| 68 |
+
option.dataset.description = preset.description;
|
| 69 |
+
this.presetSelect.appendChild(option);
|
| 70 |
+
});
|
| 71 |
+
|
| 72 |
+
// Trigger change to set initial description
|
| 73 |
+
if (presets.length > 0) {
|
| 74 |
+
this.updateDescription(presets[0].preset_name);
|
| 75 |
+
}
|
| 76 |
+
},
|
| 77 |
+
|
| 78 |
+
updateDescription: function (presetId) {
|
| 79 |
+
const selectedOption = this.presetSelect.querySelector(`option[value="${presetId}"]`);
|
| 80 |
+
if (selectedOption) {
|
| 81 |
+
this.presetDesc.value = selectedOption.dataset.description;
|
| 82 |
+
this.saveSettings();
|
| 83 |
+
} else {
|
| 84 |
+
this.presetDesc.value = "";
|
| 85 |
+
}
|
| 86 |
+
},
|
| 87 |
+
|
| 88 |
+
saveSettings: function () {
|
| 89 |
+
// Get user ID from Telegram WebApp
|
| 90 |
+
if (!window.Telegram || !window.Telegram.WebApp || !window.Telegram.WebApp.initDataUnsafe.user) {
|
| 91 |
+
console.warn("Settings not saved: Not running inside Telegram or user info missing.");
|
| 92 |
+
return;
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
const userId = window.Telegram.WebApp.initDataUnsafe.user.id;
|
| 96 |
+
|
| 97 |
+
// Gather all settings from DOM
|
| 98 |
+
const presetName = this.presetSelect.value;
|
| 99 |
+
const creativity = this.creativitySlider.value;
|
| 100 |
+
const tags = this.tagsInput.value;
|
| 101 |
+
|
| 102 |
+
console.log(`Saving settings for user ${userId}: Preset=${presetName}, Creativity=${creativity}, Tags=${tags}`);
|
| 103 |
+
|
| 104 |
+
fetch('/api/settings', {
|
| 105 |
+
method: 'POST',
|
| 106 |
+
headers: {
|
| 107 |
+
'Content-Type': 'application/json'
|
| 108 |
+
},
|
| 109 |
+
body: JSON.stringify({
|
| 110 |
+
userId,
|
| 111 |
+
presetName,
|
| 112 |
+
creativity,
|
| 113 |
+
tags
|
| 114 |
+
})
|
| 115 |
+
})
|
| 116 |
+
.then(response => response.json())
|
| 117 |
+
.then(data => {
|
| 118 |
+
if (data.success) {
|
| 119 |
+
console.log("Settings saved successfully");
|
| 120 |
+
} else {
|
| 121 |
+
console.error("Failed to save settings:", data.error);
|
| 122 |
+
}
|
| 123 |
+
})
|
| 124 |
+
.catch(err => {
|
| 125 |
+
console.error("Error saving settings:", err);
|
| 126 |
+
});
|
| 127 |
+
}
|
| 128 |
+
};
|
| 129 |
+
|
| 130 |
+
// Export for use in app.js if using modules, or just global if simple script inclusion.
|
| 131 |
+
// For simple usage without bundlers, we can attach to window or just leave it available.
|
| 132 |
+
window.settingsTab = settingsTab;
|
app/miniApp/storage.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* Storage Tab Logic
|
| 3 |
+
* Handles interaction within the Storage tab.
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
const storageTab = {
|
| 7 |
+
init: function () {
|
| 8 |
+
console.log("Storage Tab Initialized");
|
| 9 |
+
// Logic for loading stored items will go here
|
| 10 |
+
}
|
| 11 |
+
};
|
| 12 |
+
|
| 13 |
+
window.storageTab = storageTab;
|
app/miniApp/styles.css
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
:root {
|
| 2 |
+
--bg-color: #121212;
|
| 3 |
+
--surface-color: #1e1e1e;
|
| 4 |
+
--primary-color: #6200ee;
|
| 5 |
+
--primary-variant: #3700b3;
|
| 6 |
+
--secondary-color: #03dac6;
|
| 7 |
+
--text-primary: #ffffff;
|
| 8 |
+
--text-secondary: #b0b0b0;
|
| 9 |
+
--border-radius: 12px;
|
| 10 |
+
--spacing-unit: 16px;
|
| 11 |
+
--bottom-nav-height: 60px;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
* {
|
| 15 |
+
box-sizing: border-box;
|
| 16 |
+
margin: 0;
|
| 17 |
+
padding: 0;
|
| 18 |
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
body {
|
| 22 |
+
background-color: var(--bg-color);
|
| 23 |
+
color: var(--text-primary);
|
| 24 |
+
height: 100vh;
|
| 25 |
+
display: flex;
|
| 26 |
+
flex-direction: column;
|
| 27 |
+
overflow: hidden;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
/* Header/Content Area */
|
| 31 |
+
.main-content {
|
| 32 |
+
flex: 1;
|
| 33 |
+
overflow-y: auto;
|
| 34 |
+
padding: var(--spacing-unit);
|
| 35 |
+
padding-bottom: calc(var(--bottom-nav-height) + var(--spacing-unit));
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
.tab-content {
|
| 39 |
+
display: none;
|
| 40 |
+
animation: fadeIn 0.3s ease-in-out;
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
.tab-content.active {
|
| 44 |
+
display: block;
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
@keyframes fadeIn {
|
| 48 |
+
from {
|
| 49 |
+
opacity: 0;
|
| 50 |
+
transform: translateY(10px);
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
to {
|
| 54 |
+
opacity: 1;
|
| 55 |
+
transform: translateY(0);
|
| 56 |
+
}
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
h2 {
|
| 60 |
+
margin-bottom: var(--spacing-unit);
|
| 61 |
+
font-weight: 600;
|
| 62 |
+
color: var(--text-primary);
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
/* Form Elements */
|
| 66 |
+
.form-group {
|
| 67 |
+
margin-bottom: 24px;
|
| 68 |
+
background: var(--surface-color);
|
| 69 |
+
padding: 16px;
|
| 70 |
+
border-radius: var(--border-radius);
|
| 71 |
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
label {
|
| 75 |
+
display: block;
|
| 76 |
+
margin-bottom: 8px;
|
| 77 |
+
color: var(--text-secondary);
|
| 78 |
+
font-size: 0.9em;
|
| 79 |
+
font-weight: 500;
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
/* Slider */
|
| 83 |
+
input[type=range] {
|
| 84 |
+
width: 100%;
|
| 85 |
+
-webkit-appearance: none;
|
| 86 |
+
appearance: none;
|
| 87 |
+
background: transparent;
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
input[type=range]::-webkit-slider-thumb {
|
| 91 |
+
-webkit-appearance: none;
|
| 92 |
+
height: 20px;
|
| 93 |
+
width: 20px;
|
| 94 |
+
border-radius: 50%;
|
| 95 |
+
background: var(--primary-color);
|
| 96 |
+
cursor: pointer;
|
| 97 |
+
margin-top: -8px;
|
| 98 |
+
box-shadow: 0 0 10px rgba(98, 0, 238, 0.5);
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
input[type=range]::-webkit-slider-runnable-track {
|
| 102 |
+
width: 100%;
|
| 103 |
+
height: 4px;
|
| 104 |
+
cursor: pointer;
|
| 105 |
+
background: #444;
|
| 106 |
+
border-radius: 2px;
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
input[type=range]:focus {
|
| 110 |
+
outline: none;
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
.slider-value {
|
| 114 |
+
text-align: right;
|
| 115 |
+
font-size: 0.85em;
|
| 116 |
+
color: var(--primary-color);
|
| 117 |
+
margin-top: 4px;
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
/* Select */
|
| 121 |
+
select {
|
| 122 |
+
width: 100%;
|
| 123 |
+
padding: 12px;
|
| 124 |
+
background-color: #2c2c2c;
|
| 125 |
+
color: var(--text-primary);
|
| 126 |
+
border: 1px solid #444;
|
| 127 |
+
border-radius: 8px;
|
| 128 |
+
font-size: 1em;
|
| 129 |
+
appearance: none;
|
| 130 |
+
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
|
| 131 |
+
background-repeat: no-repeat;
|
| 132 |
+
background-position: right 1rem center;
|
| 133 |
+
background-size: 1em;
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
select:focus {
|
| 137 |
+
outline: none;
|
| 138 |
+
border-color: var(--primary-color);
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
/* Description Box */
|
| 142 |
+
.description-box {
|
| 143 |
+
width: 100%;
|
| 144 |
+
min-height: 100px;
|
| 145 |
+
padding: 12px;
|
| 146 |
+
background-color: #2c2c2c;
|
| 147 |
+
color: #ddd;
|
| 148 |
+
border: 1px solid #444;
|
| 149 |
+
border-radius: 8px;
|
| 150 |
+
font-size: 0.95em;
|
| 151 |
+
line-height: 1.5;
|
| 152 |
+
resize: none;
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
/* Tags Box */
|
| 156 |
+
.tags-box {
|
| 157 |
+
width: 100%;
|
| 158 |
+
min-height: 80px;
|
| 159 |
+
padding: 12px;
|
| 160 |
+
background-color: #2c2c2c;
|
| 161 |
+
color: #ddd;
|
| 162 |
+
border: 1px solid #444;
|
| 163 |
+
border-radius: 8px;
|
| 164 |
+
font-size: 0.95em;
|
| 165 |
+
line-height: 1.5;
|
| 166 |
+
resize: none;
|
| 167 |
+
font-family: monospace;
|
| 168 |
+
/* To make tags look code-like */
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
/* Bottom Navigation */
|
| 172 |
+
.bottom-nav {
|
| 173 |
+
position: fixed;
|
| 174 |
+
bottom: 0;
|
| 175 |
+
width: 100%;
|
| 176 |
+
height: var(--bottom-nav-height);
|
| 177 |
+
background-color: var(--surface-color);
|
| 178 |
+
display: flex;
|
| 179 |
+
justify-content: space-around;
|
| 180 |
+
align-items: center;
|
| 181 |
+
border-top: 1px solid #333;
|
| 182 |
+
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.2);
|
| 183 |
+
z-index: 100;
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
.nav-item {
|
| 187 |
+
display: flex;
|
| 188 |
+
flex-direction: column;
|
| 189 |
+
align-items: center;
|
| 190 |
+
justify-content: center;
|
| 191 |
+
color: var(--text-secondary);
|
| 192 |
+
padding: 8px;
|
| 193 |
+
cursor: pointer;
|
| 194 |
+
transition: color 0.2s;
|
| 195 |
+
width: 50%;
|
| 196 |
+
}
|
| 197 |
+
|
| 198 |
+
.nav-item.active {
|
| 199 |
+
color: var(--primary-color);
|
| 200 |
+
}
|
| 201 |
+
|
| 202 |
+
.nav-icon {
|
| 203 |
+
width: 24px;
|
| 204 |
+
height: 24px;
|
| 205 |
+
margin-bottom: 4px;
|
| 206 |
+
fill: currentColor;
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
.nav-label {
|
| 210 |
+
font-size: 0.75em;
|
| 211 |
+
}
|
index.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
require('dotenv').config();
|
| 2 |
+
const express = require('express');
|
| 3 |
+
const cors = require('cors');
|
| 4 |
+
const path = require('path');
|
| 5 |
+
|
| 6 |
+
// Modular imports
|
| 7 |
+
const { setupBot } = require('./app/bot/bot');
|
| 8 |
+
|
| 9 |
+
const app = express();
|
| 10 |
+
const PORT = process.env.PORT || 3000;
|
| 11 |
+
const WEBAPP_URL = process.env.WEBAPP_URL || 'https://your-domain.com';
|
| 12 |
+
|
| 13 |
+
// --- Express Server Setup ---
|
| 14 |
+
app.use(cors());
|
| 15 |
+
app.use(express.json());
|
| 16 |
+
|
| 17 |
+
// Request logging middleware
|
| 18 |
+
app.use((req, res, next) => {
|
| 19 |
+
console.log(`[Server] ${req.method} ${req.url}`);
|
| 20 |
+
next();
|
| 21 |
+
});
|
| 22 |
+
|
| 23 |
+
// Serve static files for Mini App
|
| 24 |
+
// We map '/app' URL path to the local 'app/miniApp' directory
|
| 25 |
+
app.use('/app', express.static(path.join(__dirname, 'app/miniApp')));
|
| 26 |
+
|
| 27 |
+
// Serve prompts so the Mini App can load stylePresets.js
|
| 28 |
+
// We map '/prompts' URL path to the local 'prompts' directory
|
| 29 |
+
app.use('/prompts', express.static(path.join(__dirname, 'prompts')));
|
| 30 |
+
|
| 31 |
+
// Basic health check
|
| 32 |
+
app.get('/', (req, res) => {
|
| 33 |
+
res.send('Bot and Mini App Server are running.');
|
| 34 |
+
});
|
| 35 |
+
|
| 36 |
+
// --- API Endpoints ---
|
| 37 |
+
const { updateUserSession } = require('./app/bot/userSession');
|
| 38 |
+
|
| 39 |
+
app.post('/api/settings', (req, res) => {
|
| 40 |
+
console.log('[API] Received /api/settings request:', req.body);
|
| 41 |
+
const { userId, presetName, creativity, tags } = req.body;
|
| 42 |
+
|
| 43 |
+
if (!userId) {
|
| 44 |
+
return res.status(400).json({ error: 'Missing userId' });
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
// We pass all fields to the update function
|
| 48 |
+
const success = updateUserSession(userId, { presetName, creativity, tags });
|
| 49 |
+
|
| 50 |
+
if (success) {
|
| 51 |
+
console.log(`[API] Settings updated for user ${userId}`);
|
| 52 |
+
res.json({ success: true });
|
| 53 |
+
} else {
|
| 54 |
+
res.status(500).json({ error: 'Failed to update settings' });
|
| 55 |
+
}
|
| 56 |
+
});
|
| 57 |
+
|
| 58 |
+
// --- Bot Setup ---
|
| 59 |
+
let bot;
|
| 60 |
+
try {
|
| 61 |
+
bot = setupBot(process.env.BOT_TOKEN, WEBAPP_URL);
|
| 62 |
+
} catch (error) {
|
| 63 |
+
console.error("Failed to initialize bot:", error.message);
|
| 64 |
+
process.exit(1);
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
// --- Start Server & Bot ---
|
| 68 |
+
async function start() {
|
| 69 |
+
try {
|
| 70 |
+
// Start Express Server
|
| 71 |
+
app.listen(PORT, () => {
|
| 72 |
+
console.log(`🚀 Server running on port ${PORT}`);
|
| 73 |
+
console.log(`🌍 Web App URL: ${WEBAPP_URL}/app/index.html`);
|
| 74 |
+
});
|
| 75 |
+
|
| 76 |
+
// Start Telegram Bot
|
| 77 |
+
console.log('🤖 Bot is launching...');
|
| 78 |
+
await bot.launch();
|
| 79 |
+
console.log('✅ Bot is running.');
|
| 80 |
+
|
| 81 |
+
} catch (e) {
|
| 82 |
+
console.error('❌ Error starting app:', e);
|
| 83 |
+
}
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
// Graceful stop
|
| 87 |
+
process.once('SIGINT', () => {
|
| 88 |
+
if (bot) bot.stop('SIGINT');
|
| 89 |
+
process.exit(0);
|
| 90 |
+
});
|
| 91 |
+
process.once('SIGTERM', () => {
|
| 92 |
+
if (bot) bot.stop('SIGTERM');
|
| 93 |
+
process.exit(0);
|
| 94 |
+
});
|
| 95 |
+
|
| 96 |
+
start();
|
package-lock.json
ADDED
|
@@ -0,0 +1,1149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "aipostgenerator",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
+
"lockfileVersion": 3,
|
| 5 |
+
"requires": true,
|
| 6 |
+
"packages": {
|
| 7 |
+
"": {
|
| 8 |
+
"name": "aipostgenerator",
|
| 9 |
+
"version": "1.0.0",
|
| 10 |
+
"license": "ISC",
|
| 11 |
+
"dependencies": {
|
| 12 |
+
"axios": "^1.13.5",
|
| 13 |
+
"cors": "^2.8.6",
|
| 14 |
+
"dotenv": "^17.2.4",
|
| 15 |
+
"express": "^5.2.1",
|
| 16 |
+
"telegraf": "^4.16.3"
|
| 17 |
+
}
|
| 18 |
+
},
|
| 19 |
+
"node_modules/@telegraf/types": {
|
| 20 |
+
"version": "7.1.0",
|
| 21 |
+
"resolved": "https://registry.npmjs.org/@telegraf/types/-/types-7.1.0.tgz",
|
| 22 |
+
"integrity": "sha512-kGevOIbpMcIlCDeorKGpwZmdH7kHbqlk/Yj6dEpJMKEQw5lk0KVQY0OLXaCswy8GqlIVLd5625OB+rAntP9xVw==",
|
| 23 |
+
"license": "MIT"
|
| 24 |
+
},
|
| 25 |
+
"node_modules/abort-controller": {
|
| 26 |
+
"version": "3.0.0",
|
| 27 |
+
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
| 28 |
+
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
|
| 29 |
+
"license": "MIT",
|
| 30 |
+
"dependencies": {
|
| 31 |
+
"event-target-shim": "^5.0.0"
|
| 32 |
+
},
|
| 33 |
+
"engines": {
|
| 34 |
+
"node": ">=6.5"
|
| 35 |
+
}
|
| 36 |
+
},
|
| 37 |
+
"node_modules/accepts": {
|
| 38 |
+
"version": "2.0.0",
|
| 39 |
+
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
|
| 40 |
+
"integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
|
| 41 |
+
"license": "MIT",
|
| 42 |
+
"dependencies": {
|
| 43 |
+
"mime-types": "^3.0.0",
|
| 44 |
+
"negotiator": "^1.0.0"
|
| 45 |
+
},
|
| 46 |
+
"engines": {
|
| 47 |
+
"node": ">= 0.6"
|
| 48 |
+
}
|
| 49 |
+
},
|
| 50 |
+
"node_modules/asynckit": {
|
| 51 |
+
"version": "0.4.0",
|
| 52 |
+
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
| 53 |
+
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
| 54 |
+
"license": "MIT"
|
| 55 |
+
},
|
| 56 |
+
"node_modules/axios": {
|
| 57 |
+
"version": "1.13.5",
|
| 58 |
+
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz",
|
| 59 |
+
"integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==",
|
| 60 |
+
"license": "MIT",
|
| 61 |
+
"dependencies": {
|
| 62 |
+
"follow-redirects": "^1.15.11",
|
| 63 |
+
"form-data": "^4.0.5",
|
| 64 |
+
"proxy-from-env": "^1.1.0"
|
| 65 |
+
}
|
| 66 |
+
},
|
| 67 |
+
"node_modules/body-parser": {
|
| 68 |
+
"version": "2.2.2",
|
| 69 |
+
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz",
|
| 70 |
+
"integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==",
|
| 71 |
+
"license": "MIT",
|
| 72 |
+
"dependencies": {
|
| 73 |
+
"bytes": "^3.1.2",
|
| 74 |
+
"content-type": "^1.0.5",
|
| 75 |
+
"debug": "^4.4.3",
|
| 76 |
+
"http-errors": "^2.0.0",
|
| 77 |
+
"iconv-lite": "^0.7.0",
|
| 78 |
+
"on-finished": "^2.4.1",
|
| 79 |
+
"qs": "^6.14.1",
|
| 80 |
+
"raw-body": "^3.0.1",
|
| 81 |
+
"type-is": "^2.0.1"
|
| 82 |
+
},
|
| 83 |
+
"engines": {
|
| 84 |
+
"node": ">=18"
|
| 85 |
+
},
|
| 86 |
+
"funding": {
|
| 87 |
+
"type": "opencollective",
|
| 88 |
+
"url": "https://opencollective.com/express"
|
| 89 |
+
}
|
| 90 |
+
},
|
| 91 |
+
"node_modules/buffer-alloc": {
|
| 92 |
+
"version": "1.2.0",
|
| 93 |
+
"resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
|
| 94 |
+
"integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
|
| 95 |
+
"license": "MIT",
|
| 96 |
+
"dependencies": {
|
| 97 |
+
"buffer-alloc-unsafe": "^1.1.0",
|
| 98 |
+
"buffer-fill": "^1.0.0"
|
| 99 |
+
}
|
| 100 |
+
},
|
| 101 |
+
"node_modules/buffer-alloc-unsafe": {
|
| 102 |
+
"version": "1.1.0",
|
| 103 |
+
"resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
|
| 104 |
+
"integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==",
|
| 105 |
+
"license": "MIT"
|
| 106 |
+
},
|
| 107 |
+
"node_modules/buffer-fill": {
|
| 108 |
+
"version": "1.0.0",
|
| 109 |
+
"resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
|
| 110 |
+
"integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==",
|
| 111 |
+
"license": "MIT"
|
| 112 |
+
},
|
| 113 |
+
"node_modules/bytes": {
|
| 114 |
+
"version": "3.1.2",
|
| 115 |
+
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
| 116 |
+
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
| 117 |
+
"license": "MIT",
|
| 118 |
+
"engines": {
|
| 119 |
+
"node": ">= 0.8"
|
| 120 |
+
}
|
| 121 |
+
},
|
| 122 |
+
"node_modules/call-bind-apply-helpers": {
|
| 123 |
+
"version": "1.0.2",
|
| 124 |
+
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
| 125 |
+
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
| 126 |
+
"license": "MIT",
|
| 127 |
+
"dependencies": {
|
| 128 |
+
"es-errors": "^1.3.0",
|
| 129 |
+
"function-bind": "^1.1.2"
|
| 130 |
+
},
|
| 131 |
+
"engines": {
|
| 132 |
+
"node": ">= 0.4"
|
| 133 |
+
}
|
| 134 |
+
},
|
| 135 |
+
"node_modules/call-bound": {
|
| 136 |
+
"version": "1.0.4",
|
| 137 |
+
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
| 138 |
+
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
| 139 |
+
"license": "MIT",
|
| 140 |
+
"dependencies": {
|
| 141 |
+
"call-bind-apply-helpers": "^1.0.2",
|
| 142 |
+
"get-intrinsic": "^1.3.0"
|
| 143 |
+
},
|
| 144 |
+
"engines": {
|
| 145 |
+
"node": ">= 0.4"
|
| 146 |
+
},
|
| 147 |
+
"funding": {
|
| 148 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 149 |
+
}
|
| 150 |
+
},
|
| 151 |
+
"node_modules/combined-stream": {
|
| 152 |
+
"version": "1.0.8",
|
| 153 |
+
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
| 154 |
+
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
| 155 |
+
"license": "MIT",
|
| 156 |
+
"dependencies": {
|
| 157 |
+
"delayed-stream": "~1.0.0"
|
| 158 |
+
},
|
| 159 |
+
"engines": {
|
| 160 |
+
"node": ">= 0.8"
|
| 161 |
+
}
|
| 162 |
+
},
|
| 163 |
+
"node_modules/content-disposition": {
|
| 164 |
+
"version": "1.0.1",
|
| 165 |
+
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz",
|
| 166 |
+
"integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==",
|
| 167 |
+
"license": "MIT",
|
| 168 |
+
"engines": {
|
| 169 |
+
"node": ">=18"
|
| 170 |
+
},
|
| 171 |
+
"funding": {
|
| 172 |
+
"type": "opencollective",
|
| 173 |
+
"url": "https://opencollective.com/express"
|
| 174 |
+
}
|
| 175 |
+
},
|
| 176 |
+
"node_modules/content-type": {
|
| 177 |
+
"version": "1.0.5",
|
| 178 |
+
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
| 179 |
+
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
| 180 |
+
"license": "MIT",
|
| 181 |
+
"engines": {
|
| 182 |
+
"node": ">= 0.6"
|
| 183 |
+
}
|
| 184 |
+
},
|
| 185 |
+
"node_modules/cookie": {
|
| 186 |
+
"version": "0.7.2",
|
| 187 |
+
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
| 188 |
+
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
| 189 |
+
"license": "MIT",
|
| 190 |
+
"engines": {
|
| 191 |
+
"node": ">= 0.6"
|
| 192 |
+
}
|
| 193 |
+
},
|
| 194 |
+
"node_modules/cookie-signature": {
|
| 195 |
+
"version": "1.2.2",
|
| 196 |
+
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
|
| 197 |
+
"integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
|
| 198 |
+
"license": "MIT",
|
| 199 |
+
"engines": {
|
| 200 |
+
"node": ">=6.6.0"
|
| 201 |
+
}
|
| 202 |
+
},
|
| 203 |
+
"node_modules/cors": {
|
| 204 |
+
"version": "2.8.6",
|
| 205 |
+
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
|
| 206 |
+
"integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
|
| 207 |
+
"license": "MIT",
|
| 208 |
+
"dependencies": {
|
| 209 |
+
"object-assign": "^4",
|
| 210 |
+
"vary": "^1"
|
| 211 |
+
},
|
| 212 |
+
"engines": {
|
| 213 |
+
"node": ">= 0.10"
|
| 214 |
+
},
|
| 215 |
+
"funding": {
|
| 216 |
+
"type": "opencollective",
|
| 217 |
+
"url": "https://opencollective.com/express"
|
| 218 |
+
}
|
| 219 |
+
},
|
| 220 |
+
"node_modules/debug": {
|
| 221 |
+
"version": "4.4.3",
|
| 222 |
+
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
| 223 |
+
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
| 224 |
+
"license": "MIT",
|
| 225 |
+
"dependencies": {
|
| 226 |
+
"ms": "^2.1.3"
|
| 227 |
+
},
|
| 228 |
+
"engines": {
|
| 229 |
+
"node": ">=6.0"
|
| 230 |
+
},
|
| 231 |
+
"peerDependenciesMeta": {
|
| 232 |
+
"supports-color": {
|
| 233 |
+
"optional": true
|
| 234 |
+
}
|
| 235 |
+
}
|
| 236 |
+
},
|
| 237 |
+
"node_modules/delayed-stream": {
|
| 238 |
+
"version": "1.0.0",
|
| 239 |
+
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
| 240 |
+
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
| 241 |
+
"license": "MIT",
|
| 242 |
+
"engines": {
|
| 243 |
+
"node": ">=0.4.0"
|
| 244 |
+
}
|
| 245 |
+
},
|
| 246 |
+
"node_modules/depd": {
|
| 247 |
+
"version": "2.0.0",
|
| 248 |
+
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
| 249 |
+
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
| 250 |
+
"license": "MIT",
|
| 251 |
+
"engines": {
|
| 252 |
+
"node": ">= 0.8"
|
| 253 |
+
}
|
| 254 |
+
},
|
| 255 |
+
"node_modules/dotenv": {
|
| 256 |
+
"version": "17.2.4",
|
| 257 |
+
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.4.tgz",
|
| 258 |
+
"integrity": "sha512-mudtfb4zRB4bVvdj0xRo+e6duH1csJRM8IukBqfTRvHotn9+LBXB8ynAidP9zHqoRC/fsllXgk4kCKlR21fIhw==",
|
| 259 |
+
"license": "BSD-2-Clause",
|
| 260 |
+
"engines": {
|
| 261 |
+
"node": ">=12"
|
| 262 |
+
},
|
| 263 |
+
"funding": {
|
| 264 |
+
"url": "https://dotenvx.com"
|
| 265 |
+
}
|
| 266 |
+
},
|
| 267 |
+
"node_modules/dunder-proto": {
|
| 268 |
+
"version": "1.0.1",
|
| 269 |
+
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
| 270 |
+
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
| 271 |
+
"license": "MIT",
|
| 272 |
+
"dependencies": {
|
| 273 |
+
"call-bind-apply-helpers": "^1.0.1",
|
| 274 |
+
"es-errors": "^1.3.0",
|
| 275 |
+
"gopd": "^1.2.0"
|
| 276 |
+
},
|
| 277 |
+
"engines": {
|
| 278 |
+
"node": ">= 0.4"
|
| 279 |
+
}
|
| 280 |
+
},
|
| 281 |
+
"node_modules/ee-first": {
|
| 282 |
+
"version": "1.1.1",
|
| 283 |
+
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
| 284 |
+
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
|
| 285 |
+
"license": "MIT"
|
| 286 |
+
},
|
| 287 |
+
"node_modules/encodeurl": {
|
| 288 |
+
"version": "2.0.0",
|
| 289 |
+
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
| 290 |
+
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
| 291 |
+
"license": "MIT",
|
| 292 |
+
"engines": {
|
| 293 |
+
"node": ">= 0.8"
|
| 294 |
+
}
|
| 295 |
+
},
|
| 296 |
+
"node_modules/es-define-property": {
|
| 297 |
+
"version": "1.0.1",
|
| 298 |
+
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
| 299 |
+
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
| 300 |
+
"license": "MIT",
|
| 301 |
+
"engines": {
|
| 302 |
+
"node": ">= 0.4"
|
| 303 |
+
}
|
| 304 |
+
},
|
| 305 |
+
"node_modules/es-errors": {
|
| 306 |
+
"version": "1.3.0",
|
| 307 |
+
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
| 308 |
+
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
| 309 |
+
"license": "MIT",
|
| 310 |
+
"engines": {
|
| 311 |
+
"node": ">= 0.4"
|
| 312 |
+
}
|
| 313 |
+
},
|
| 314 |
+
"node_modules/es-object-atoms": {
|
| 315 |
+
"version": "1.1.1",
|
| 316 |
+
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
| 317 |
+
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
| 318 |
+
"license": "MIT",
|
| 319 |
+
"dependencies": {
|
| 320 |
+
"es-errors": "^1.3.0"
|
| 321 |
+
},
|
| 322 |
+
"engines": {
|
| 323 |
+
"node": ">= 0.4"
|
| 324 |
+
}
|
| 325 |
+
},
|
| 326 |
+
"node_modules/es-set-tostringtag": {
|
| 327 |
+
"version": "2.1.0",
|
| 328 |
+
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
| 329 |
+
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
| 330 |
+
"license": "MIT",
|
| 331 |
+
"dependencies": {
|
| 332 |
+
"es-errors": "^1.3.0",
|
| 333 |
+
"get-intrinsic": "^1.2.6",
|
| 334 |
+
"has-tostringtag": "^1.0.2",
|
| 335 |
+
"hasown": "^2.0.2"
|
| 336 |
+
},
|
| 337 |
+
"engines": {
|
| 338 |
+
"node": ">= 0.4"
|
| 339 |
+
}
|
| 340 |
+
},
|
| 341 |
+
"node_modules/escape-html": {
|
| 342 |
+
"version": "1.0.3",
|
| 343 |
+
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
| 344 |
+
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
| 345 |
+
"license": "MIT"
|
| 346 |
+
},
|
| 347 |
+
"node_modules/etag": {
|
| 348 |
+
"version": "1.8.1",
|
| 349 |
+
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
| 350 |
+
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
|
| 351 |
+
"license": "MIT",
|
| 352 |
+
"engines": {
|
| 353 |
+
"node": ">= 0.6"
|
| 354 |
+
}
|
| 355 |
+
},
|
| 356 |
+
"node_modules/event-target-shim": {
|
| 357 |
+
"version": "5.0.1",
|
| 358 |
+
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
|
| 359 |
+
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
|
| 360 |
+
"license": "MIT",
|
| 361 |
+
"engines": {
|
| 362 |
+
"node": ">=6"
|
| 363 |
+
}
|
| 364 |
+
},
|
| 365 |
+
"node_modules/express": {
|
| 366 |
+
"version": "5.2.1",
|
| 367 |
+
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
|
| 368 |
+
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
|
| 369 |
+
"license": "MIT",
|
| 370 |
+
"dependencies": {
|
| 371 |
+
"accepts": "^2.0.0",
|
| 372 |
+
"body-parser": "^2.2.1",
|
| 373 |
+
"content-disposition": "^1.0.0",
|
| 374 |
+
"content-type": "^1.0.5",
|
| 375 |
+
"cookie": "^0.7.1",
|
| 376 |
+
"cookie-signature": "^1.2.1",
|
| 377 |
+
"debug": "^4.4.0",
|
| 378 |
+
"depd": "^2.0.0",
|
| 379 |
+
"encodeurl": "^2.0.0",
|
| 380 |
+
"escape-html": "^1.0.3",
|
| 381 |
+
"etag": "^1.8.1",
|
| 382 |
+
"finalhandler": "^2.1.0",
|
| 383 |
+
"fresh": "^2.0.0",
|
| 384 |
+
"http-errors": "^2.0.0",
|
| 385 |
+
"merge-descriptors": "^2.0.0",
|
| 386 |
+
"mime-types": "^3.0.0",
|
| 387 |
+
"on-finished": "^2.4.1",
|
| 388 |
+
"once": "^1.4.0",
|
| 389 |
+
"parseurl": "^1.3.3",
|
| 390 |
+
"proxy-addr": "^2.0.7",
|
| 391 |
+
"qs": "^6.14.0",
|
| 392 |
+
"range-parser": "^1.2.1",
|
| 393 |
+
"router": "^2.2.0",
|
| 394 |
+
"send": "^1.1.0",
|
| 395 |
+
"serve-static": "^2.2.0",
|
| 396 |
+
"statuses": "^2.0.1",
|
| 397 |
+
"type-is": "^2.0.1",
|
| 398 |
+
"vary": "^1.1.2"
|
| 399 |
+
},
|
| 400 |
+
"engines": {
|
| 401 |
+
"node": ">= 18"
|
| 402 |
+
},
|
| 403 |
+
"funding": {
|
| 404 |
+
"type": "opencollective",
|
| 405 |
+
"url": "https://opencollective.com/express"
|
| 406 |
+
}
|
| 407 |
+
},
|
| 408 |
+
"node_modules/finalhandler": {
|
| 409 |
+
"version": "2.1.1",
|
| 410 |
+
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz",
|
| 411 |
+
"integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==",
|
| 412 |
+
"license": "MIT",
|
| 413 |
+
"dependencies": {
|
| 414 |
+
"debug": "^4.4.0",
|
| 415 |
+
"encodeurl": "^2.0.0",
|
| 416 |
+
"escape-html": "^1.0.3",
|
| 417 |
+
"on-finished": "^2.4.1",
|
| 418 |
+
"parseurl": "^1.3.3",
|
| 419 |
+
"statuses": "^2.0.1"
|
| 420 |
+
},
|
| 421 |
+
"engines": {
|
| 422 |
+
"node": ">= 18.0.0"
|
| 423 |
+
},
|
| 424 |
+
"funding": {
|
| 425 |
+
"type": "opencollective",
|
| 426 |
+
"url": "https://opencollective.com/express"
|
| 427 |
+
}
|
| 428 |
+
},
|
| 429 |
+
"node_modules/follow-redirects": {
|
| 430 |
+
"version": "1.15.11",
|
| 431 |
+
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
|
| 432 |
+
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
|
| 433 |
+
"funding": [
|
| 434 |
+
{
|
| 435 |
+
"type": "individual",
|
| 436 |
+
"url": "https://github.com/sponsors/RubenVerborgh"
|
| 437 |
+
}
|
| 438 |
+
],
|
| 439 |
+
"license": "MIT",
|
| 440 |
+
"engines": {
|
| 441 |
+
"node": ">=4.0"
|
| 442 |
+
},
|
| 443 |
+
"peerDependenciesMeta": {
|
| 444 |
+
"debug": {
|
| 445 |
+
"optional": true
|
| 446 |
+
}
|
| 447 |
+
}
|
| 448 |
+
},
|
| 449 |
+
"node_modules/form-data": {
|
| 450 |
+
"version": "4.0.5",
|
| 451 |
+
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
|
| 452 |
+
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
|
| 453 |
+
"license": "MIT",
|
| 454 |
+
"dependencies": {
|
| 455 |
+
"asynckit": "^0.4.0",
|
| 456 |
+
"combined-stream": "^1.0.8",
|
| 457 |
+
"es-set-tostringtag": "^2.1.0",
|
| 458 |
+
"hasown": "^2.0.2",
|
| 459 |
+
"mime-types": "^2.1.12"
|
| 460 |
+
},
|
| 461 |
+
"engines": {
|
| 462 |
+
"node": ">= 6"
|
| 463 |
+
}
|
| 464 |
+
},
|
| 465 |
+
"node_modules/form-data/node_modules/mime-db": {
|
| 466 |
+
"version": "1.52.0",
|
| 467 |
+
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
| 468 |
+
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
| 469 |
+
"license": "MIT",
|
| 470 |
+
"engines": {
|
| 471 |
+
"node": ">= 0.6"
|
| 472 |
+
}
|
| 473 |
+
},
|
| 474 |
+
"node_modules/form-data/node_modules/mime-types": {
|
| 475 |
+
"version": "2.1.35",
|
| 476 |
+
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
| 477 |
+
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
| 478 |
+
"license": "MIT",
|
| 479 |
+
"dependencies": {
|
| 480 |
+
"mime-db": "1.52.0"
|
| 481 |
+
},
|
| 482 |
+
"engines": {
|
| 483 |
+
"node": ">= 0.6"
|
| 484 |
+
}
|
| 485 |
+
},
|
| 486 |
+
"node_modules/forwarded": {
|
| 487 |
+
"version": "0.2.0",
|
| 488 |
+
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
| 489 |
+
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
|
| 490 |
+
"license": "MIT",
|
| 491 |
+
"engines": {
|
| 492 |
+
"node": ">= 0.6"
|
| 493 |
+
}
|
| 494 |
+
},
|
| 495 |
+
"node_modules/fresh": {
|
| 496 |
+
"version": "2.0.0",
|
| 497 |
+
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
|
| 498 |
+
"integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
|
| 499 |
+
"license": "MIT",
|
| 500 |
+
"engines": {
|
| 501 |
+
"node": ">= 0.8"
|
| 502 |
+
}
|
| 503 |
+
},
|
| 504 |
+
"node_modules/function-bind": {
|
| 505 |
+
"version": "1.1.2",
|
| 506 |
+
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
| 507 |
+
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
| 508 |
+
"license": "MIT",
|
| 509 |
+
"funding": {
|
| 510 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 511 |
+
}
|
| 512 |
+
},
|
| 513 |
+
"node_modules/get-intrinsic": {
|
| 514 |
+
"version": "1.3.0",
|
| 515 |
+
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
| 516 |
+
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
| 517 |
+
"license": "MIT",
|
| 518 |
+
"dependencies": {
|
| 519 |
+
"call-bind-apply-helpers": "^1.0.2",
|
| 520 |
+
"es-define-property": "^1.0.1",
|
| 521 |
+
"es-errors": "^1.3.0",
|
| 522 |
+
"es-object-atoms": "^1.1.1",
|
| 523 |
+
"function-bind": "^1.1.2",
|
| 524 |
+
"get-proto": "^1.0.1",
|
| 525 |
+
"gopd": "^1.2.0",
|
| 526 |
+
"has-symbols": "^1.1.0",
|
| 527 |
+
"hasown": "^2.0.2",
|
| 528 |
+
"math-intrinsics": "^1.1.0"
|
| 529 |
+
},
|
| 530 |
+
"engines": {
|
| 531 |
+
"node": ">= 0.4"
|
| 532 |
+
},
|
| 533 |
+
"funding": {
|
| 534 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 535 |
+
}
|
| 536 |
+
},
|
| 537 |
+
"node_modules/get-proto": {
|
| 538 |
+
"version": "1.0.1",
|
| 539 |
+
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
| 540 |
+
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
| 541 |
+
"license": "MIT",
|
| 542 |
+
"dependencies": {
|
| 543 |
+
"dunder-proto": "^1.0.1",
|
| 544 |
+
"es-object-atoms": "^1.0.0"
|
| 545 |
+
},
|
| 546 |
+
"engines": {
|
| 547 |
+
"node": ">= 0.4"
|
| 548 |
+
}
|
| 549 |
+
},
|
| 550 |
+
"node_modules/gopd": {
|
| 551 |
+
"version": "1.2.0",
|
| 552 |
+
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
| 553 |
+
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
| 554 |
+
"license": "MIT",
|
| 555 |
+
"engines": {
|
| 556 |
+
"node": ">= 0.4"
|
| 557 |
+
},
|
| 558 |
+
"funding": {
|
| 559 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 560 |
+
}
|
| 561 |
+
},
|
| 562 |
+
"node_modules/has-symbols": {
|
| 563 |
+
"version": "1.1.0",
|
| 564 |
+
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
| 565 |
+
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
| 566 |
+
"license": "MIT",
|
| 567 |
+
"engines": {
|
| 568 |
+
"node": ">= 0.4"
|
| 569 |
+
},
|
| 570 |
+
"funding": {
|
| 571 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 572 |
+
}
|
| 573 |
+
},
|
| 574 |
+
"node_modules/has-tostringtag": {
|
| 575 |
+
"version": "1.0.2",
|
| 576 |
+
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
| 577 |
+
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
| 578 |
+
"license": "MIT",
|
| 579 |
+
"dependencies": {
|
| 580 |
+
"has-symbols": "^1.0.3"
|
| 581 |
+
},
|
| 582 |
+
"engines": {
|
| 583 |
+
"node": ">= 0.4"
|
| 584 |
+
},
|
| 585 |
+
"funding": {
|
| 586 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 587 |
+
}
|
| 588 |
+
},
|
| 589 |
+
"node_modules/hasown": {
|
| 590 |
+
"version": "2.0.2",
|
| 591 |
+
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
| 592 |
+
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
| 593 |
+
"license": "MIT",
|
| 594 |
+
"dependencies": {
|
| 595 |
+
"function-bind": "^1.1.2"
|
| 596 |
+
},
|
| 597 |
+
"engines": {
|
| 598 |
+
"node": ">= 0.4"
|
| 599 |
+
}
|
| 600 |
+
},
|
| 601 |
+
"node_modules/http-errors": {
|
| 602 |
+
"version": "2.0.1",
|
| 603 |
+
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
|
| 604 |
+
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
|
| 605 |
+
"license": "MIT",
|
| 606 |
+
"dependencies": {
|
| 607 |
+
"depd": "~2.0.0",
|
| 608 |
+
"inherits": "~2.0.4",
|
| 609 |
+
"setprototypeof": "~1.2.0",
|
| 610 |
+
"statuses": "~2.0.2",
|
| 611 |
+
"toidentifier": "~1.0.1"
|
| 612 |
+
},
|
| 613 |
+
"engines": {
|
| 614 |
+
"node": ">= 0.8"
|
| 615 |
+
},
|
| 616 |
+
"funding": {
|
| 617 |
+
"type": "opencollective",
|
| 618 |
+
"url": "https://opencollective.com/express"
|
| 619 |
+
}
|
| 620 |
+
},
|
| 621 |
+
"node_modules/iconv-lite": {
|
| 622 |
+
"version": "0.7.2",
|
| 623 |
+
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
|
| 624 |
+
"integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
|
| 625 |
+
"license": "MIT",
|
| 626 |
+
"dependencies": {
|
| 627 |
+
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
| 628 |
+
},
|
| 629 |
+
"engines": {
|
| 630 |
+
"node": ">=0.10.0"
|
| 631 |
+
},
|
| 632 |
+
"funding": {
|
| 633 |
+
"type": "opencollective",
|
| 634 |
+
"url": "https://opencollective.com/express"
|
| 635 |
+
}
|
| 636 |
+
},
|
| 637 |
+
"node_modules/inherits": {
|
| 638 |
+
"version": "2.0.4",
|
| 639 |
+
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
| 640 |
+
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
| 641 |
+
"license": "ISC"
|
| 642 |
+
},
|
| 643 |
+
"node_modules/ipaddr.js": {
|
| 644 |
+
"version": "1.9.1",
|
| 645 |
+
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
| 646 |
+
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
| 647 |
+
"license": "MIT",
|
| 648 |
+
"engines": {
|
| 649 |
+
"node": ">= 0.10"
|
| 650 |
+
}
|
| 651 |
+
},
|
| 652 |
+
"node_modules/is-promise": {
|
| 653 |
+
"version": "4.0.0",
|
| 654 |
+
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
|
| 655 |
+
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
|
| 656 |
+
"license": "MIT"
|
| 657 |
+
},
|
| 658 |
+
"node_modules/math-intrinsics": {
|
| 659 |
+
"version": "1.1.0",
|
| 660 |
+
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
| 661 |
+
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
| 662 |
+
"license": "MIT",
|
| 663 |
+
"engines": {
|
| 664 |
+
"node": ">= 0.4"
|
| 665 |
+
}
|
| 666 |
+
},
|
| 667 |
+
"node_modules/media-typer": {
|
| 668 |
+
"version": "1.1.0",
|
| 669 |
+
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
|
| 670 |
+
"integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
|
| 671 |
+
"license": "MIT",
|
| 672 |
+
"engines": {
|
| 673 |
+
"node": ">= 0.8"
|
| 674 |
+
}
|
| 675 |
+
},
|
| 676 |
+
"node_modules/merge-descriptors": {
|
| 677 |
+
"version": "2.0.0",
|
| 678 |
+
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
|
| 679 |
+
"integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
|
| 680 |
+
"license": "MIT",
|
| 681 |
+
"engines": {
|
| 682 |
+
"node": ">=18"
|
| 683 |
+
},
|
| 684 |
+
"funding": {
|
| 685 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 686 |
+
}
|
| 687 |
+
},
|
| 688 |
+
"node_modules/mime-db": {
|
| 689 |
+
"version": "1.54.0",
|
| 690 |
+
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
| 691 |
+
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
| 692 |
+
"license": "MIT",
|
| 693 |
+
"engines": {
|
| 694 |
+
"node": ">= 0.6"
|
| 695 |
+
}
|
| 696 |
+
},
|
| 697 |
+
"node_modules/mime-types": {
|
| 698 |
+
"version": "3.0.2",
|
| 699 |
+
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
|
| 700 |
+
"integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
|
| 701 |
+
"license": "MIT",
|
| 702 |
+
"dependencies": {
|
| 703 |
+
"mime-db": "^1.54.0"
|
| 704 |
+
},
|
| 705 |
+
"engines": {
|
| 706 |
+
"node": ">=18"
|
| 707 |
+
},
|
| 708 |
+
"funding": {
|
| 709 |
+
"type": "opencollective",
|
| 710 |
+
"url": "https://opencollective.com/express"
|
| 711 |
+
}
|
| 712 |
+
},
|
| 713 |
+
"node_modules/mri": {
|
| 714 |
+
"version": "1.2.0",
|
| 715 |
+
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
|
| 716 |
+
"integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==",
|
| 717 |
+
"license": "MIT",
|
| 718 |
+
"engines": {
|
| 719 |
+
"node": ">=4"
|
| 720 |
+
}
|
| 721 |
+
},
|
| 722 |
+
"node_modules/ms": {
|
| 723 |
+
"version": "2.1.3",
|
| 724 |
+
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
| 725 |
+
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
| 726 |
+
"license": "MIT"
|
| 727 |
+
},
|
| 728 |
+
"node_modules/negotiator": {
|
| 729 |
+
"version": "1.0.0",
|
| 730 |
+
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
| 731 |
+
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
|
| 732 |
+
"license": "MIT",
|
| 733 |
+
"engines": {
|
| 734 |
+
"node": ">= 0.6"
|
| 735 |
+
}
|
| 736 |
+
},
|
| 737 |
+
"node_modules/node-fetch": {
|
| 738 |
+
"version": "2.7.0",
|
| 739 |
+
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
| 740 |
+
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
| 741 |
+
"license": "MIT",
|
| 742 |
+
"dependencies": {
|
| 743 |
+
"whatwg-url": "^5.0.0"
|
| 744 |
+
},
|
| 745 |
+
"engines": {
|
| 746 |
+
"node": "4.x || >=6.0.0"
|
| 747 |
+
},
|
| 748 |
+
"peerDependencies": {
|
| 749 |
+
"encoding": "^0.1.0"
|
| 750 |
+
},
|
| 751 |
+
"peerDependenciesMeta": {
|
| 752 |
+
"encoding": {
|
| 753 |
+
"optional": true
|
| 754 |
+
}
|
| 755 |
+
}
|
| 756 |
+
},
|
| 757 |
+
"node_modules/object-assign": {
|
| 758 |
+
"version": "4.1.1",
|
| 759 |
+
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
| 760 |
+
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
| 761 |
+
"license": "MIT",
|
| 762 |
+
"engines": {
|
| 763 |
+
"node": ">=0.10.0"
|
| 764 |
+
}
|
| 765 |
+
},
|
| 766 |
+
"node_modules/object-inspect": {
|
| 767 |
+
"version": "1.13.4",
|
| 768 |
+
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
| 769 |
+
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
|
| 770 |
+
"license": "MIT",
|
| 771 |
+
"engines": {
|
| 772 |
+
"node": ">= 0.4"
|
| 773 |
+
},
|
| 774 |
+
"funding": {
|
| 775 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 776 |
+
}
|
| 777 |
+
},
|
| 778 |
+
"node_modules/on-finished": {
|
| 779 |
+
"version": "2.4.1",
|
| 780 |
+
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
| 781 |
+
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
| 782 |
+
"license": "MIT",
|
| 783 |
+
"dependencies": {
|
| 784 |
+
"ee-first": "1.1.1"
|
| 785 |
+
},
|
| 786 |
+
"engines": {
|
| 787 |
+
"node": ">= 0.8"
|
| 788 |
+
}
|
| 789 |
+
},
|
| 790 |
+
"node_modules/once": {
|
| 791 |
+
"version": "1.4.0",
|
| 792 |
+
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
| 793 |
+
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
| 794 |
+
"license": "ISC",
|
| 795 |
+
"dependencies": {
|
| 796 |
+
"wrappy": "1"
|
| 797 |
+
}
|
| 798 |
+
},
|
| 799 |
+
"node_modules/p-timeout": {
|
| 800 |
+
"version": "4.1.0",
|
| 801 |
+
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-4.1.0.tgz",
|
| 802 |
+
"integrity": "sha512-+/wmHtzJuWii1sXn3HCuH/FTwGhrp4tmJTxSKJbfS+vkipci6osxXM5mY0jUiRzWKMTgUT8l7HFbeSwZAynqHw==",
|
| 803 |
+
"license": "MIT",
|
| 804 |
+
"engines": {
|
| 805 |
+
"node": ">=10"
|
| 806 |
+
}
|
| 807 |
+
},
|
| 808 |
+
"node_modules/parseurl": {
|
| 809 |
+
"version": "1.3.3",
|
| 810 |
+
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
| 811 |
+
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
| 812 |
+
"license": "MIT",
|
| 813 |
+
"engines": {
|
| 814 |
+
"node": ">= 0.8"
|
| 815 |
+
}
|
| 816 |
+
},
|
| 817 |
+
"node_modules/path-to-regexp": {
|
| 818 |
+
"version": "8.3.0",
|
| 819 |
+
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
|
| 820 |
+
"integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
|
| 821 |
+
"license": "MIT",
|
| 822 |
+
"funding": {
|
| 823 |
+
"type": "opencollective",
|
| 824 |
+
"url": "https://opencollective.com/express"
|
| 825 |
+
}
|
| 826 |
+
},
|
| 827 |
+
"node_modules/proxy-addr": {
|
| 828 |
+
"version": "2.0.7",
|
| 829 |
+
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
| 830 |
+
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
| 831 |
+
"license": "MIT",
|
| 832 |
+
"dependencies": {
|
| 833 |
+
"forwarded": "0.2.0",
|
| 834 |
+
"ipaddr.js": "1.9.1"
|
| 835 |
+
},
|
| 836 |
+
"engines": {
|
| 837 |
+
"node": ">= 0.10"
|
| 838 |
+
}
|
| 839 |
+
},
|
| 840 |
+
"node_modules/proxy-from-env": {
|
| 841 |
+
"version": "1.1.0",
|
| 842 |
+
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
| 843 |
+
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
| 844 |
+
"license": "MIT"
|
| 845 |
+
},
|
| 846 |
+
"node_modules/qs": {
|
| 847 |
+
"version": "6.14.1",
|
| 848 |
+
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
|
| 849 |
+
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
|
| 850 |
+
"license": "BSD-3-Clause",
|
| 851 |
+
"dependencies": {
|
| 852 |
+
"side-channel": "^1.1.0"
|
| 853 |
+
},
|
| 854 |
+
"engines": {
|
| 855 |
+
"node": ">=0.6"
|
| 856 |
+
},
|
| 857 |
+
"funding": {
|
| 858 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 859 |
+
}
|
| 860 |
+
},
|
| 861 |
+
"node_modules/range-parser": {
|
| 862 |
+
"version": "1.2.1",
|
| 863 |
+
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
| 864 |
+
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
|
| 865 |
+
"license": "MIT",
|
| 866 |
+
"engines": {
|
| 867 |
+
"node": ">= 0.6"
|
| 868 |
+
}
|
| 869 |
+
},
|
| 870 |
+
"node_modules/raw-body": {
|
| 871 |
+
"version": "3.0.2",
|
| 872 |
+
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
|
| 873 |
+
"integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
|
| 874 |
+
"license": "MIT",
|
| 875 |
+
"dependencies": {
|
| 876 |
+
"bytes": "~3.1.2",
|
| 877 |
+
"http-errors": "~2.0.1",
|
| 878 |
+
"iconv-lite": "~0.7.0",
|
| 879 |
+
"unpipe": "~1.0.0"
|
| 880 |
+
},
|
| 881 |
+
"engines": {
|
| 882 |
+
"node": ">= 0.10"
|
| 883 |
+
}
|
| 884 |
+
},
|
| 885 |
+
"node_modules/router": {
|
| 886 |
+
"version": "2.2.0",
|
| 887 |
+
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
|
| 888 |
+
"integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
|
| 889 |
+
"license": "MIT",
|
| 890 |
+
"dependencies": {
|
| 891 |
+
"debug": "^4.4.0",
|
| 892 |
+
"depd": "^2.0.0",
|
| 893 |
+
"is-promise": "^4.0.0",
|
| 894 |
+
"parseurl": "^1.3.3",
|
| 895 |
+
"path-to-regexp": "^8.0.0"
|
| 896 |
+
},
|
| 897 |
+
"engines": {
|
| 898 |
+
"node": ">= 18"
|
| 899 |
+
}
|
| 900 |
+
},
|
| 901 |
+
"node_modules/safe-compare": {
|
| 902 |
+
"version": "1.1.4",
|
| 903 |
+
"resolved": "https://registry.npmjs.org/safe-compare/-/safe-compare-1.1.4.tgz",
|
| 904 |
+
"integrity": "sha512-b9wZ986HHCo/HbKrRpBJb2kqXMK9CEWIE1egeEvZsYn69ay3kdfl9nG3RyOcR+jInTDf7a86WQ1d4VJX7goSSQ==",
|
| 905 |
+
"license": "MIT",
|
| 906 |
+
"dependencies": {
|
| 907 |
+
"buffer-alloc": "^1.2.0"
|
| 908 |
+
}
|
| 909 |
+
},
|
| 910 |
+
"node_modules/safer-buffer": {
|
| 911 |
+
"version": "2.1.2",
|
| 912 |
+
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
| 913 |
+
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
| 914 |
+
"license": "MIT"
|
| 915 |
+
},
|
| 916 |
+
"node_modules/sandwich-stream": {
|
| 917 |
+
"version": "2.0.2",
|
| 918 |
+
"resolved": "https://registry.npmjs.org/sandwich-stream/-/sandwich-stream-2.0.2.tgz",
|
| 919 |
+
"integrity": "sha512-jLYV0DORrzY3xaz/S9ydJL6Iz7essZeAfnAavsJ+zsJGZ1MOnsS52yRjU3uF3pJa/lla7+wisp//fxOwOH8SKQ==",
|
| 920 |
+
"license": "Apache-2.0",
|
| 921 |
+
"engines": {
|
| 922 |
+
"node": ">= 0.10"
|
| 923 |
+
}
|
| 924 |
+
},
|
| 925 |
+
"node_modules/send": {
|
| 926 |
+
"version": "1.2.1",
|
| 927 |
+
"resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz",
|
| 928 |
+
"integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==",
|
| 929 |
+
"license": "MIT",
|
| 930 |
+
"dependencies": {
|
| 931 |
+
"debug": "^4.4.3",
|
| 932 |
+
"encodeurl": "^2.0.0",
|
| 933 |
+
"escape-html": "^1.0.3",
|
| 934 |
+
"etag": "^1.8.1",
|
| 935 |
+
"fresh": "^2.0.0",
|
| 936 |
+
"http-errors": "^2.0.1",
|
| 937 |
+
"mime-types": "^3.0.2",
|
| 938 |
+
"ms": "^2.1.3",
|
| 939 |
+
"on-finished": "^2.4.1",
|
| 940 |
+
"range-parser": "^1.2.1",
|
| 941 |
+
"statuses": "^2.0.2"
|
| 942 |
+
},
|
| 943 |
+
"engines": {
|
| 944 |
+
"node": ">= 18"
|
| 945 |
+
},
|
| 946 |
+
"funding": {
|
| 947 |
+
"type": "opencollective",
|
| 948 |
+
"url": "https://opencollective.com/express"
|
| 949 |
+
}
|
| 950 |
+
},
|
| 951 |
+
"node_modules/serve-static": {
|
| 952 |
+
"version": "2.2.1",
|
| 953 |
+
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz",
|
| 954 |
+
"integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==",
|
| 955 |
+
"license": "MIT",
|
| 956 |
+
"dependencies": {
|
| 957 |
+
"encodeurl": "^2.0.0",
|
| 958 |
+
"escape-html": "^1.0.3",
|
| 959 |
+
"parseurl": "^1.3.3",
|
| 960 |
+
"send": "^1.2.0"
|
| 961 |
+
},
|
| 962 |
+
"engines": {
|
| 963 |
+
"node": ">= 18"
|
| 964 |
+
},
|
| 965 |
+
"funding": {
|
| 966 |
+
"type": "opencollective",
|
| 967 |
+
"url": "https://opencollective.com/express"
|
| 968 |
+
}
|
| 969 |
+
},
|
| 970 |
+
"node_modules/setprototypeof": {
|
| 971 |
+
"version": "1.2.0",
|
| 972 |
+
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
| 973 |
+
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
|
| 974 |
+
"license": "ISC"
|
| 975 |
+
},
|
| 976 |
+
"node_modules/side-channel": {
|
| 977 |
+
"version": "1.1.0",
|
| 978 |
+
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
| 979 |
+
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
|
| 980 |
+
"license": "MIT",
|
| 981 |
+
"dependencies": {
|
| 982 |
+
"es-errors": "^1.3.0",
|
| 983 |
+
"object-inspect": "^1.13.3",
|
| 984 |
+
"side-channel-list": "^1.0.0",
|
| 985 |
+
"side-channel-map": "^1.0.1",
|
| 986 |
+
"side-channel-weakmap": "^1.0.2"
|
| 987 |
+
},
|
| 988 |
+
"engines": {
|
| 989 |
+
"node": ">= 0.4"
|
| 990 |
+
},
|
| 991 |
+
"funding": {
|
| 992 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 993 |
+
}
|
| 994 |
+
},
|
| 995 |
+
"node_modules/side-channel-list": {
|
| 996 |
+
"version": "1.0.0",
|
| 997 |
+
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
|
| 998 |
+
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
|
| 999 |
+
"license": "MIT",
|
| 1000 |
+
"dependencies": {
|
| 1001 |
+
"es-errors": "^1.3.0",
|
| 1002 |
+
"object-inspect": "^1.13.3"
|
| 1003 |
+
},
|
| 1004 |
+
"engines": {
|
| 1005 |
+
"node": ">= 0.4"
|
| 1006 |
+
},
|
| 1007 |
+
"funding": {
|
| 1008 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 1009 |
+
}
|
| 1010 |
+
},
|
| 1011 |
+
"node_modules/side-channel-map": {
|
| 1012 |
+
"version": "1.0.1",
|
| 1013 |
+
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
|
| 1014 |
+
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
|
| 1015 |
+
"license": "MIT",
|
| 1016 |
+
"dependencies": {
|
| 1017 |
+
"call-bound": "^1.0.2",
|
| 1018 |
+
"es-errors": "^1.3.0",
|
| 1019 |
+
"get-intrinsic": "^1.2.5",
|
| 1020 |
+
"object-inspect": "^1.13.3"
|
| 1021 |
+
},
|
| 1022 |
+
"engines": {
|
| 1023 |
+
"node": ">= 0.4"
|
| 1024 |
+
},
|
| 1025 |
+
"funding": {
|
| 1026 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 1027 |
+
}
|
| 1028 |
+
},
|
| 1029 |
+
"node_modules/side-channel-weakmap": {
|
| 1030 |
+
"version": "1.0.2",
|
| 1031 |
+
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
|
| 1032 |
+
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
|
| 1033 |
+
"license": "MIT",
|
| 1034 |
+
"dependencies": {
|
| 1035 |
+
"call-bound": "^1.0.2",
|
| 1036 |
+
"es-errors": "^1.3.0",
|
| 1037 |
+
"get-intrinsic": "^1.2.5",
|
| 1038 |
+
"object-inspect": "^1.13.3",
|
| 1039 |
+
"side-channel-map": "^1.0.1"
|
| 1040 |
+
},
|
| 1041 |
+
"engines": {
|
| 1042 |
+
"node": ">= 0.4"
|
| 1043 |
+
},
|
| 1044 |
+
"funding": {
|
| 1045 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 1046 |
+
}
|
| 1047 |
+
},
|
| 1048 |
+
"node_modules/statuses": {
|
| 1049 |
+
"version": "2.0.2",
|
| 1050 |
+
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
| 1051 |
+
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
|
| 1052 |
+
"license": "MIT",
|
| 1053 |
+
"engines": {
|
| 1054 |
+
"node": ">= 0.8"
|
| 1055 |
+
}
|
| 1056 |
+
},
|
| 1057 |
+
"node_modules/telegraf": {
|
| 1058 |
+
"version": "4.16.3",
|
| 1059 |
+
"resolved": "https://registry.npmjs.org/telegraf/-/telegraf-4.16.3.tgz",
|
| 1060 |
+
"integrity": "sha512-yjEu2NwkHlXu0OARWoNhJlIjX09dRktiMQFsM678BAH/PEPVwctzL67+tvXqLCRQQvm3SDtki2saGO9hLlz68w==",
|
| 1061 |
+
"license": "MIT",
|
| 1062 |
+
"dependencies": {
|
| 1063 |
+
"@telegraf/types": "^7.1.0",
|
| 1064 |
+
"abort-controller": "^3.0.0",
|
| 1065 |
+
"debug": "^4.3.4",
|
| 1066 |
+
"mri": "^1.2.0",
|
| 1067 |
+
"node-fetch": "^2.7.0",
|
| 1068 |
+
"p-timeout": "^4.1.0",
|
| 1069 |
+
"safe-compare": "^1.1.4",
|
| 1070 |
+
"sandwich-stream": "^2.0.2"
|
| 1071 |
+
},
|
| 1072 |
+
"bin": {
|
| 1073 |
+
"telegraf": "lib/cli.mjs"
|
| 1074 |
+
},
|
| 1075 |
+
"engines": {
|
| 1076 |
+
"node": "^12.20.0 || >=14.13.1"
|
| 1077 |
+
}
|
| 1078 |
+
},
|
| 1079 |
+
"node_modules/toidentifier": {
|
| 1080 |
+
"version": "1.0.1",
|
| 1081 |
+
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
| 1082 |
+
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
| 1083 |
+
"license": "MIT",
|
| 1084 |
+
"engines": {
|
| 1085 |
+
"node": ">=0.6"
|
| 1086 |
+
}
|
| 1087 |
+
},
|
| 1088 |
+
"node_modules/tr46": {
|
| 1089 |
+
"version": "0.0.3",
|
| 1090 |
+
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
| 1091 |
+
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
| 1092 |
+
"license": "MIT"
|
| 1093 |
+
},
|
| 1094 |
+
"node_modules/type-is": {
|
| 1095 |
+
"version": "2.0.1",
|
| 1096 |
+
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
|
| 1097 |
+
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
|
| 1098 |
+
"license": "MIT",
|
| 1099 |
+
"dependencies": {
|
| 1100 |
+
"content-type": "^1.0.5",
|
| 1101 |
+
"media-typer": "^1.1.0",
|
| 1102 |
+
"mime-types": "^3.0.0"
|
| 1103 |
+
},
|
| 1104 |
+
"engines": {
|
| 1105 |
+
"node": ">= 0.6"
|
| 1106 |
+
}
|
| 1107 |
+
},
|
| 1108 |
+
"node_modules/unpipe": {
|
| 1109 |
+
"version": "1.0.0",
|
| 1110 |
+
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
| 1111 |
+
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
| 1112 |
+
"license": "MIT",
|
| 1113 |
+
"engines": {
|
| 1114 |
+
"node": ">= 0.8"
|
| 1115 |
+
}
|
| 1116 |
+
},
|
| 1117 |
+
"node_modules/vary": {
|
| 1118 |
+
"version": "1.1.2",
|
| 1119 |
+
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
| 1120 |
+
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
| 1121 |
+
"license": "MIT",
|
| 1122 |
+
"engines": {
|
| 1123 |
+
"node": ">= 0.8"
|
| 1124 |
+
}
|
| 1125 |
+
},
|
| 1126 |
+
"node_modules/webidl-conversions": {
|
| 1127 |
+
"version": "3.0.1",
|
| 1128 |
+
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
| 1129 |
+
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
| 1130 |
+
"license": "BSD-2-Clause"
|
| 1131 |
+
},
|
| 1132 |
+
"node_modules/whatwg-url": {
|
| 1133 |
+
"version": "5.0.0",
|
| 1134 |
+
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
| 1135 |
+
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
| 1136 |
+
"license": "MIT",
|
| 1137 |
+
"dependencies": {
|
| 1138 |
+
"tr46": "~0.0.3",
|
| 1139 |
+
"webidl-conversions": "^3.0.0"
|
| 1140 |
+
}
|
| 1141 |
+
},
|
| 1142 |
+
"node_modules/wrappy": {
|
| 1143 |
+
"version": "1.0.2",
|
| 1144 |
+
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
| 1145 |
+
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
| 1146 |
+
"license": "ISC"
|
| 1147 |
+
}
|
| 1148 |
+
}
|
| 1149 |
+
}
|
package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "aipostgenerator",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
+
"description": "",
|
| 5 |
+
"main": "index.js",
|
| 6 |
+
"scripts": {
|
| 7 |
+
"start": "node index.js",
|
| 8 |
+
"test": "echo \"Error: no test specified\" && exit 1"
|
| 9 |
+
},
|
| 10 |
+
"keywords": [],
|
| 11 |
+
"author": "",
|
| 12 |
+
"license": "ISC",
|
| 13 |
+
"dependencies": {
|
| 14 |
+
"axios": "^1.13.5",
|
| 15 |
+
"cors": "^2.8.6",
|
| 16 |
+
"dotenv": "^17.2.4",
|
| 17 |
+
"express": "^5.2.1",
|
| 18 |
+
"telegraf": "^4.16.3"
|
| 19 |
+
}
|
| 20 |
+
}
|
prompts/imgSystemPrompt.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const imgSystemPrompt = "";
|
| 2 |
+
|
| 3 |
+
module.exports = { imgSystemPrompt };
|
prompts/promptBuilder.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* Prompt Builder
|
| 3 |
+
* Helper methods to assemble complete prompts for LLM and Image Generation services.
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
/**
|
| 7 |
+
* Builds the final prompt for the LLM.
|
| 8 |
+
* Combines the system prompt, style instruction, and user's message.
|
| 9 |
+
*
|
| 10 |
+
* @param {string} userMessage - The input text from the user.
|
| 11 |
+
* @param {string} systemPrompt - The base system prompt (e.g., from storySystemPrompt.js).
|
| 12 |
+
* @param {string} stylePreset - The chosen style preset object.
|
| 13 |
+
* @returns {string} - The fully assembled prompt string.
|
| 14 |
+
*/
|
| 15 |
+
function buildStoryPrompt(userMessage, systemPrompt, stylePreset) {
|
| 16 |
+
// Format examples if available
|
| 17 |
+
let examplesText = "";
|
| 18 |
+
if (stylePreset.text_examples && stylePreset.text_examples.length > 0) {
|
| 19 |
+
examplesText = stylePreset.text_examples.map(ex => `- ${ex}`).join("\n");
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
const fullPrompt = `
|
| 23 |
+
${systemPrompt}
|
| 24 |
+
|
| 25 |
+
TOPIC: "${userMessage}"
|
| 26 |
+
|
| 27 |
+
STYLE INSTRUCTION:
|
| 28 |
+
${stylePreset.text_instruction}
|
| 29 |
+
|
| 30 |
+
REFERENCE EXAMPLES (Mimic this density and flow):
|
| 31 |
+
${examplesText}
|
| 32 |
+
`.trim();
|
| 33 |
+
|
| 34 |
+
return fullPrompt;
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
/**
|
| 38 |
+
* Builds the final prompt for the Image Generator.
|
| 39 |
+
*
|
| 40 |
+
* @param {string} storyText - The context/content of the story.
|
| 41 |
+
* @param {string} styleSuffix - The style keywords/suffix from the preset.
|
| 42 |
+
* @returns {string} - The assembled image prompt.
|
| 43 |
+
*/
|
| 44 |
+
function buildImagePrompt(storyText, styleSuffix) {
|
| 45 |
+
return `
|
| 46 |
+
${styleSuffix}
|
| 47 |
+
|
| 48 |
+
STORY CONTEXT:
|
| 49 |
+
"${storyText}"
|
| 50 |
+
`.trim();
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
/**
|
| 54 |
+
* Builds a prompt for editing an existing story.
|
| 55 |
+
*
|
| 56 |
+
* @param {string} userMessage - The edit instructions from the user.
|
| 57 |
+
* @param {string} contextPrompt - The original prompt used to generate the story.
|
| 58 |
+
* @param {string} contextStory - The previously generated story text.
|
| 59 |
+
* @returns {string} - The assembled edit prompt.
|
| 60 |
+
*/
|
| 61 |
+
function buildEditPrompt(userMessage, contextPrompt, contextStory) {
|
| 62 |
+
return `
|
| 63 |
+
You are an editor. Below is the background context and the story that was previously generated.
|
| 64 |
+
|
| 65 |
+
CONTEXT PROMPT (Initial Intent/Style):
|
| 66 |
+
"${contextPrompt}"
|
| 67 |
+
|
| 68 |
+
CURRENT GENERATED STORY:
|
| 69 |
+
"${contextStory}"
|
| 70 |
+
|
| 71 |
+
USER'S EDIT INSTRUCTION:
|
| 72 |
+
"${userMessage}"
|
| 73 |
+
|
| 74 |
+
Task: Rewrite the story based on the changes requested while maintaining the original tone, English language, and staccato rhythm.
|
| 75 |
+
Output ONLY the revised story text.
|
| 76 |
+
`.trim();
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
/**
|
| 80 |
+
* Builds a prompt for regenerating/editing an image.
|
| 81 |
+
*
|
| 82 |
+
* @param {string} userInstruction - The user's hint or instruction for the image.
|
| 83 |
+
* @returns {string} - The formatted prompt.
|
| 84 |
+
*/
|
| 85 |
+
function buildImageRegenerationPrompt(userInstruction) {
|
| 86 |
+
return `
|
| 87 |
+
User Instruction: "${userInstruction}"
|
| 88 |
+
|
| 89 |
+
Task: detailed infographic, similar to the previous image but applying the user's instruction.
|
| 90 |
+
`.trim();
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
module.exports = {
|
| 94 |
+
buildStoryPrompt,
|
| 95 |
+
buildImagePrompt,
|
| 96 |
+
buildEditPrompt,
|
| 97 |
+
buildImageRegenerationPrompt
|
| 98 |
+
};
|
prompts/storySystemPrompt.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const storySystemPrompt = `Language: Always respond in English, regardless of the input language.
|
| 2 |
+
Formatting: Write as a continuous paragraph without line breaks between sentences. Use a single blank line before hashtags.`;
|
| 3 |
+
|
| 4 |
+
module.exports = { storySystemPrompt };
|
prompts/stylePresets.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const stylePresets = [
|
| 2 |
+
{
|
| 3 |
+
"preset_name": "Before_After",
|
| 4 |
+
"description": "Emotional storytelling in 'memoir minimalism' style. Contrasts past and present with raw, staccato rhythm.",
|
| 5 |
+
"text_instruction": `Role: You are a master of emotional storytelling in the style of "memoir minimalism."
|
| 6 |
+
Task: Write a social media post about: TOPIC.
|
| 7 |
+
Style Guidelines (Follow strictly):
|
| 8 |
+
Instant Hook: Start with the main character and the core conflict in the very first sentence. No introductions.
|
| 9 |
+
Staccato Rhythm: Use short, punchy sentences. One sentence = one fact or action. Avoid complex conjunctions or excessive commas.
|
| 10 |
+
Show, Don't Tell: Do not describe how the character feels. Describe what they did or what they looked at. Let the reader feel the emotion through the facts.
|
| 11 |
+
Time Dynamics: Show the passage of time (days, months, years). The story must move forward, not stay static.
|
| 12 |
+
The "Quiet" Punchline: End with a brief, powerful sentence that provides closure without being overly dramatic.
|
| 13 |
+
Final Touch (Tags): At the end of the post, add 2–3 relevant hashtags. Make them specific to the story's theme and emotional takeaway.`,
|
| 14 |
+
"text_examples": [
|
| 15 |
+
"Michael waited to ring the cancer bell with me. Ten years later, I married him. We met at seventeen during cancer treatment. Different diagnoses, same hospital, same long days of waiting. Michael finished treatment first. Nurses told him to ring the bell. He didn’t. He said he’d wait. Months later, it was my turn. On my last day, Michael stood next to me. We rang the bell together. He didn’t just wait for the bell. He stayed for everything after.",
|
| 16 |
+
"At 55, I dropped 400 pounds to stand on my own again. My name is Linda. After my divorce 15 years ago, my weight slowly took over my life. I stayed inside, stopped driving, and depended on others. I chose operations, therapy, and daily work. Progress was slow, but it added up. I learned how to move again and trust my body. Today, I walk on my own, go out, and plan for the future. I didn’t just change my body. I changed my life."
|
| 17 |
+
],
|
| 18 |
+
"image_style_suffix": `Role: You are a Senior Documentary Photojournalist and Image Archivist. Your mission is to convert a raw human story into a technical prompt for an image generator (NanaBanana) to create a "Then vs. Now" diptych.
|
| 19 |
+
The Goal: The final image must look like a real, leaked, or archived photo collage from social media. It must feel authentic, imperfect, and deeply emotional.
|
| 20 |
+
STEP 1: NARRATIVE DISSECTION (Internal Analysis)
|
| 21 |
+
Before writing the prompt, identify:
|
| 22 |
+
1. **The Catalyst (Past):** What is the core struggle? (e.g., flood, illness, poverty). What are the visual markers? (Mud, hospital gowns, shivering, wet hair).
|
| 23 |
+
2. **The Triumph (Present):** What is the success? (A career, a wedding, health). What is the "Uniform" of this success? (Police gear, medical scrubs, a suit).
|
| 24 |
+
3. **The Human Connection:** How are they touching? (Desperate cling vs. proud embrace).
|
| 25 |
+
4. **Physical Anchors:** Choose 2-3 traits to keep consistent (Ethnicity, jawline shape, eye color).
|
| 26 |
+
|
| 27 |
+
STEP 2: VISUAL STYLE & TECHNICAL SPECS
|
| 28 |
+
- Format: A vertical-split diptych (side-by-side) with a thin, natural white divider.
|
| 29 |
+
- Left Panel Aesthetic: "Analog Archive." 1990s/2000s film grain, slightly overexposed flash, flat lighting, amateur composition. Use terms like "disposable camera look" or "grainy film scan."
|
| 30 |
+
- Right Panel Aesthetic: "Modern Candid." High-quality smartphone photo, natural outdoor lighting, authentic skin textures (pores, slight wrinkles, sweat), "shot on iPhone" feel.
|
| 31 |
+
- NO-GO ZONE: Avoid "cinematic," "8k," "masterpiece," "digital art," "perfect faces." If it looks like a movie poster, you failed. It must look like a real photo.
|
| 32 |
+
|
| 33 |
+
STEP 3: OUTPUT CONSTRUCTION
|
| 34 |
+
Start your response ONLY with: "A raw, documentary-style side-by-side photo collage..."
|
| 35 |
+
|
| 36 |
+
Example of the detail level required:
|
| 37 |
+
"Left: A grainy 2005 point-and-shoot photo of a soot-covered 19-year-old girl in a torn sweater, holding a crying toddler outside a burnt house; harsh flash, red-eye effect. Right: A modern smartphone photo of the same woman, now in her 30s with realistic sun-damage on skin, smiling and hugging the same boy who is now a tall firefighter in a heavy, soot-stained yellow jacket on a sunny day."`
|
| 38 |
+
},
|
| 39 |
+
{
|
| 40 |
+
"preset_name": "Cyberpunk_Neon",
|
| 41 |
+
"description": "Мир высокого хай-тека и низкой жизни. Неон, кибер-импланты и корпоративные интриги.",
|
| 42 |
+
"text_instruction": "Пиши в стиле киберпанка, с акцентом на технологии и антиутопию...",
|
| 43 |
+
"text_examples": ["Пример 1: Неоновые огни отражались в лужах...", "Пример 2: Импланты гудели тихо..."],
|
| 44 |
+
"image_style_suffix": "cyberpunk style, neon lights, rainy city, futuristic, high tech low life"
|
| 45 |
+
},
|
| 46 |
+
{
|
| 47 |
+
"preset_name": "Fantasy_Adventure",
|
| 48 |
+
"description": "Эпические приключения в мире магии, драконов и героев. Яркие краски и величественные пейзажи.",
|
| 49 |
+
"text_instruction": "Пиши в стиле эпического фэнтези...",
|
| 50 |
+
"text_examples": ["Пример 1: Дракон взмыл в небо...", "Пример 2: Маг поднял посох..."],
|
| 51 |
+
"image_style_suffix": "fantasy art, epic landscape, magical atmosphere, vibrant colors, detailed"
|
| 52 |
+
}
|
| 53 |
+
];
|
| 54 |
+
|
| 55 |
+
// Export for both Node.js and Browser
|
| 56 |
+
if (typeof module !== 'undefined' && module.exports) {
|
| 57 |
+
module.exports = { stylePresets };
|
| 58 |
+
} else {
|
| 59 |
+
window.stylePresets = stylePresets;
|
| 60 |
+
}
|
services/imageService.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* Image Service
|
| 3 |
+
* Handles communication with Image Generation APIs.
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
const axios = require('axios'); // Assuming axios might be used
|
| 7 |
+
|
| 8 |
+
// Placeholder for API Key configuration
|
| 9 |
+
const API_KEY = process.env.IMG_API_KEY || process.env.LLM_API_KEY || '';
|
| 10 |
+
const MODEL = 'gemini-3-pro-image-preview';
|
| 11 |
+
const API_URL = `https://generativelanguage.googleapis.com/v1beta/models/${MODEL}:generateContent`;
|
| 12 |
+
|
| 13 |
+
/**
|
| 14 |
+
* Generates an image based on the text prompt.
|
| 15 |
+
* @param {string} prompt - The text description for the image.
|
| 16 |
+
* @param {Object} options - Optional parameters (aspectRatio, etc.)
|
| 17 |
+
* @returns {Promise<string>} - The base64 string of the generated image.
|
| 18 |
+
*/
|
| 19 |
+
async function generateImage(prompt, options = {}) {
|
| 20 |
+
if (!API_KEY) {
|
| 21 |
+
throw new Error("IMG_API_KEY or LLM_API_KEY is not defined in environment variables.");
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
try {
|
| 25 |
+
console.log(`[Image Service] Sending request to ${MODEL}...`, prompt);
|
| 26 |
+
|
| 27 |
+
const url = `${API_URL}?key=${API_KEY}`;
|
| 28 |
+
|
| 29 |
+
const payload = {
|
| 30 |
+
contents: [{
|
| 31 |
+
parts: [{
|
| 32 |
+
text: prompt
|
| 33 |
+
}]
|
| 34 |
+
}]
|
| 35 |
+
};
|
| 36 |
+
|
| 37 |
+
// Add optional configuration if supported by the model and needed
|
| 38 |
+
/*
|
| 39 |
+
if (options.aspectRatio) {
|
| 40 |
+
payload.generationConfig = {
|
| 41 |
+
aspectRatio: options.aspectRatio // e.g., "1:1", "4:3", "16:9"
|
| 42 |
+
};
|
| 43 |
+
}
|
| 44 |
+
*/
|
| 45 |
+
|
| 46 |
+
const response = await axios.post(url, payload, {
|
| 47 |
+
headers: {
|
| 48 |
+
'Content-Type': 'application/json'
|
| 49 |
+
},
|
| 50 |
+
timeout: 120000 // 2 minutes timeout
|
| 51 |
+
});
|
| 52 |
+
|
| 53 |
+
// Extract base64 image data from Gemini response structure
|
| 54 |
+
if (response.data && response.data.candidates && response.data.candidates.length > 0) {
|
| 55 |
+
const candidate = response.data.candidates[0];
|
| 56 |
+
if (candidate.content && candidate.content.parts) {
|
| 57 |
+
const imagePart = candidate.content.parts.find(p => p.inlineData);
|
| 58 |
+
if (imagePart && imagePart.inlineData) {
|
| 59 |
+
return imagePart.inlineData.data; // This is the base64 string
|
| 60 |
+
}
|
| 61 |
+
}
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
console.warn("[Image Service] Unexpected response structure:", JSON.stringify(response.data));
|
| 65 |
+
throw new Error("Empty or invalid response from Image Generation API.");
|
| 66 |
+
|
| 67 |
+
} catch (error) {
|
| 68 |
+
console.error('[Image Service] Error generating image:', error.response ? JSON.stringify(error.response.data) : error.message);
|
| 69 |
+
throw error;
|
| 70 |
+
}
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
/**
|
| 74 |
+
* Regenerates/Edits an image based on a text prompt and an existing image.
|
| 75 |
+
*
|
| 76 |
+
* @param {string} prompt - The text instruction.
|
| 77 |
+
* @param {string} imageBase64 - The base64 string of the previous image.
|
| 78 |
+
* @returns {Promise<string>} - The base64 string of the new image.
|
| 79 |
+
*/
|
| 80 |
+
async function regenerateImage(prompt, imageBase64) {
|
| 81 |
+
if (!API_KEY) {
|
| 82 |
+
throw new Error("IMG_API_KEY or LLM_API_KEY is not defined.");
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
try {
|
| 86 |
+
console.log(`[Image Service] Regenerating image with prompt: ${prompt}`);
|
| 87 |
+
|
| 88 |
+
const url = `${API_URL}?key=${API_KEY}`;
|
| 89 |
+
|
| 90 |
+
const payload = {
|
| 91 |
+
contents: [{
|
| 92 |
+
parts: [
|
| 93 |
+
{ text: prompt },
|
| 94 |
+
{
|
| 95 |
+
inline_data: {
|
| 96 |
+
mime_type: "image/png", // Assuming PNG for now, simplest for base64
|
| 97 |
+
data: imageBase64
|
| 98 |
+
}
|
| 99 |
+
}
|
| 100 |
+
]
|
| 101 |
+
}]
|
| 102 |
+
};
|
| 103 |
+
|
| 104 |
+
const response = await axios.post(url, payload, {
|
| 105 |
+
headers: { 'Content-Type': 'application/json' },
|
| 106 |
+
timeout: 120000
|
| 107 |
+
});
|
| 108 |
+
|
| 109 |
+
if (response.data && response.data.candidates && response.data.candidates.length > 0) {
|
| 110 |
+
const candidate = response.data.candidates[0];
|
| 111 |
+
if (candidate.content && candidate.content.parts) {
|
| 112 |
+
const imagePart = candidate.content.parts.find(p => p.inlineData);
|
| 113 |
+
if (imagePart && imagePart.inlineData) {
|
| 114 |
+
return imagePart.inlineData.data;
|
| 115 |
+
}
|
| 116 |
+
}
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
console.warn("[Image Service] Unexpected response structure:", JSON.stringify(response.data));
|
| 120 |
+
throw new Error("Empty or invalid response from Image Generation API.");
|
| 121 |
+
|
| 122 |
+
} catch (error) {
|
| 123 |
+
console.error('[Image Service] Error regenerating image:', error.response ? JSON.stringify(error.response.data) : error.message);
|
| 124 |
+
throw error;
|
| 125 |
+
}
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
module.exports = {
|
| 129 |
+
generateImage,
|
| 130 |
+
regenerateImage
|
| 131 |
+
};
|
services/llmService.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* LLM Service
|
| 3 |
+
* Handles communication with Google Gemini API.
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
const axios = require('axios');
|
| 7 |
+
const { llm } = require('./settings');
|
| 8 |
+
|
| 9 |
+
// Configuration
|
| 10 |
+
const API_KEY = process.env.LLM_API_KEY; // Using API Key from .env
|
| 11 |
+
const BASE_URL = llm.baseUrl;
|
| 12 |
+
|
| 13 |
+
/**
|
| 14 |
+
* Sends a prompt to the Gemini LLM and returns the generated text.
|
| 15 |
+
*
|
| 16 |
+
* @param {string} promptText - The fully constructed prompt string (including system instructions if embedded).
|
| 17 |
+
* @param {Object} config - Optional configuration overrides (temperature, etc).
|
| 18 |
+
* @returns {Promise<string>} - The generated response text.
|
| 19 |
+
*/
|
| 20 |
+
async function generateText(promptText, config = {}) {
|
| 21 |
+
if (!API_KEY) {
|
| 22 |
+
throw new Error("API_KEY is not defined in environment variables.");
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
try {
|
| 26 |
+
const url = `${BASE_URL}?key=${API_KEY}`;
|
| 27 |
+
|
| 28 |
+
const payload = {
|
| 29 |
+
contents: [{
|
| 30 |
+
parts: [{
|
| 31 |
+
text: promptText
|
| 32 |
+
}]
|
| 33 |
+
}],
|
| 34 |
+
generationConfig: {
|
| 35 |
+
temperature: config.temperature || 0.7,
|
| 36 |
+
maxOutputTokens: config.maxTokens || 8192,
|
| 37 |
+
}
|
| 38 |
+
};
|
| 39 |
+
|
| 40 |
+
console.log(`[LLM Service] Sending request to ${llm.model}...`);
|
| 41 |
+
|
| 42 |
+
const response = await axios.post(url, payload, {
|
| 43 |
+
headers: {
|
| 44 |
+
'Content-Type': 'application/json'
|
| 45 |
+
}
|
| 46 |
+
});
|
| 47 |
+
|
| 48 |
+
// Extract text from Gemini response structure
|
| 49 |
+
if (response.data && response.data.candidates && response.data.candidates.length > 0) {
|
| 50 |
+
const candidate = response.data.candidates[0];
|
| 51 |
+
if (candidate.content && candidate.content.parts && candidate.content.parts.length > 0) {
|
| 52 |
+
return candidate.content.parts[0].text;
|
| 53 |
+
}
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
console.warn("[LLM Service] Unexpected response structure:", JSON.stringify(response.data));
|
| 57 |
+
return "Error: Empty response from LLM.";
|
| 58 |
+
|
| 59 |
+
} catch (error) {
|
| 60 |
+
console.error('[LLM Service] Error generating text:', error.response ? error.response.data : error.message);
|
| 61 |
+
throw error;
|
| 62 |
+
}
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
module.exports = {
|
| 66 |
+
generateText
|
| 67 |
+
};
|
services/settings.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* Global Settings for Services
|
| 3 |
+
* Stores configuration for LLM and Image Generators (excluding sensitive keys).
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
// LLM Configuration
|
| 7 |
+
const LLM_MODEL = 'gemini-flash-latest';
|
| 8 |
+
const LLM_BASE_URL = `https://generativelanguage.googleapis.com/v1beta/models/${LLM_MODEL}:generateContent`;
|
| 9 |
+
|
| 10 |
+
// Image Generator Configuration
|
| 11 |
+
const IMG_MODEL = 'gemini-3-pro-image-preview';
|
| 12 |
+
const IMG_BASE_URL = `https://generativelanguage.googleapis.com/v1beta/models/${IMG_MODEL}:generateContent`;
|
| 13 |
+
|
| 14 |
+
module.exports = {
|
| 15 |
+
llm: {
|
| 16 |
+
model: LLM_MODEL,
|
| 17 |
+
baseUrl: LLM_BASE_URL
|
| 18 |
+
},
|
| 19 |
+
image: {
|
| 20 |
+
model: IMG_MODEL,
|
| 21 |
+
baseUrl: IMG_BASE_URL
|
| 22 |
+
}
|
| 23 |
+
};
|
test-llm.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const axios = require('axios');
|
| 2 |
+
require('dotenv').config();
|
| 3 |
+
|
| 4 |
+
const API_KEY = process.env.LLM_API_KEY;
|
| 5 |
+
const MODEL = 'gemini-flash-latest';
|
| 6 |
+
const URL = `https://generativelanguage.googleapis.com/v1beta/models/${MODEL}:generateContent?key=${API_KEY}`;
|
| 7 |
+
|
| 8 |
+
async function test() {
|
| 9 |
+
console.log(`Testing LLM with Model: ${MODEL}`);
|
| 10 |
+
console.log(`URL: ${URL.replace(API_KEY, 'HIDDEN_KEY')}`);
|
| 11 |
+
|
| 12 |
+
const payload = {
|
| 13 |
+
contents: [{
|
| 14 |
+
parts: [{ text: "Hello, tell me a joke." }]
|
| 15 |
+
}]
|
| 16 |
+
};
|
| 17 |
+
|
| 18 |
+
try {
|
| 19 |
+
const response = await axios.post(URL, payload, {
|
| 20 |
+
headers: { 'Content-Type': 'application/json' }
|
| 21 |
+
});
|
| 22 |
+
console.log("Success!");
|
| 23 |
+
console.log(response.data.candidates[0].content.parts[0].text);
|
| 24 |
+
} catch (error) {
|
| 25 |
+
console.error("Error Status:", error.response?.status);
|
| 26 |
+
console.error("Error Data:", JSON.stringify(error.response?.data, null, 2));
|
| 27 |
+
}
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
test();
|