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