Пишем авто переводчик через Skype на C#

16 Янв
2012

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


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


Финальный вид


В итоге программа выглядела так:
using System;
using System.IO;
using System.Timers;
using System.Windows;
using NAudio.Wave;
using SKYPE4COMLib;
using xNet.Collections;
using xNet.Net;
using xNet.Text;

namespace SkypeAuto
{
/// /// Логика взаимодействия для MainWindow.xaml
///
public partial class MainWindow : Window
{
public static Skype Skype = new Skype();
private Call call;
private Timer timer;

public MainWindow()
{
InitializeComponent();
}

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

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;
}
}

private void button1_Click(object sender, RoutedEventArgs e)
{
//Проверяем, запущен ли 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);
}

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

private void CallStatusChanged(Call pcall, TCallStatus status)
{
//Сохраняем ссылку на звонок, чтобы затем повесить трубку
call = pcall;

//Если соединение произошло
if (status == TCallStatus.clsInProgress)
{
//Начинаем проигрывание файла
pcall.set_InputDevice(TCallIoDeviceType.callIoDeviceTypeFile, @"d:\test.wav");

//И запускаем таймер
timer.Start();
}
}

private 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 string GetTranslate(string message, string toLang)
{
//Создаём необходимые классы для работы с веб-запросами
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)
{
//Создаём необходимые классы для работы с веб-запросами
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;
}
}
}


В завершение


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

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



загрузка...

Комментарии:

Наверх