.NET / [Из песочницы] Автопереводчик через Skype

в 11:56, , рубрики: .net, Skype4com, метки: ,

С чего всё начиналось

После того, как я получил сообщение от знакомого с просьбой перевести элементарное предложение с русского на английский и знакомый был послан в Гугл.Переводчик, я подумал, что надо бы это дело автоматизировать, но, так как посылать людей не хорошо, я решил забирать информацию с Гугла и отправлять её через Skype. Но это было бы слишком скучно, и я решил, чтобы бот также звонил и говорил на искомом языке заданное предложение.

Немного поисков, и я нашёл библиотеку Skype4COM, которая позволяет взаимодействовать со Skype.

Также, для работы со звуком, я буду использовать NAudio, а чтобы забирать данные с Гугла — xNet.

Начинаем кодить

Для начала, нам необходимо зарегистрировать Skype4COM в системе, для этого выполним

regsvr32 "Путь_к_dll"

После этого мы можем добавить ссылки на все dll в проект.

Теперь нам необходимо присоедениться к Skype:

  //Проверяем, запущен ли Skype             if (!Skype.Client.IsRunning)             {                 Skype.Client.Start(true, true);             }              //Подписываемся на событие присоединения к Skype             ((_ISkypeEvents_Event)Skype).AttachmentStatus += OurAttachmentStatus;              //Подписываемся на события приёма сообщения и звонка             Skype.CallStatus += CallStatusChanged;             Skype.MessageStatus += ReceiveMessage;              //Присоединяемся к Skype             Skype.Attach(8); 

Так как присоединение к Skype может длиться относительно продолжительное время, нам необходимо узнать, когда мы к нему присоединились:

private void OurAttachmentStatus(TAttachmentStatus status)         {             if (status == TAttachmentStatus.apiAttachSuccess)                 textBox1.Text += "Присоединение прошло успешно";         } 

Теперь, когда мы присоединились к Skype, мы можем начать получать сообщения.

private void ReceiveMessage(ChatMessage pmessage, TChatMessageStatus status)         {             //Если сообщение получено             if (status == TChatMessageStatus.cmsReceived)             {                 //И сообщение имеет формат Искомый язык:Сообщение                 string[] message = pmessage.Body.Split(':');                  if (message.Length != 2)                     return;                  string mess = message[1];                 string toLang = message[0];                  //Получаем перевод сообщения                 string translate = GetTranslate(mess, toLang);                  //Получаем озвучку данного сообщения                 byte[] bytes = GetFile(translate, toLang);                  Stream stream = new MemoryStream(bytes);                 //Так как Skype4COM может работать только с wav файлами, перекодируем mp3 в wav.                 TimeSpan time = Mp3ToWav(stream, @"d:test.wav");                  //Звоним абоненту                 Skype.PlaceCall(pmessage.FromHandle);                  //И отправляем данному абоненту перевод                 pmessage.Chat.SendMessage(translate);                  //Задаём, через какое время нужно повесить трубку                 timer = new Timer(time.TotalMilliseconds);                 timer.Elapsed += FinishCall;                 timer.AutoReset = false;             }         }

Для работы с сетью, я решил использовать библиотеку xNet, которая позволяет легко автоматизировать действия с веб-сайтами.

Для начала, нам необходимо получить перевод предложения:

 private string GetTranslate(string message, string toLang)         {             //Создаём необходимые объекты для работы с веб-запросами             using (HttpRequest request = new HttpRequest())             {                 StringDictionary reqParams = new StringDictionary();                  //Можно определять язык, на котором пользователь написал своё сообщение,                  //но так как моим знакомым врятли понадобиться переводить с китайского, то я решил немного схалтурить                 string myLang;                 if (toLang == "en")                 {                     myLang = "ru";                 }                 else                 {                     myLang = "en";                 }                  //Задаём необходимые параметры веб-запроса                 request.UserAgent = HttpHelper.RandomChromeUserAgent();                  reqParams["text"] = message;                 reqParams["tl"] = toLang;                 reqParams["sl"] = myLang;                 reqParams["client"] = "x";                  //Получаем ответ от сервера                 string s = request.Get(                     "http://translate.google.ru/translate_a/t", reqParams).ToText();                  //Выдираем из него перевод                 string translate = s.Substring(":"", """);                  return translate;             } 			         }

Теперь мы можем получить его озвучку:

private byte[] GetFile(string translate, string toLang)         {            using (HttpRequest request = new HttpRequest())             {                 StringDictionary reqParams = new StringDictionary();                  //Задаём необходимые параметры веб-запроса                 reqParams["ie"] = "UTF-8";                 reqParams["q"] = translate;                 reqParams["tl"] = toLang;                 reqParams["prev"] = "input";                  //Получаем файл                 byte[] bytes = request.Get(                       "http://translate.google.ru/translate_tts", reqParams).ToBytes();                  return bytes;             }         }

Теперь нам необходимо перекодировать файл в Wav и сохранить его на диске:

public static TimeSpan Mp3ToWav(Stream mp3File, string outputFile)         {             //Создаём объект для чтения mp3 файла             using (Mp3FileReader reader = new Mp3FileReader(mp3File))             {                 //Задаём формат выходного файла                 var newFormat = new WaveFormat(16000, 16, 1);                 using (WaveStream pcmStream = new WaveFormatConversionStream(newFormat, reader))                 {                     //Записываем перекодированный поток в файл                     WaveFileWriter.CreateWaveFile(outputFile, pcmStream);                      //Возвращаем продолжительность файла                     return reader.TotalTime;                 }             }         }

Теперь мы можем написать код проигрывания файла:

 private void CallStatusChanged(Call pcall, TCallStatus status)         {             //Сохраняем ссылку на звонок, чтобы затем повесить трубку             call = pcall;              //Если соединение произошло             if (status == TCallStatus.clsInProgress)             {                 //Начинаем проигрывание файла                 pcall.set_InputDevice(TCallIoDeviceType.callIoDeviceTypeFile, @"d:test.wav");                  //И запускаем таймер                 timer.Start();             }          }

Ну и код завершения звонка:

 private void FinishCall(object sender, ElapsedEventArgs e)         {             call.Finish();         }

Файлы проекта

В завершение

Я знаю, что наверняка допустил много ошибок в коде, и надеюсь, что гуру программирования меня просветят, в чём же я не прав.

P.S. По-хорошему, заканчивать звонок надо бы было в событии Skype.CallInputStatusChanged, но сколько я не возился, оно у меня так ни разу и не вызвалось.

Автор: RoboNET


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js