From 2e03e8b74740132fc698001fc909a04f63df590d Mon Sep 17 00:00:00 2001 From: Denis Denisov Date: Thu, 28 Apr 2022 09:10:41 +0300 Subject: [PATCH] Added user state (nested menus) and sub-menu to control different potential problems and system status --- BotService.cs | 151 +++++------------------------------- IUserState.cs | 13 ++++ MainMenuUserState.cs | 161 +++++++++++++++++++++++++++++++++++++++ OpenHab/Item.cs | 13 ++++ OpenHab/ItemState.cs | 12 +++ OpenHab/OpenHabClient.cs | 83 ++++++++++++++++++++ StatusMenuUserState.cs | 74 ++++++++++++++++++ 7 files changed, 375 insertions(+), 132 deletions(-) create mode 100644 IUserState.cs create mode 100644 MainMenuUserState.cs create mode 100644 OpenHab/Item.cs create mode 100644 OpenHab/ItemState.cs create mode 100644 OpenHab/OpenHabClient.cs create mode 100644 StatusMenuUserState.cs diff --git a/BotService.cs b/BotService.cs index 548a1c0..2f3d6d1 100644 --- a/BotService.cs +++ b/BotService.cs @@ -15,11 +15,8 @@ namespace ZaoBot { private JsonDocument config; static ITelegramBotClient botClient; - HttpClient http = new HttpClient(); - - const string CMD_WEATHER = "Погода"; - const string CMD_LEFT_GATE = "Открой левые ворота"; - const string CMD_RIGHT_GATE = "Открой правые ворота"; + Dictionary userStates = new Dictionary(); + private OpenHab.OpenHabClient openhab; bool shouldRestart = false; @@ -33,6 +30,7 @@ namespace ZaoBot var token = config.RootElement.GetProperty("token").GetString(); shouldRestart = false; botClient = new TelegramBotClient(token); + openhab = new OpenHab.OpenHabClient(config.RootElement.GetProperty("openhab_url").GetString()); var me = botClient.GetMeAsync().Result; Console.WriteLine($"Hello, World! I am user {me.Id} and my name is {me.FirstName}."); botClient.OnMessage += OnBotMessage; @@ -49,53 +47,27 @@ namespace ZaoBot { if (e.Message.Text != null) { - if (!VerifyUser(e.Message.Chat.Id, e.Message.Chat.Username)) + var id = e.Message.Chat.Id; + IUserState state = null; + if (userStates.ContainsKey(id)) + { + state = userStates[id]; + } + if (state == null) + { + state = new MainMenuUserState(botClient, config, openhab); + } + state = await state.ProcessMessage(state, e); + if (state == null) { - Console.WriteLine($"Received message {e.Message.Text} from invalid user {e.Message.Chat.Username}"); await botClient.SendTextMessageAsync( chatId: e.Message.Chat, - text: "Ваш ID " + e.Message.Chat.Id + " сообщите его администратору" + text: "Я запутался, давайте сначала" ); - return; + state = new MainMenuUserState(botClient, config, openhab); + state = await state.ProcessMessage(state, e); } - Console.WriteLine($"Received a text message in chat {e.Message.Chat.Id}."); - - var response = ""; - if (e.Message.Text == CMD_LEFT_GATE) - { - response = await OpenLeftGate(); - } - else if (e.Message.Text == CMD_RIGHT_GATE) - { - response = await OpenRightGate(); - } - else if (e.Message.Text == CMD_WEATHER) - { - response = await ShowWeather(); - } - else if (e.Message.Text == "") - { - response = "Превед!"; - } - else response = "Я не понял :-("; - - if (e.Message.Text != "") response += "\nЧто дальше?"; - else response = "\nЧего изволите?"; - - var replyKeyboardMarkup = new ReplyKeyboardMarkup( - new KeyboardButton[][] - { - new KeyboardButton[] { CMD_WEATHER }, - new KeyboardButton[] { CMD_LEFT_GATE, CMD_RIGHT_GATE }, - }, - resizeKeyboard: true - ); - - await botClient.SendTextMessageAsync( - chatId: e.Message.Chat, - text: response, - replyMarkup: replyKeyboardMarkup - ); + userStates[id] = state; } } catch (Exception ex) @@ -106,90 +78,5 @@ namespace ZaoBot shouldRestart = true; } } - - private async Task ShowWeather() - { - try - { - string response = ""; - foreach (var element in config.RootElement.GetProperty("items_weather").EnumerateObject()) - { - var url = config.RootElement.GetProperty("openhab_url").GetString(); - url += element.Name; - var result = await http.GetAsync(url); - var content = await result.Content.ReadAsStringAsync(); - response += element.Value + ": " + ParseContent(content) + "\n"; - } - return response; - } - catch (Exception e) - { - return "Fail: " + e.Message; - } - } - - private string ParseContent(string content) - { - var doc = JsonDocument.Parse(content); - float value; - if (!float.TryParse(doc.RootElement.GetProperty("state").GetString(), out value)) - { - return "null"; - } - try - { - var stateDescription = doc.RootElement.GetProperty("stateDescription"); - var pattern = stateDescription.GetProperty("pattern"); - return printf.Printf.sprintf(pattern.GetString(), value); - } - catch (KeyNotFoundException) - { - return value.ToString(); - } - } - - private async Task OpenRightGate() - { - var url = config.RootElement.GetProperty("openhab_url").GetString(); - url += config.RootElement.GetProperty("item_right_gate").GetString(); - return await Post(url, "ON"); - } - - private async Task Post(string url, string v) - { - var content = new ByteArrayContent(Encoding.UTF8.GetBytes(v)); - content.Headers.Add("Content-Type", "text/plain"); - var result = await http.PostAsync(url, content); - if (result.IsSuccessStatusCode) return "OK"; - else return "Fail. " + result.ReasonPhrase; - - } - - private async Task OpenLeftGate() - { - var url = config.RootElement.GetProperty("openhab_url").GetString(); - url += config.RootElement.GetProperty("item_left_gate").GetString(); - return await Post(url, "ON"); - } - - private bool VerifyUser(long id, string username) - { - Console.WriteLine("Verify user " + username + " id " + id); - foreach (var element in config.RootElement.GetProperty("valid_users").EnumerateArray()) - { - if (username == element.GetString()) - { - return true; - } - } - foreach (var element in config.RootElement.GetProperty("valid_user_ids").EnumerateArray()) - { - if (id.ToString() == element.GetString()) - { - return true; - } - } - return false; - } } } diff --git a/IUserState.cs b/IUserState.cs new file mode 100644 index 0000000..07143e3 --- /dev/null +++ b/IUserState.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Telegram.Bot.Args; + +namespace ZaoBot +{ + interface IUserState + { + Task ProcessMessage(IUserState previous, MessageEventArgs e); + } +} diff --git a/MainMenuUserState.cs b/MainMenuUserState.cs new file mode 100644 index 0000000..9e69209 --- /dev/null +++ b/MainMenuUserState.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Telegram.Bot; +using Telegram.Bot.Args; +using Telegram.Bot.Types.ReplyMarkups; +using ZaoBot.OpenHab; + +namespace ZaoBot +{ + class MainMenuUserState : IUserState + { + const string CMD_WEATHER = "Погода"; + const string CMD_STATUS = "Статус"; + const string CMD_LEFT_GATE = "Открой левые ворота"; + const string CMD_RIGHT_GATE = "Открой правые ворота"; + + ITelegramBotClient botClient; + private JsonDocument config; + HttpClient http = new HttpClient(); + OpenHabClient openhab; + + public MainMenuUserState(ITelegramBotClient botClient, JsonDocument config, OpenHabClient openhab) + { + this.botClient = botClient; + this.config = config; + this.openhab = openhab; + } + + public async Task ProcessMessage(IUserState previous, MessageEventArgs e) + { + var response = ""; + if (e != null) + { + if (!VerifyUser(e.Message.Chat.Id, e.Message.Chat.Username)) + { + Console.WriteLine($"Received message {e.Message.Text} from invalid user {e.Message.Chat.Username}"); + await botClient.SendTextMessageAsync( + chatId: e.Message.Chat, + text: "Ваш ID " + e.Message.Chat.Id + " сообщите его администратору" + ); + return this; + } + Console.WriteLine($"Received a text message in chat {e.Message.Chat.Id}."); + + if (e.Message.Text == CMD_LEFT_GATE) + { + response = await OpenLeftGate(); + } + else if (e.Message.Text == CMD_RIGHT_GATE) + { + response = await OpenRightGate(); + } + else if (e.Message.Text == CMD_WEATHER) + { + response = await ShowWeather(); + } + else if (e.Message.Text == CMD_STATUS) + { + var newState = new StatusMenuUserState(botClient, config, openhab); + await newState.ProcessMessage(this, e); + return newState; + } + else if (e.Message.Text == "") + { + response = "Превед!"; + } + else if (previous != this) response = "Главное меню"; + else response = "Я не понял :-("; + + if (e.Message.Text != "") response += "\nЧто дальше?"; + else response = "\nЧего изволите?"; + } + + var replyKeyboardMarkup = new ReplyKeyboardMarkup( + new KeyboardButton[][] + { + new KeyboardButton[] { CMD_WEATHER }, + new KeyboardButton[] { CMD_LEFT_GATE, CMD_RIGHT_GATE }, + new KeyboardButton[] { CMD_STATUS }, + }, + resizeKeyboard: true + ); + + await botClient.SendTextMessageAsync( + chatId: e.Message.Chat, + text: response, + replyMarkup: replyKeyboardMarkup + ); + return this; + } + + private async Task ShowWeather() + { + try + { + string response = ""; + foreach (var element in config.RootElement.GetProperty("items_weather").EnumerateObject()) + { + var result = await openhab.GetItemState(element.Name); + response += element.Value + ": " + ParseState(result) + "\n"; + } + return response; + } + catch (Exception e) + { + return "Fail: " + e.Message; + } + } + + private string ParseState(ItemState state) + { + float value; + if (!float.TryParse(state.state, out value)) + { + return "null"; + } + try + { + return printf.Printf.sprintf(state.pattern, value); + } + catch (KeyNotFoundException) + { + return value.ToString(); + } + } + + private async Task OpenRightGate() + { + return await openhab.PostItemState(config.RootElement.GetProperty("item_right_gate").GetString(), "ON"); + } + + private async Task OpenLeftGate() + { + return await openhab.PostItemState(config.RootElement.GetProperty("item_left_gate").GetString(), "ON"); + } + + private bool VerifyUser(long id, string username) + { + Console.WriteLine("Verify user " + username + " id " + id); + foreach (var element in config.RootElement.GetProperty("valid_users").EnumerateArray()) + { + if (username == element.GetString()) + { + return true; + } + } + foreach (var element in config.RootElement.GetProperty("valid_user_ids").EnumerateArray()) + { + if (id.ToString() == element.GetString()) + { + return true; + } + } + return false; + } + } +} diff --git a/OpenHab/Item.cs b/OpenHab/Item.cs new file mode 100644 index 0000000..3c58399 --- /dev/null +++ b/OpenHab/Item.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ZaoBot.OpenHab +{ + class Item + { + public string name; + public string type; + public string label; + } +} diff --git a/OpenHab/ItemState.cs b/OpenHab/ItemState.cs new file mode 100644 index 0000000..6240690 --- /dev/null +++ b/OpenHab/ItemState.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ZaoBot.OpenHab +{ + class ItemState + { + public string state; + public string pattern; + } +} diff --git a/OpenHab/OpenHabClient.cs b/OpenHab/OpenHabClient.cs new file mode 100644 index 0000000..e28494b --- /dev/null +++ b/OpenHab/OpenHabClient.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; + +namespace ZaoBot.OpenHab +{ + class OpenHabClient + { + private string url; + HttpClient http = new HttpClient(); + + public OpenHabClient(string url) + { + this.url = url; + } + + public async Task GetItemState(string item) + { + var result = await http.GetAsync(url + "/rest/items/" + item); + var content = await result.Content.ReadAsStringAsync(); + return ParseState(content); + } + + private ItemState ParseState(string content) + { + var doc = JsonDocument.Parse(content); + + var pattern = ""; + JsonElement stateDescription; + if (doc.RootElement.TryGetProperty("stateDescription", out stateDescription)) + { + pattern = stateDescription.GetProperty("pattern").GetString(); + } + var result = new ItemState() + { + state = doc.RootElement.GetProperty("state").GetString(), + pattern = pattern, + }; + return result; + } + + public async Task PostItemState(string item, string state) + { + return await Post(url + "/rest/items/" + item, state); + } + + private async Task Post(string url, string v) + { + var content = new ByteArrayContent(Encoding.UTF8.GetBytes(v)); + content.Headers.Add("Content-Type", "text/plain"); + var result = await http.PostAsync(url, content); + if (result.IsSuccessStatusCode) return "OK"; + else return "Fail. " + result.ReasonPhrase; + } + + public async Task> GetItems() + { + var result = await http.GetAsync(url + "/rest/items/?fields=name,type,label"); + var content = await result.Content.ReadAsStringAsync(); + var doc = JsonDocument.Parse(content); + var items = new List(); + foreach (var element in doc.RootElement.EnumerateArray()) + { + var label = ""; + JsonElement l; + if (element.TryGetProperty("label", out l)) + { + label = l.GetString(); + } + items.Add(new Item() + { + name = element.GetProperty("name").GetString(), + type = element.GetProperty("type").GetString(), + label = label, + }); + } + return items; + } + } +} diff --git a/StatusMenuUserState.cs b/StatusMenuUserState.cs new file mode 100644 index 0000000..6b4dd3a --- /dev/null +++ b/StatusMenuUserState.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Telegram.Bot; +using Telegram.Bot.Args; +using Telegram.Bot.Types.ReplyMarkups; +using ZaoBot.OpenHab; + +namespace ZaoBot +{ + class StatusMenuUserState : IUserState + { + public const string CMD_BACK = "Назад"; + + ITelegramBotClient botClient; + private JsonDocument config; + HttpClient http = new HttpClient(); + OpenHabClient openhab; + + public StatusMenuUserState(ITelegramBotClient botClient, JsonDocument config, OpenHabClient openhab) + { + this.botClient = botClient; + this.config = config; + this.openhab = openhab; + } + + public async Task ProcessMessage(IUserState previous, MessageEventArgs e) + { + var response = ""; + if (previous != this) + { + response = "Возможные проблемы:\n"; + var items = await openhab.GetItems(); + foreach (var item in items) + { + if (!item.name.StartsWith("Problem_")) continue; + response += item.label + ": "; + var state = await openhab.GetItemState(item.name); + if (state.state == "OFF") response += "OK"; + else if (state.state == "ON") response += "PROBLEM!"; + else response += "UNKNOWN"; + response += "\n"; + } + } + else if (e.Message.Text == CMD_BACK) + { + var newState = new MainMenuUserState(botClient, config, openhab); + await newState.ProcessMessage(this, e); + return newState; + } + else + { + response = "Я не понял"; + } + var replyKeyboardMarkup = new ReplyKeyboardMarkup( + new KeyboardButton[][] + { + new KeyboardButton[] { CMD_BACK }, + }, + resizeKeyboard: true + ); + + await botClient.SendTextMessageAsync( + chatId: e.Message.Chat, + text: response, + replyMarkup: replyKeyboardMarkup + ); + return this; + } + } +}