201 lines
7.1 KiB
JavaScript
201 lines
7.1 KiB
JavaScript
const express = require('express');
|
||
const cors = require('cors');
|
||
const cron = require('node-cron');
|
||
const fs = require('fs').promises;
|
||
const path = require('path');
|
||
require('dotenv').config();
|
||
|
||
const app = express();
|
||
const PORT = process.env.PORT || 3000;
|
||
|
||
// Middleware
|
||
app.use(cors());
|
||
app.use(express.json());
|
||
app.use(express.static('.'));
|
||
|
||
// Файл для хранения цитат
|
||
const QUOTES_FILE = path.join(__dirname, 'quotes.json');
|
||
|
||
// Загрузка цитат из файла
|
||
async function loadQuotes() {
|
||
try {
|
||
const data = await fs.readFile(QUOTES_FILE, 'utf8');
|
||
return JSON.parse(data);
|
||
} catch (error) {
|
||
return [];
|
||
}
|
||
}
|
||
|
||
// Сохранение цитат в файл
|
||
async function saveQuotes(quotes) {
|
||
await fs.writeFile(QUOTES_FILE, JSON.stringify(quotes, null, 2));
|
||
}
|
||
|
||
// Генерация цитаты через OpenRouter
|
||
async function generateQuote() {
|
||
const prompt = `Generate a gay pride quote in Russian on behalf of Denis Shulepov.
|
||
The quote should be:
|
||
- Short and concise (1-3 sentences)
|
||
- Bold, confident and unapologetic about being gay
|
||
- Celebrate gay culture, freedom and pride
|
||
- Witty, sarcastic and clever
|
||
- Support LGBTQ+ community and gay lifestyle
|
||
- Elegant but with attitude
|
||
|
||
Topics: gay pride, gay culture, fashion, style, relationships, love, freedom, celebrating being gay, self-expression, beauty.
|
||
|
||
Use gay slang, humor and vivid imagery. Be bold and confident. The quote should celebrate gay culture and pride with style and elegance. Never use crude street language or insults.
|
||
|
||
Answer only with the text of the quote, without additional explanations. Answer in Russian!`;
|
||
|
||
try {
|
||
const requestBody = JSON.stringify({
|
||
model: process.env.OPENROUTER_MODEL || 'google/gemini-2.5-flash-lite-preview-09-2025',
|
||
messages: [
|
||
{
|
||
role: 'user',
|
||
content: prompt
|
||
}
|
||
],
|
||
max_tokens: 200,
|
||
temperature: 0.9
|
||
});
|
||
|
||
const response = await fetch('https://openrouter.ai/api/v1/chat/completions', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
|
||
'Content-Type': 'application/json; charset=utf-8',
|
||
'HTTP-Referer': 'http://localhost:3000',
|
||
'X-Title': 'Gay Quotes App'
|
||
},
|
||
body: requestBody
|
||
});
|
||
|
||
if (!response.ok) {
|
||
const errorText = await response.text();
|
||
throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`);
|
||
}
|
||
|
||
const data = await response.json();
|
||
return data.choices[0].message.content.trim();
|
||
} catch (error) {
|
||
console.error('Ошибка при генерации цитаты:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// Автоматическая публикация цитаты
|
||
async function autoPublishQuote() {
|
||
try {
|
||
console.log('Автоматическая генерация цитаты...');
|
||
|
||
const quotes = await loadQuotes();
|
||
const quoteText = await generateQuote();
|
||
|
||
const newQuote = {
|
||
id: Date.now().toString(),
|
||
text: quoteText,
|
||
author: 'Денис Шулепов',
|
||
date: new Date().toISOString()
|
||
};
|
||
|
||
quotes.unshift(newQuote);
|
||
|
||
// Ограничиваем количество цитат до 100
|
||
if (quotes.length > 100) {
|
||
quotes.splice(100);
|
||
}
|
||
|
||
await saveQuotes(quotes);
|
||
console.log('Новая цитата опубликована:', quoteText);
|
||
|
||
return newQuote;
|
||
} catch (error) {
|
||
console.error('Ошибка при автоматической публикации цитаты:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// API Routes
|
||
app.get('/api/quotes', async (req, res) => {
|
||
try {
|
||
const quotes = await loadQuotes();
|
||
res.json(quotes);
|
||
} catch (error) {
|
||
res.status(500).json({ error: 'Ошибка при загрузке цитат' });
|
||
}
|
||
});
|
||
|
||
app.post('/api/quotes', async (req, res) => {
|
||
try {
|
||
const newQuote = await autoPublishQuote();
|
||
res.json(newQuote);
|
||
} catch (error) {
|
||
res.status(500).json({ error: 'Ошибка при генерации цитаты' });
|
||
}
|
||
});
|
||
|
||
app.delete('/api/quotes/:id', async (req, res) => {
|
||
try {
|
||
const quotes = await loadQuotes();
|
||
const filteredQuotes = quotes.filter(q => q.id !== req.params.id);
|
||
await saveQuotes(filteredQuotes);
|
||
res.json({ success: true });
|
||
} catch (error) {
|
||
res.status(500).json({ error: 'Ошибка при удалении цитаты' });
|
||
}
|
||
});
|
||
|
||
// Запуск автопубликации каждый час
|
||
cron.schedule('0 * * * *', async () => {
|
||
console.log('Запланированная публикация цитаты...');
|
||
await autoPublishQuote();
|
||
});
|
||
|
||
// Инициализация при старте сервера
|
||
async function initializeServer() {
|
||
try {
|
||
// Создаем файл с цитатами если его нет
|
||
try {
|
||
await fs.access(QUOTES_FILE);
|
||
} catch {
|
||
const initialQuotes = [
|
||
{
|
||
id: '1',
|
||
text: 'Если жизнь подкидывает лимоны, сделай не лимонад, а шикарный коктейль с водкой и танцуй на барной стойке!',
|
||
author: 'Денис Шулепов',
|
||
date: new Date(Date.now() - 86400000).toISOString()
|
||
},
|
||
{
|
||
id: '2',
|
||
text: 'Быть собой — это самый эпатажный coming out в мире, дорогие. И я уже на подиуме!',
|
||
author: 'Денис Шулепов',
|
||
date: new Date(Date.now() - 172800000).toISOString()
|
||
},
|
||
{
|
||
id: '3',
|
||
text: 'Драма — это когда ты не нашёл туфли 42 размера. Трагедия — когда они есть, но не в твоём цвете.',
|
||
author: 'Денис Шулепов',
|
||
date: new Date(Date.now() - 259200000).toISOString()
|
||
}
|
||
];
|
||
await saveQuotes(initialQuotes);
|
||
console.log('Создан начальный файл с цитатами');
|
||
}
|
||
|
||
// Публикуем первую цитату при старте
|
||
await autoPublishQuote();
|
||
|
||
} catch (error) {
|
||
console.error('Ошибка при инициализации сервера:', error);
|
||
}
|
||
}
|
||
|
||
// Запуск сервера
|
||
app.listen(PORT, () => {
|
||
console.log(`Сервер запущен на порту ${PORT}`);
|
||
console.log(`Гейский цитатник доступен по адресу: http://localhost:${PORT}`);
|
||
initializeServer();
|
||
});
|