armyan tts add

This commit is contained in:
2026-03-03 01:10:41 +04:00
parent a0d98b95e5
commit 424823794b
6 changed files with 183 additions and 91 deletions

151
bot.js
View File

@@ -4,6 +4,18 @@ import axios from "axios";
import FormData from 'form-data'; import FormData from 'form-data';
import fs from 'fs'; import fs from 'fs';
import ffmpeg from 'fluent-ffmpeg';
function convertToOgg(inputPath, outputPath) {
return new Promise((resolve, reject) => {
ffmpeg(inputPath)
.audioCodec('libopus')
.format('ogg')
.on('end', () => resolve())
.on('error', (err) => reject(err))
.save(outputPath);
});
}
import { UserRepository } from "./user.repository.js"; import { UserRepository } from "./user.repository.js";
@@ -18,39 +30,42 @@ const bot = new TelegramBot(tokenTg, { polling: true });
console.log("Бот запущен..."); console.log("Бот запущен...");
bot.onText(/\/info/, (msg)=>{ bot.onText(/\/info/, (msg) => {
const message = `Информация о боте: \n const message = `Информация о боте: \n
'/start' - перезапускает чат, забывает контекст прошлого диалого \n '/start' - перезапускает чат, забывает контекст прошлого диалого \n
'/toggleModel' - Выбор модели (еще не доступен)` '/toggleModel' - Выбор модели (еще не доступен)`
const chatId = msg.chat.id const chatId = msg.chat.id
bot.sendMessage(chatId, message) bot.sendMessage(chatId, message)
}) })
bot.onText(/\/armyan (.+)/, async(msg, match)=>{ bot.onText(/\/armyan (.+)/, async (msg, match) => {
const chatId = msg.chat.id; const chatId = msg.chat.id;
const textAfterCommand = match[1]; const textAfterCommand = match[1];
const response = await textToArmyan(textAfterCommand);
await convertToOgg(`voices/${response.file}.wav`, `voices/${response.file}.ogg`);
bot.sendVoice(chatId, `voices/${response.file}.ogg`);
}) })
bot.onText(/\/start/, async(msg) => { bot.onText(/\/start/, async (msg) => {
const chatId = msg.chat.id; const chatId = msg.chat.id;
const chatType = msg.chat.type const chatType = msg.chat.type
const user = msg.from const user = msg.from
try{ try {
const createOrUpdateUser = await UserRepository.createOrUpdateUser({ const createOrUpdateUser = await UserRepository.createOrUpdateUser({
telegramId: user.id, telegramId: user.id,
username: user.username, username: user.username,
chatId: chatId chatId: chatId
}); });
}catch(err){ } catch (err) {
console.error('Ошибка при создании или обновлении пользователя:', err); console.error('Ошибка при создании или обновлении пользователя:', err);
} }
bot.sendMessage(chatId, `Чат перезапущен, тип чата ${chatType}`); bot.sendMessage(chatId, `Чат перезапущен, тип чата ${chatType}`);
}); });
bot.on('message', async(msg) => { bot.on('message', async (msg) => {
const chatId = msg.chat.id; const chatId = msg.chat.id;
const user = msg.from const user = msg.from
@@ -62,115 +77,155 @@ bot.on('message', async(msg) => {
} }
if (msg.photo) { if (msg.photo) {
bot.sendMessage(chatId, 'Красиво, но такое мне не надо', {reply_to_message_id: msg.message_id}); bot.sendMessage(chatId, 'Красиво, но такое мне не надо', { reply_to_message_id: msg.message_id });
return; return;
} }
if (msg.document) { if (msg.document) {
bot.sendMessage(chatId, 'Такое мне не надо', {reply_to_message_id: msg.message_id}); bot.sendMessage(chatId, 'Такое мне не надо', { reply_to_message_id: msg.message_id });
return; return;
} }
if (msg.from.is_bot){ if (msg.from.is_bot) {
bot.sendMessage(chatId, 'Пошел нахуй бот ебанный, не мешай слушать брата', {reply_to_message_id: msg.message_id}); bot.sendMessage(chatId, 'Пошел нахуй бот ебанный, не мешай слушать брата', { reply_to_message_id: msg.message_id });
return; return;
} }
if (msg.voice ){ if (msg.voice) {
const fileId = msg.voice.file_id; const fileId = msg.voice.file_id;
const filePath = await bot.downloadFile(fileId, './voices'); const filePath = await bot.downloadFile(fileId, './voices');
const form = new FormData(); const form = new FormData();
form.append('file', fs.createReadStream(filePath)); form.append('file', fs.createReadStream(filePath));
const response = await axios.post( const response = await axios.post(
'http://10.8.1.2:3000/upload', `http://${process.env.API_URL}:${process.env.API_PORT}/upload`,
form, form,
{ {
headers: form.getHeaders() headers: form.getHeaders()
} }
); );
if(response.status === 200){ if (response.status === 200) {
const recognizedText = response.data.text; const recognizedText = response.data.text;
bot.sendMessage(chatId, recognizedText, {reply_to_message_id: msg.message_id}); bot.sendMessage(chatId, recognizedText, { reply_to_message_id: msg.message_id });
}else{ } else {
bot.sendMessage(chatId, 'Что-то пошло не так', {reply_to_message_id: msg.message_id}); bot.sendMessage(chatId, 'Что-то пошло не так', { reply_to_message_id: msg.message_id });
} }
return; return;
} }
if (msg.text && user.id === 5536333712){ if (msg.text && user.id === 5536333712) {
console.log('Армянин, переключаем модель') console.log('Армянин, переключаем модель')
const response = await switchToRussian(msg.text); const response = await switchToRussian(msg.text);
bot.sendMessage(chatId, response.message, {reply_to_message_id: msg.message_id}); bot.sendMessage(chatId, response.message, { reply_to_message_id: msg.message_id });
return; return;
} }
if (msg.text && msg.chat.type === 'private') { if (msg.text && msg.chat.type === 'private') {
//bot.sendMessage(chatId, `Ты написал: ${msg.text}`); //bot.sendMessage(chatId, `Ты написал: ${msg.text}`);
try{ try {
const last_msg_context = await UserRepository.getContext(user.id) || '' const last_msg_context = await UserRepository.getContext(user.id) || ''
console.log('last_msg_context:', last_msg_context); console.log('last_msg_context:', last_msg_context);
if(last_msg_context === ''){ if (last_msg_context === '') {
console.log('Новый пользователь, начинаем диалог с чистого листа'); console.log('Новый пользователь, начинаем диалог с чистого листа');
const response = await getAIResponse(msg.text, null); const response = await getAIResponse(msg.text, null);
bot.sendMessage(chatId, response.message); bot.sendMessage(chatId, response.message);
await UserRepository.updateContext(user.id, response.id); await UserRepository.updateContext(user.id, response.id);
}else{ } else {
console.log('Продолжаем диалог, контекст найден'); console.log('Продолжаем диалог, контекст найден');
const response = await getAIResponse(msg.text, last_msg_context); const response = await getAIResponse(msg.text, last_msg_context);
bot.sendMessage(chatId, response.message); bot.sendMessage(chatId, response.message);
await UserRepository.updateContext(user.id, response.id); await UserRepository.updateContext(user.id, response.id);
} }
}catch(err){ } catch (err) {
console.error('Ошибка при обработке сообщения:', err); console.error('Ошибка при обработке сообщения:', err);
} }
return; return;
} }
}); });
async function switchToRussian(message){ async function switchToRussian(message) {
try{ try {
const options = { const options = {
method:'POST', method: 'POST',
url:`https://agent.timeweb.cloud/api/v1/cloud-ai/agents/${accessId}/call`, url: `https://agent.timeweb.cloud/api/v1/cloud-ai/agents/${accessId}/call`,
headers: { headers: {
Authorization: `Bearer ${tokenAi}`, Authorization: `Bearer ${tokenAi}`,
'x-proxy-source': '', 'x-proxy-source': '',
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
data:{ data: {
message: `Убери ошибки в тексте и поставь знаки препинания: "${message}". Если текст короткий или без явных ошибок ничего не делай и отвечай просто "Армянин молодец", особо не размышляй над содержимым текста, просто выполняй свою задачу. Так же делай рядом версию с переводом на армянский язык`, message: `Убери ошибки в тексте и поставь знаки препинания: "${message}". Если текст короткий или без явных ошибок ничего не делай и отвечай просто "Армянин молодец", особо не размышляй над содержимым текста, просто выполняй свою задачу. Так же делай рядом версию с переводом на армянский язык`,
parent_message_id: '' parent_message_id: ''
} }
} }
const response = await axios.request(options); const response = await axios.request(options);
return response.data; return response.data;
}catch(err){ } catch (err) {
console.error('Ошибка при получении ответа от AI:', err); console.error('Ошибка при получении ответа от AI:', err);
} }
} }
async function getAIResponse(message, context) { async function getAIResponse(message, context) {
try{ try {
const options = { const options = {
method:'POST', method: 'POST',
url:`https://agent.timeweb.cloud/api/v1/cloud-ai/agents/${accessId}/call`, url: `https://agent.timeweb.cloud/api/v1/cloud-ai/agents/${accessId}/call`,
headers: { headers: {
Authorization: `Bearer ${tokenAi}`, Authorization: `Bearer ${tokenAi}`,
'x-proxy-source': '', 'x-proxy-source': '',
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
data:{ data: {
message: message, message: message,
parent_message_id: context parent_message_id: context
} }
} }
const response = await axios.request(options); const response = await axios.request(options);
return response.data; return response.data;
}catch(err){ } catch (err) {
console.error('Ошибка при получении ответа от AI:', err);
}
}
async function textToArmyan(message) {
try {
const optionsArmyan = {
method: 'POST',
url: `https://agent.timeweb.cloud/api/v1/cloud-ai/agents/${accessId}/call`,
headers: {
Authorization: `Bearer ${tokenAi}`,
'x-proxy-source': '',
'Content-Type': 'application/json'
},
data: {
message: `Переведи текст на армянский, ничего не добаляй от себя, просто переведи: '${message}'`,
parent_message_id: ''
}
}
const responseArmyan = await axios.request(optionsArmyan);
const options = {
method: 'POST',
url: `http://${process.env.API_URL}:${process.env.API_PORT}/voice/start`,
headers: {
'Content-Type': 'application/json'
},
data: {
text: responseArmyan.data.message
}
}
const response = await axios.request(options);
if (response.status === 200) {
return response.data;
} else {
console.error('Ошибка при получении ответа от AI:', response.data);
}
} catch (err) {
console.error('Ошибка при получении ответа от AI:', err); console.error('Ошибка при получении ответа от AI:', err);
} }
} }

View File

@@ -7,3 +7,7 @@ DATABASE_URL =
DATABASE_USER = DATABASE_USER =
DATABASE_PASSWORD = DATABASE_PASSWORD =
GEMINI_KEY = GEMINI_KEY =
PORT = 3000
JWT_SECRET = super_secret_key_123
API_URL = 10.8.1.3
API_PORT = 3000

View File

@@ -17,6 +17,7 @@
"cors": "^2.8.6", "cors": "^2.8.6",
"dotenv": "^17.2.3", "dotenv": "^17.2.3",
"express": "^5.2.1", "express": "^5.2.1",
"fluent-ffmpeg": "^2.1.3",
"form-data": "^4.0.5", "form-data": "^4.0.5",
"jsonwebtoken": "^9.0.3", "jsonwebtoken": "^9.0.3",
"multer": "^2.0.2", "multer": "^2.0.2",

View File

@@ -8,30 +8,29 @@ import { GoogleGenAI, createUserContent, createPartFromUri } from "@google/genai
import dotenv from "dotenv"; import dotenv from "dotenv";
dotenv.config(); dotenv.config();
import wav from 'wav';
import { text } from "stream/consumers"; import { text } from "stream/consumers";
async function shneine(fileName, mimeType){ async function shneine(fileName, mimeType) {
const myfile = await ai.files.upload({ const myfile = await ai.files.upload({
file:`./uploads/${fileName}`, file: `./uploads/${fileName}`,
config:{ config: {
mimeType:mimeType, mimeType: mimeType,
} }
}); });
const response = await ai.models.generateContent({ const response = await ai.models.generateContent({
model: "gemini-3-flash-preview", model: "gemini-3-flash-preview",
contents: createUserContent([ contents: createUserContent([
createPartFromUri(myfile.uri, myfile.mimeType), createPartFromUri(myfile.uri, myfile.mimeType),
'Что дословно говорится в этом аудиофайле? отвечай сухо, чисто текст, может исправить ошибки в тексте и поставить знаки препинания. Если звуковая дорожка маленькая 1-2 секунды и в ней нету слов или не понятна, отвечай "Армянин молодец, напердел". Так же делай рядом версию с переводом на армянский язык' 'Что дословно говорится в этом аудиофайле? отвечай сухо, чисто текст, может исправить ошибки в тексте и поставить знаки препинания. Если звуковая дорожка маленькая 1-2 секунды и в ней нету слов или не понятна, отвечай "Армянин молодец, напердел". Так же делай рядом версию с переводом на армянский язык'
]) ])
}) })
return response.text; return response.text;
} }
// The client gets the API key from the environment variable `GEMINI_API_KEY`. // The client gets the API key from the environment variable `GEMINI_API_KEY`.
const ai = new GoogleGenAI({apiKey: process.env.GEMINI_KEY}); const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_KEY });
const router = Router(); const router = Router();
@@ -58,7 +57,7 @@ const storage = multer.diskStorage({
const upload = multer({ const upload = multer({
storage, storage,
limits: { limits: {
fileSize: 50 * 1024 * 1024, // 5 MB fileSize: 500 * 1024 * 1024, // 5 MB
}, },
}); });

View File

@@ -2,12 +2,15 @@ import { Router } from "express";
import multer from "multer"; import multer from "multer";
import path from "path"; import path from "path";
import fs from "fs"; import fs from "fs";
import wav from "wav";
const router = Router(); const router = Router();
import { GoogleGenAI, createUserContent, createPartFromUri } from "@google/genai"; import { GoogleGenAI, createUserContent, createPartFromUri } from "@google/genai";
import dotenv from "dotenv"; import dotenv from "dotenv";
dotenv.config(); dotenv.config();
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_KEY });
async function saveWaveFile( async function saveWaveFile(
filename, filename,
pcmData, pcmData,
@@ -17,9 +20,9 @@ async function saveWaveFile(
) { ) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const writer = new wav.FileWriter(filename, { const writer = new wav.FileWriter(filename, {
channels, channels,
sampleRate: rate, sampleRate: rate,
bitDepth: sampleWidth * 8, bitDepth: sampleWidth * 8,
}); });
writer.on('finish', resolve); writer.on('finish', resolve);
@@ -30,34 +33,63 @@ async function saveWaveFile(
}); });
} }
async function fawatafa() { const voicesDir = "voices";
if (!fs.existsSync(voicesDir)) {
fs.mkdirSync(voicesDir);
}
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, voicesDir);
},
filename: (req, file, cb) => {
const uniqueName =
Date.now() + "-" + Math.round(Math.random() * 1e9);
const ext = path.extname(file.originalname);
cb(null, uniqueName + ext);
},
});
const upload = multer({
storage,
limits: {
fileSize: 500 * 1024 * 1024, // 50 MB
},
});
async function fawatafa(text) {
const response = await ai.models.generateContent({ const response = await ai.models.generateContent({
model: "gemini-2.5-flash-preview-tts", model: "gemini-2.5-flash-preview-tts",
contents: [{ parts: [{ text: `Say aggresive: А перекинул я этот пост, потому что его Саша переслал. И вот вдумайся: Саше 30 лет, и вот на него это полностью работает. То есть он верит, что можно заставить Дурова открыть представительство в России, и это снимет к нему все вопросы, и Телеграм оставят незаблокированным` }] }], contents: [{ parts: [{ text: `Say aggresive: ${text}` }] }],
config: { config: {
responseModalities: ['AUDIO'], responseModalities: ['AUDIO'],
speechConfig: { speechConfig: {
voiceConfig: { voiceConfig: {
prebuiltVoiceConfig: { voiceName: 'Leda' }, prebuiltVoiceConfig: { voiceName: 'Sadaltager' },
},
}, },
},
}, },
}); });
const data = response.candidates?.[0]?.content?.parts?.[0]?.inlineData?.data; const data = response.candidates?.[0]?.content?.parts?.[0]?.inlineData?.data;
const audioBuffer = Buffer.from(data, 'base64'); const audioBuffer = Buffer.from(data, 'base64');
const file = Date.now() + "-" + Math.round(Math.random() * 1e9);
const fileName = 'outMarat5.wav'; const fileName = `${file}.wav`;
await saveWaveFile(fileName, audioBuffer); const filePath = path.join(voicesDir, fileName);
await saveWaveFile(filePath, audioBuffer);
return file;
} }
router.post('/voice', async(req, res) => { router.post('/start', async (req, res) => {
try{ try {
const text = req.body.text;
}catch(err){ const fileName = await fawatafa(text);
console.error('Ошибка при обработке голосового сообщения:', err); res.status(200).json({ message: олосовое сообщение успешно обработано', file: fileName });
res.status(500).json({ message: 'Ошибка при обработке голосового сообщения' }); } catch (err) {
} console.error('Ошибка при обработке голосового сообщения:', err);
res.status(500).json({ message: 'Ошибка при обработке голосового сообщения' });
}
}) })

View File

@@ -6,7 +6,7 @@ import cors from 'cors'
import authRoutes from "./routes/auth.js"; import authRoutes from "./routes/auth.js";
import userRoutes from "./routes/user.js"; import userRoutes from "./routes/user.js";
import uploadRoutes from "./routes/upload.js"; import uploadRoutes from "./routes/upload.js";
import voiceRoutes from "./routes/voice.js";
dotenv.config(); dotenv.config();
const app = express(); const app = express();
@@ -17,6 +17,7 @@ app.use(cors());
app.use("/auth", authRoutes); app.use("/auth", authRoutes);
app.use("/user", userRoutes); app.use("/user", userRoutes);
app.use("/upload", uploadRoutes); app.use("/upload", uploadRoutes);
app.use("/voice", voiceRoutes);
app.get("/health", (req, res) => { app.get("/health", (req, res) => {
@@ -25,6 +26,6 @@ app.get("/health", (req, res) => {
const PORT = process.env.PORT || 3000; const PORT = process.env.PORT || 3000;
const HOST = "0.0.0.0" const HOST = "0.0.0.0"
app.listen(PORT,"0.0.0.0", () => { app.listen(PORT, "0.0.0.0", () => {
console.log(`🚀 Server started on http://localhost:${PORT}`); console.log(`🚀 Server started on http://localhost:${PORT}`);
}); });