Pull to refresh

Автопереводчик через Skype

Reading time4 min
Views5.7K
С чего всё начиналось

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

Articles