1
0
mirror of https://github.com/salexdv/Telemonitor1C.git synced 2025-02-21 19:06:45 +02:00
Telemonitor1C/TelegramWorker.cs

494 lines
18 KiB
C#

/*
* Created by SharpDevelop.
* User: Alex
* Date: 07.08.2015
* Time: 10:47
*
* To change this template use Tools | Options | Coding | Edit Standard Headers.
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net;
using System.IO;
using System.Net.Mime;
using System.Threading;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text;
using System.Windows.Forms;
using System.Web;
using Newtonsoft.Json;
using System.Timers;
namespace Telemonitor
{
/// <summary>
/// Класс для работы с API Telegram (получение сообщений и отправка ответов на них)
/// </summary>
public class TelegramWorker
{
/// <summary>
/// Настройки программы
/// </summary>
private Settings tmSettings;
/// <summary>
/// Token бота
/// </summary>
private string botToken;
/// <summary>
/// Смещение для опроса getUpdates
/// </summary>
private int tmOffset;
/// <summary>
/// Mutex для записи лога из разных потоков
/// </summary>
private Mutex mutLogger;
/// <summary>
/// Mutex для запросов к API
/// </summary>
private Mutex mutAPI;
/// <summary>
/// Основной таймер для работы опроса бота через
/// заданные промежутки времени
/// </summary>
private System.Timers.Timer listenerTimer;
/// <summary>
/// Очередь необработанных сообщений
/// </summary>
private Dictionary<int, TelegramCommand> messageOrder;
/// <summary>
/// Конструктор
/// <PARAM name="settings">Настройки</PARAM>
/// </summary>
public TelegramWorker(Settings settings)
{
this.tmOffset = 0;
this.tmSettings = settings;
this.botToken = settings.BotToken;
this.mutLogger = new Mutex();
this.mutAPI = new Mutex();
this.messageOrder = new Dictionary<int, TelegramCommand>();
}
/// <summary>
/// Запускает работу с api.telegram.org
/// </summary>
public void StartWork()
{
listenerTimer = new System.Timers.Timer(this.tmSettings.Interval * 1000);
listenerTimer.Elapsed += new ElapsedEventHandler(listenerTimer_Elapsed);
listenerTimer.Start();
//listener = new BackgroundWorker();
//listener.WorkerSupportsCancellation = true;
//listener.DoWork += new DoWorkEventHandler(listener_DoWork);
//listener.RunWorkerAsync();
}
void listenerTimer_Elapsed(object sender, ElapsedEventArgs e)
{
// Получение updates с заданной периодичностью
HttpWebRequest request = null;
string url = "https://api.telegram.org/bot{0}/getUpdates?offset=" + this.tmOffset.ToString();
Logger.Debug(tmSettings, "url: " + url, false, mutLogger);
//Logger.Debug(tmSettings, "mt wait", false, mutLogger);
mutAPI.WaitOne();
request = CreateRequest(String.Format(url, botToken));
//Logger.Debug(tmSettings, "request created", false, mutLogger);
TelegramAnswer answer = null;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) {
//Logger.Debug(tmSettings, "response ok", false, mutLogger);
using (StreamReader reader = new StreamReader(response.GetResponseStream())) {
string jsonText = reader.ReadToEnd();
// Убираем пикрограммы
jsonText = jsonText.Replace(Const.PIC_BUTTON_START_REP, "");
jsonText = jsonText.Replace(Const.PIC_BUTTON_OTHER_REP, "");
Logger.Debug(tmSettings, "request:" + jsonText, false, mutLogger);
// Получение updates из JSON
answer = JsonConvert.DeserializeObject<TelegramAnswer>(jsonText);
Logger.Debug(tmSettings, answer.ok.ToString(), false, mutLogger);
}
}
request = null;
// Обработка, полученных updates
if (answer != null)
tmOffset = Math.Max(tmOffset, listener_CheckTelegramAnswer(answer));
mutAPI.ReleaseMutex();
}
/// <summary>
/// Оправляет multipart/form-data на заданный url
/// <PARAM name="url">Адрес url</PARAM>
/// <PARAM name="pData">PostData</PARAM>
/// </summary>
private void SendMultipartFormdata(string url, PostData pData)
{
//Logger.Debug(tmSettings, "mt wait", false, mutLogger);
mutAPI.WaitOne();
//Logger.Debug(tmSettings, "mt success", false, mutLogger);
HttpWebRequest request = CreateRequest(String.Format(url, botToken));
request.Method = WebRequestMethods.Http.Post;
request.KeepAlive = true;
request.Credentials = System.Net.CredentialCache.DefaultCredentials;
request.ContentType = "multipart/form-data; boundary=" + pData.Boundary;
MemoryStream postDataStream = pData.GetPostData();
request.ContentLength = postDataStream.Length;
//Logger.Debug(tmSettings, "request created", false, mutLogger);
using (Stream s = request.GetRequestStream())
{
//Logger.Debug(tmSettings, "write to stream", false, mutLogger);
postDataStream.WriteTo(s);
//Logger.Debug(tmSettings, "flush", false, mutLogger);
postDataStream.Flush();
}
//Logger.Debug(tmSettings, "get response", false, mutLogger);
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) {
//Logger.Debug(tmSettings, "response ok", false, mutLogger);
using (StreamReader reader = new StreamReader(response.GetResponseStream())) {
string jsonText = reader.ReadToEnd();
Logger.Debug(tmSettings, "answer to response: " + jsonText, false, mutLogger);
TelegramAnswerMessage answer = JsonConvert.DeserializeObject<TelegramAnswerMessage>(jsonText);
Logger.Debug(tmSettings, answer.ok.ToString());
if (!answer.ok)
Logger.Write(answer.description, true, mutLogger);
}
}
request = null;
postDataStream.Close();
postDataStream.Dispose();
mutAPI.ReleaseMutex();
//Logger.Debug(tmSettings, "mt release", false, mutLogger);
}
/// <summary>
/// Отправляет фото image/png в заданный чат
/// <PARAM name="chat_id">Идентификатор чата</PARAM>
/// <PARAM name="fileName">Имя отправляемого файла</PARAM>
/// </summary>
private void SendPhoto(int chat_id, string fileName)
{
string url = "https://api.telegram.org/bot{0}/sendPhoto";
Logger.Debug(tmSettings, "url: " + url, false, mutLogger);
Logger.Debug(tmSettings, "response file: " + fileName, false, mutLogger);
PostData pData = new PostData();
pData.Params.Add(new PostDataParam("chat_id", chat_id.ToString(), PostDataParamType.Field));
pData.Params.Add(new PostDataParam("caption", "Скриншот " + DateTime.Now.ToString(), PostDataParamType.Field));
pData.Params.Add(new PostDataParam("photo", fileName, "image/png"));
SendMultipartFormdata(url, pData);
pData.Dispose();
}
/// <summary>
/// Отправляет сообщение в заданный чат
/// <PARAM name="chat_id">Идентификатор чата</PARAM>
/// <PARAM name="message">Текст сообщения</PARAM>
/// </summary>
private void SendMessage(int chat_id, string message, string keyboard = "")
{
string url = "https://api.telegram.org/bot{0}/sendMessage";
Logger.Debug(tmSettings, "url: " + url, false, mutLogger);
Logger.Debug(tmSettings, "response: " + message, false, mutLogger);
PostData pData = new PostData();
pData.Params.Add(new PostDataParam("chat_id", chat_id.ToString(), PostDataParamType.Field));
pData.Params.Add(new PostDataParam("text", message, PostDataParamType.Field));
if (!String.IsNullOrEmpty(keyboard))
pData.Params.Add(new PostDataParam("reply_markup", keyboard, PostDataParamType.Field));
else
{
if (tmSettings.HideButtonsAfterMessage)
pData.Params.Add(new PostDataParam("reply_markup", JsonConvert.SerializeObject(new TelegramReplyKeyboardHide()), PostDataParamType.Field));
}
SendMultipartFormdata(url, pData);
pData.Dispose();
}
/// <summary>
/// Отправляет документ (файл) в заданный чат
/// <PARAM name="chat_id">Идентификатор чата</PARAM>
/// <PARAM name="fName">Имя файла</PARAM>
/// </summary>
private void SendDocument(int chat_id, string fileName)
{
if (File.Exists(fileName)) {
string url = "https://api.telegram.org/bot{0}/sendDocument";
Logger.Debug(tmSettings, "url: " + url, false, mutLogger);
Logger.Debug(tmSettings, "response file: " + fileName, false, mutLogger);
PostData pData = new PostData();
pData.Params.Add(new PostDataParam("chat_id", chat_id.ToString(), PostDataParamType.Field));
pData.Params.Add(new PostDataParam("document", fileName, ""));
SendMultipartFormdata(url, pData);
pData.Dispose();
}
else
Logger.Write("Файл для отправки " + fileName + " не обнаружен", true, mutLogger);
}
/// <summary>
/// Создает HttpWebRequest с заданным Url
/// <PARAM name="url">Адрес url</PARAM>
/// </summary>
/// <returns>HttpWebRequest</returns>
private HttpWebRequest CreateRequest(string url)
{
HttpWebRequest request = HttpWebRequest.Create(url) as HttpWebRequest;
request.Method = WebRequestMethods.Http.Post;
request.Timeout = 5000;
if (tmSettings.UseProxy) {
request.Proxy = new WebProxy(tmSettings.ProxyServer, tmSettings.ProxyPort);
}
return request;
}
/// <summary>
/// Делает скриншот всей области экрана и возващает имя файла PNG
/// </summary>
/// <returns>string</returns>
private string getScreenShot() {
// Получение скриншота
string tmpFileName = Path.GetTempFileName();
string fileName = Path.GetTempPath() + Path.GetFileNameWithoutExtension(tmpFileName) + ".png";
Bitmap bmp = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
using (var gr = Graphics.FromImage(bmp)) {
gr.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y,
0, 0, Screen.PrimaryScreen.Bounds.Size);
}
// Сохранение в файл
bmp.Save(fileName, ImageFormat.Png);
bmp.Dispose();
File.Delete(tmpFileName);
return fileName;
}
/// <summary>
/// Обработчик для BackgroundWorker, который обрабатывает команды
/// </summary>
private void ExecuteCommand(object sender, DoWorkEventArgs e)
{
TelegramCommand tCommand = (TelegramCommand)e.Argument;
V8Connector connector = new V8Connector(tCommand.Command, tCommand.Parameters, tmSettings.SafeMode1C);
Logger.Debug(tmSettings, "Запуск команды " + tCommand.Command.ID + " на выполнение", false, mutLogger);
// Создание ComConnector и выполнение кода команды
V8Answer result = connector.Execute(mutLogger);
Logger.Debug(tmSettings, "Команда " + tCommand.Command.ID + " выполнена", false, mutLogger);
if (connector.Success) {
if (!String.IsNullOrEmpty(result.Text))
SendMessage(tCommand.Message.chat.id, result.Text);
if (!String.IsNullOrEmpty(result.FileName))
SendDocument(tCommand.Message.chat.id, result.FileName);
if (String.IsNullOrEmpty(result.Text) && String.IsNullOrEmpty(result.FileName))
SendMessage(tCommand.Message.chat.id, "Команда выполнена");
}
else
SendMessage(tCommand.Message.chat.id, "Ошибка при выполнении команды");
connector.Dispose();
messageOrder.Remove(tCommand.Message.message_id);
((BackgroundWorker)sender).CancelAsync();
}
/// <summary>
/// Возвращает параметры из команды (все что отделено пробелом).
/// Текст команды при этом приводится к чистому виду.
/// </summary>
/// <param name="cmd">Текст поступившей команды</param>
/// <returns>Параметры команды, разделенные запятыми</returns>
private string extractParamForCommand(string cmd, out string newCmd)
{
string param = "";
string[] subStr = cmd.Split(' ');
newCmd = subStr[0];
for (int i = 1; i < subStr.Length; i++) {
param += subStr[i];
param += ',';
}
if (!String.IsNullOrEmpty(param))
param = param.Remove(param.Length - 1);
return param;
}
/// <summary>
/// Разбирает полученное сообщение и определяет, что с ним делать
/// <PARAM name="message">Сообщение TelegramMessage</PARAM>
/// </summary>
private void listener_CheckMessage(TelegramMessage message)
{
if (message.text == null) {
SendMessage(message.chat.id, "Неизвестная команда");
}
else {
string text = message.ToString();
string username = message.from.username;
string param = extractParamForCommand(text, out text);
text = text.ToLower();
// Фильтрация пользователей
if (tmSettings.AllowableUser(username)) {
if (text == "/start" || text == "/help" || text == "/settings") {
// Запрошен список команд
SendMessage(message.chat.id, tmSettings.GetCommands(username), tmSettings.GetKeyboardCommands(username));
}
else if (text == "/screen") {
// Запрошен скриншот всей области экрана
if (tmSettings.AllowToGetScreenshot(username)) {
string fileName = getScreenShot();
SendPhoto(message.chat.id, fileName);
File.Delete(fileName);
}
else {
SendMessage(message.chat.id, "У вас нет доступа к данной команде");
}
}
else {
object cmd = tmSettings.GetCommandByName(text);
if (cmd != null) {
// Запрошена команда из списка
if (!messageOrder.ContainsKey(message.message_id)) {
Command cur_command = (Command)cmd;
if (tmSettings.AllowableUserForCommand(username, cur_command)) {
TelegramCommand tCommand = new TelegramCommand();
tCommand.Message = message;
tCommand.Command = cur_command;
tCommand.Parameters = param;
messageOrder.Add(message.message_id, tCommand);
BackgroundWorker executer = new BackgroundWorker();
executer.WorkerSupportsCancellation = true;
executer.DoWork += new DoWorkEventHandler(ExecuteCommand);
executer.RunWorkerAsync(tCommand);
}
else {
SendMessage(message.chat.id, "У вас нет доступа к данной команде");
}
}
}
else {
// unknow command
SendMessage(message.chat.id, "Неизвестная команда");
}
}
}
else {
SendMessage(message.chat.id, "У вас нет доступа к данному боту");
}
}
}
/// <summary>
/// Разбирает полученные updates c сообщениями
/// <PARAM name="updates">List<TelegramUpdate></PARAM>
/// </summary>
/// <returns>int - номер последнего обработанного TelegramUpdate</returns>
private int listener_CheckUpdates(List<TelegramUpdate> updates)
{
int newOffset = tmOffset;
foreach (TelegramUpdate update in updates) {
// Обработка каждого сообщения
listener_CheckMessage(update.message);
newOffset = update.update_id + 1;
}
return newOffset;
}
/// <summary>
/// Запускает обработку полученных updates
/// <PARAM name="answer">List<TelegramAnswer></PARAM>
/// </summary>
/// <returns>int - номер последнего обработанного TelegramUpdate</returns>
private int listener_CheckTelegramAnswer(TelegramAnswer answer)
{
if (answer.ok) {
// Ошибок нет, можно обработать updates
return listener_CheckUpdates(answer.updates);
}
else
{
// API вернул ошибку
Logger.Write(answer.description, true, mutLogger);
return tmOffset;
}
}
/// <summary>
/// Останавливае работу с api.telegram.org
/// </summary>
public void StopWork()
{
listenerTimer.Stop();
listenerTimer.Dispose();
}
}
}