С чего всё начиналось
После того, как я получил сообщение от знакомого с просьбой перевести элементарное предложение с русского на английский и знакомый был послан в Гугл.Переводчик, я подумал, что надо бы это дело автоматизировать, но, так как посылать людей не хорошо, я решил забирать информацию с Гугла и отправлять её через 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