нужно чтобы после того как голосовое сообщение переведется в текстовое удалить звуковые файлы mp3 и wav . import { Telegraf, session } from 'telegraf'; index.js
import { message } from 'telegraf/filters';
import { code } from 'telegraf/format';
import config from 'config';
import { ogg } from './src/ogg.js';
import { transcribeAudio, initializeVosk } from './src/transcriber.js';
import { Client } from '@gradio/client';

process.on('uncaughtException', (err) => {
  console.error('Uncaught Exception:', err);
});

process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled Rejection at:', promise, 'reason:', reason);
});

const INITIAL_SESSION = {
  messages: [],
  processedMessages: new Set(),
};

const BOT_TOKEN = config.get('TELEGRAM_TOKEN');
const bot = new Telegraf(BOT_TOKEN);

let gradioClient;
let voskReady = false;

async function initGradioClient() {
  try {
    const client = await Client.connect('yuntian-deng/ChatGPT4');
    console.log('Gradio client initialized');
    return client;
  } catch (error) {
    console.error('Ошибка подключения клиента Gradio:', error);
    throw error;
  }
}

async function getPrediction(app, inputText, apiEndpoint) {
  const parameters = {
    inputs: inputText,
    top_p: 1.0,
    temperature: 1.0,
    chat_counter: 0,
    chatbot: [],
  };

  try {
    const predictionResult = await app.predict(apiEndpoint, [
      parameters.inputs,
      parameters.top_p,
      parameters.temperature,
      parameters.chat_counter,
      parameters.chatbot,
    ]);

    const nestedArray = predictionResult.data[0];

    if (Array.isArray(nestedArray) && nestedArray.length > 0) {
      const lastPair = nestedArray[nestedArray.length - 1];
      if (Array.isArray(lastPair) && lastPair.length > 1) {
        const actualResponse = lastPair[1];
        if (typeof actualResponse === 'string') {
          return actualResponse;
        }
      }
    }

    console.warn('Получен неожиданный формат данных.');
    return 'Получен неожиданный формат данных.';
  } catch (error) {
    console.error(`Ошибка во время запроса ${apiEndpoint}:`, error);
    return 'Извините, произошла ошибка при обработке вашего запроса.';
  }
}

// Инициализация клиента Gradio
initGradioClient()
  .then((client) => {
    gradioClient = client;
    console.log('Gradio client successfully initialized. Launching bot...');
  })
  .catch((error) => {
    console.error('Не удалось инициализировать клиент Gradio:', error);
  });

// Асинхронная инициализация Vosk
(async () => {
  try {
    await initializeVosk();
    voskReady = true;
    console.log('Vosk model initialized');
  } catch (error) {
    console.error('Ошибка инициализации Vosk:', error);
  }
})();

bot.use(session());

bot.command('start', async (ctx) => {
  ctx.session = { ...INITIAL_SESSION };
  await ctx.reply('Жду вашего голосового или текстового сообщения');
});

bot.on(message('voice'), async (ctx) => {
  ctx.session ??= INITIAL_SESSION;

  // Проверяем, было ли сообщение уже обработано
  if (ctx.session.processedMessages.has(ctx.message.message_id)) {
    return; // Пропускаем, если уже обработано
  }

  if (!voskReady) {
    await ctx.reply('Голосовая обработка ещё не готова. Попробуйте позже.');
    return;
  }

  try {
    const link = await ctx.telegram.getFileLink(ctx.message.voice.file_id);
    const userId = String(ctx.message.from.id);
    const oggPath = await ogg.create(link.href, userId);
    const mp3Path = await ogg.toMp3(oggPath, userId);
    const name = ctx.from.first_name;

    const text = await transcribeAudio(mp3Path);
    console.log('Transcription text:', text);

    if (text.includes('кеша') && gradioClient) {
      const textAsked = text.slice(5);
      await ctx.reply(code(`${name}, я понял ваше сообщение: ${text}`));

      try {
        const responseMessage = await getPrediction(gradioClient, textAsked, '/predict');
        console.log('Gradio response:', responseMessage);
        await ctx.reply(`${name}, ${responseMessage}`);

        // Отмечаем сообщение как обработанное
        ctx.session.processedMessages.add(ctx.message.message_id);
      } catch (error) {
        console.log('Error during prediction request:', error.message);
        await ctx.reply('Извините, произошла ошибка при обработке вашего запроса.');
      }
    } else {
      console.log('Сообщение не для Кеши');
    }
  } catch (error) {
    console.log('Ошибка при обработке голосового сообщения:', error.message);
    // await ctx.reply('Произошла ошибка при обработке голосового сообщения.');
  }
});

