После открытия API ботов для Telegram их популяция начала стремительно расти. Я решил не отставать и обзавестись собственным для возможности удаленно включать компьютер. Для разработки был выбран язык C#, а в качестве хостинга выбор пал на Azure.
Задача
Написать бота, который сможет отправлять «магические пакеты» для включения компьютера на указанный адрес и порт.
Примечание: в статье не рассматриваются получение токена для бота и деплой сайта на Azure. На эти вопросы без труда находится ответ на хабре или Google.
Немного теории и сетевые настройки
Для включения компьютера будет использоваться технология Wake on Lan. Поскольку бот будет находиться в облаке, нам нужно отправлять пакет на внешний адрес. В связи с этим, нужно позаботиться о том, чтобы пакет в итоге попал в локальную сеть и достиг нужной сетевой платы. Для этого нужно пробросить порт на маршрутизаторе:
Пакет состоит из набора байт в следующем порядке: первые 6 байтов — нулевые, затем идет последовательность байт из мак-адреса сетевой карты, который повторяется 16 раз. Собственно, именно по MAC адресу сетевая карта и понимает, что нужно включить именно ее компьютер.
Мы будем формировать такой пакет и отправлять его на наш внешний адрес (например, на адрес домашнего роутера), после чего пакет должен будет маршрутизироваться на broadcast адрес локальной сети.
В моем случае пробрасывается 7 порт (доступный из интернета) на шировещательный адрес сети (10.10.10.255).
Пишем код
Для работы с API бота была выбрана библиотека Telegram.Bot. Для получения обновлений будем использовать вариант с вебхуком. В этом случае каждое сообщение или событие, которые будет получать бот, будут отправлены на указанный URL.
В качестве хостинга было решено использовать Azure, так как он подходит по всем параметрам:
- всегда доступен
- на бесплатном тарифе есть сертификат, который позволяет обращаться к сайту по HTTPS (боты могут отправлять обновления только по зашифрованным соединениям)
- быстрая и удобная публикация сайта прямо из Visual Studio
Для начала создаем класс, который будет возвращать нам объект для работы с ботом:
public static class Bot { private static Api _bot; /// <summary> /// Получаем бота, а если он еще /// не инициализирован - инициализируем /// и возвращаем /// </summary> public static Api Get() { if (_bot != null) return _bot; _bot = new Api(Config.BotApiKey); _bot.SetWebhook(Config.WebHookUrl); return _bot; } }
Настройки достаем из класса Config
public static class Config { /// <summary> /// Настройки для бота храним в настройках приложения /// </summary> private static readonly NameValueCollection Appsettings = ConfigurationManager.AppSettings; /// <summary> /// Полученный токен для бота /// </summary> public static string BotApiKey { get { return Appsettings["BotApiKey"]; } } /// <summary> /// URL, на который должны приходить все обновления от бота /// </summary> public static string WebHookUrl { get { return Appsettings["WebHookUrl"]; } } }
Теперь нам нужно формировать и отправлять пакет. За это будет отвечать класс WakeOnLan
/// <summary> /// Может отправлять "волшебные" пакеты для включения удаленного компьютера /// </summary> public static class WakeOnLan { public static void Up(string ip, string mac, int? port = null) { var client = new UdpClient(); var data = new byte[102]; for (var i = 0; i <= 5; i++) // первые шесть байт - нулевые data[i] = 0xff; var macDigits = GetMacDigits(mac); if (macDigits.Length != 6) throw new ArgumentException("Incorrect MAC address supplied!"); const int start = 6; for (var i = 0; i < 16; i++) // создаем нужную последовательность байт для пакета for (var x = 0; x < 6; x++) data[start + i * 6 + x] = (byte)Convert.ToInt32(macDigits[x], 16); client.Send(data, data.Length, ip, port ?? 7); // отправляем пакет } private static string[] GetMacDigits(string mac) // парсим MAC { return mac.Split(mac.Contains("-") ? '-' : ':'); } public static bool ValidateMac(string mac) // простая проверка на валидность MAC адреса { return GetMacDigits(mac).Length == 6; } }
Пакеты формируем и умеем отправлять. Осталось научить бота отвечать на команду, к примеру, /wol.
Для простоты реализована команда с параметрами, т.е. пользователь должен будет ввести примерно следующее
/wol 1.2.3.4 01:02:03:04:05:06 7
для того, чтобы отправить пакет на адрес 1.2.3.4, на 7 порт и разбудить компьютер с MAC адресом 01:02:03:04:05:06
public async void Handle(Message message) { var text = message.Text.Split(' '); if (text.First() != "/wol") return; switch (text.Count()) { case 1: case 2: await _bot.SendTextMessage(message.Chat.Id, "Пример использования: /wol 1.2.3.4 01:02:03:04:05:06 7"); break; default: if (!WakeOnLan.ValidateMac(text[2])) await _bot.SendTextMessage(message.Chat.Id, "Неверный MAC адрес"); else { try { WakeOnLan.Up(text[1], text[2], GetPort(text)); await _bot.SendTextMessage(message.Chat.Id, "Пакет отправлен!"); } catch (Exception) { await _bot.SendTextMessage(message.Chat.Id, "Произошла ошибка :("); } } break; } } /// <summary> /// Получаем порт из параметров /// </summary> private static int? GetPort(IReadOnlyList<string> text) { int port; if (text.Count == 4 && int.TryParse(text[3], out port)) return port; return null; }
Отлично, осталось лишь создать контроллер, который будет принимать обновления от бота:
public class MessageController : ApiController { [Route(@"api/message/wol")] public OkResult Post([FromBody]Update value) { Task.Run(() => new Handler().Handle(value.Message)); return Ok(); } }
После «заливки» приложения в Azure проверяем бота:
Ссылки:
Проект на GitHub
API ботов Telegram
Библиотека Telegram.Bot (GitHub)
ссылка на оригинал статьи http://habrahabr.ru/post/265305/
Добавить комментарий