bot.on(message('text'), async (ctx) => {
  ctx.session ??= INITIAL_SESSION;

  // Проверяем, было ли сообщение уже обработано
  if (ctx.session.processedMessages.has(ctx.message.message_id)) {
    return; // Пропускаем, если уже обработано
  }

  const text = ctx.message.text;

  if (text.includes('Кеша') && gradioClient) {
    const textAsked = text.slice(5);
    try {
      await ctx.reply(code('Сообщение принял. Жду ответ от сервера...'));

      const responseMessage = await getPrediction(gradioClient, textAsked, '/predict');
      const name = ctx.from.first_name;
      await ctx.reply(`${name}, ${responseMessage}`);

      // Отмечаем сообщение как обработанное
      ctx.session.processedMessages.add(ctx.message.message_id);
    } catch (err) {
      console.log('Ошибка при обработке текстового сообщения:', err.message);
      const name = ctx.from.first_name;
      ctx.reply(
        `${name}, "Извините. Но из-за высокой нагрузки на сервер, я не смогу ответить Вам".`,
      );
    }
  }
});

bot.launch();

process.once('SIGINT', () => bot.stop('SIGINT'));
process.once('SIGTERM', () => bot.stop('SIGTERM'));   transcriber.js  import pkg from 'vosk'; 
import fs from 'fs';
import { execFile } from 'child_process';
import path from 'path';

const { Model, Recognizer } = pkg;

const MODEL_PATH = 'e:/program/vosk/vosk-model-ru-0.42/';
const SAMPLE_RATE = 16000;

pkg.setLogLevel(0);

let model = null; // Глобально доступная переменная для модели

// Функция инициализации Vosk
export const initializeVosk = async () => {
  return new Promise((resolve, reject) => {
    if (!fs.existsSync(MODEL_PATH)) {
      console.error('Модель Vosk не найдена');
      return reject('Модель Vosk не найдена');
    }

    try {
      model = new Model(MODEL_PATH); // Инициализируем глобальную модель
      console.log('Vosk model loaded');
      resolve();
    } catch (error) {
      console.error('Ошибка при загрузке модели Vosk:', error);
      reject(error);
    }
  });
};

// Функция транскрипции аудио
export const transcribeAudio = (filePath) => {
  return new Promise((resolve, reject) => {
    console.log('Starting audio transcription');

    const wavPath = filePath.replace('.mp3', '.wav');

    execFile(
      'ffmpeg',
      ['-y', '-i', filePath, '-ar', SAMPLE_RATE.toString(), '-ac', '1', wavPath],
      (error, stdout, stderr) => {
        if (error) {
          console.error('Error during audio conversion:', error);
          return reject(error);
        }

        if (stderr) {
          // console.error('FFmpeg stderr:', stderr.toString());
        }

        console.log('Audio converted successfully');

        if (model === null) {
          return reject(new Error('Vosk model not initialized'));
        }

        const rec = new Recognizer({ model, sampleRate: SAMPLE_RATE });
        const audioStream = fs.createReadStream(path.resolve(wavPath), { highWaterMark: 4096 });

        audioStream.on('data', (data) => {
          rec.acceptWaveform(data);
        });

        audioStream.on('end', () => {
          try {
            const result = rec.finalResult();
            // console.log('Raw transcription result:', result);

            resolve(result.text);
          } catch (err) {
            console.error('Error during transcription:', err);
            reject(err);
          } finally {
            rec.free();
          }
        });

        audioStream.on('error', (err) => {
          console.error('Error in audio stream:', err);
          reject(err);
        });
      },
    );
  });
};  ogg.js  import axios from 'axios';
import ffmpeg from 'fluent-ffmpeg';
import installer from '@ffmpeg-installer/ffmpeg';
import { createWriteStream } from 'fs';
import { dirname, resolve } from 'path';
import { fileURLToPath } from 'url';
import { removeFile } from './utils.js';

const __dirname = dirname(fileURLToPath(import.meta.url));

class OggConverter {
  constructor() {
    ffmpeg.setFfmpegPath(installer.path);
  }

  toMp3(input, output) {
    try {
      const outputPath = resolve(dirname(input), `${output}.mp3`);
      return new Promise((resolve, reject) => {
        ffmpeg(input)
          .inputOption('-t 30')
          .output(outputPath)
          .on('end', () => {
            removeFile(input);
            resolve(outputPath);
          })
          .on('error', (err) => reject(err.message))
          .run();
      });
    } catch (error) {
      console.log(error.message);
    }
  }
  async create(url, filename) {
    try {
      const oggPath = resolve(__dirname, '../voices', `${filename}.ogg`);
      const response = await axios({
        method: 'get',
        url,
        responseType: 'stream',
      });
      return new Promise((resolve) => {
        const stream = createWriteStream(oggPath);
        response.data.pipe(stream);
        stream.on('finish', () => resolve(oggPath));
      });
    } catch (error) {
      console.log(error.message);
    }
  }
}

export const ogg = new OggConverter(); 

utils.js import { unlink } from 'fs/promises';

export async function removeFile(path) {
  try {
    await unlink(path);
  } catch (error) {
    console.log(error.message);
  }
}