/// <summary> /// Reads response and build UI for user /// consider user language, preferences and context /// </summary> public async Task HandleResponse(BotResponse response, long chatId) { // get user preferences first IUserPreferences userPreferences = response.User.UserPreferences; UserContextState userContext = ContextController.GetUserState(response.User); // set dictionary of Dictionary <string, object> OpArgs = new Dictionary <string, object>(); OpArgs.Add("ChatId", chatId); // chat id is setted here and unchangeble! OpArgs.Add("Content", null); OpArgs.Add("ReplyMarkdown", null); bool IsAddHeader = Configs["ShowDialogPath"].GetValue <bool>(); bool replaceMsg = Configs["ReplaceDialogs"].GetValue <bool>(); int msgId = -1; #region service method(s) async Task <bool> SendBack() { try { Dialog dialog = // owner dialog is like step aback response.Dialog.Owner == null ? Dialogs.RootDialog : response.Dialog.Owner; OpArgs["Content"] = ProcessContent(TranslateContent(dialog.Content, userPreferences), dialog, IsAddHeader); OpArgs["ReplyMarkdown"] = GetMarkup(dialog, response.User); OperationResult rMsg = await Operations["SendMessageOperation"].Execute(new OperationArgs(response.User, OpArgs)); msgId = (int)rMsg.Result; // set user context and return to upper dialog ContextController.SetState(new UserContextState(response.User, dialog, msgId)); return(true); } catch (Exception exp) { BotManager.Core?.LogController? .LogError(new DebugMessage("Can't show user error notification!", "Uidispatcher.SendBack()", exp)); return(false); } } async Task <bool> SendToDialog() { try { // work with serial dialogs here (?) Dialog dialog = response.Dialog; OpArgs["Content"] = ProcessContent(TranslateContent(dialog.Content, userPreferences), dialog, IsAddHeader); OpArgs["ReplyMarkdown"] = GetMarkup(dialog, response.User); OpArgs["MsgId"] = msgId = userContext.LastMsgId; if (replaceMsg && msgId > 0) { OperationResult rMsg = await Operations["ReplaceMessageOperation"].Execute(new OperationArgs(response.User, OpArgs)); int? msgVal = rMsg.Result as int?; msgId = msgVal == null ? msgId : msgVal.Value; } else { OperationResult rMsg = await Operations["SendMessageOperation"].Execute(new OperationArgs(response.User, OpArgs)); int? msgVal = rMsg.Result as int?; msgId = msgVal == null ? msgId : msgVal.Value; } // set user context and return to upper dialog ContextController.SetState(new UserContextState(response.User, dialog, msgId)); return(true); } catch (Exception exp) { BotManager.Core?.LogController? .LogError(new DebugMessage("Can't show user error notification!", "Uidispatcher.SendToDialog()", exp)); return(false); } } #endregion // handle exception if (response is BotExceptionResponse) { var rsp = response as BotExceptionResponse; // print warning message to log BotManager.Core?.LogController? .LogWarning(new DebugMessage("Exception occured during dialog execution!", "Uidispatcher.HandleResponse()", rsp.Message)); // print response to user if responce allow it if (rsp.ShowNotification) { string content = TranslateContent((string)response.Data, userPreferences); OpArgs["Content"] = content; OperationArgs opArgs = new OperationArgs(response.User, OpArgs); OperationResult result = await Operations["SendMessageOperation"].Execute(opArgs); if (result.ResultType == OperationResult.OperationResultType.Failed || result.ResultType == OperationResult.OperationResultType.Unknown) { BotManager.Core?.LogController? .LogError(new DebugMessage("Can't show user error notification!", "Uidispatcher.HandleResponse()", result.ExceptionMessage)); // nullify user context - we definitely have something uncommon ContextController.ClearState(response.User); return; // and do nothing. no menu and etc... } } await Task.Delay(BotManager.Core.Configs.BasicDelay); // exception message sended succesfully, so display new one, above error await SendBack(); // state changed, dialog showed, nothing more to do... return; } // dialog response recieved - redirect user to this dialog // and, of course change state if (response.Type == BotResponse.ResponseType.Dialog) { await SendToDialog(); } // response is data so parse it and act accordingly else if (response.Type == BotResponse.ResponseType.Data) { // first transform raw data CallbackData callback = CallbackDataParser.ParseData((string)response.Data); // next - do something with this callback // supose make some separete method - too much logic here... } // response is plain text, so display it and keep state unchanged else if (response.Type == BotResponse.ResponseType.Text) { // check dialog - it should stay same cos it's just a text wich placed above Dialog dialog = userContext.CurrentDialog; OpArgs["Content"] = ProcessContent(TranslateContent((string)response.Data, userPreferences), dialog, false); OpArgs["ReplyMarkdown"] = null; OperationResult rMsg = await Operations["SendMessageOperation"].Execute(new OperationArgs(response.User, OpArgs)); msgId = (int)rMsg.Result; // set user context and return to upper dialog ContextController.SetState(new UserContextState(response.User, dialog, msgId)); } else { // response is null or exception type, witch is wrong // make log entry, send user back and change user state BotManager.Core?.LogController?.LogWarning(new DebugMessage("Response can't be processed!", "UiDispatcher.HandleResponse()", $"UserId: {response.User.UserId}")); await SendBack(); // state changed, dialog showed, nothing more to do... return; } }
protected async void Api_OnCallbackQuery(object sender, Telegram.Bot.Args.CallbackQueryEventArgs e) { // first awaits some time to prevent flooding await Task.Delay(Configs.BasicDelay); CallbackData data = DataParser.ParseData(e.CallbackQuery.Data); IUser user = await UsersController.GetOrCreateUser(e.CallbackQuery.From); BotResponse response = new BotResponse(user); // lock the context, so user cant spam thousand messages // while some operations execute! if (ContextController.GetUserState(user)?.OccupieContext() == false) { return; } var userState = ContextController.GetUserState(user); // juat show dialog, stored in button if (data.T == CallbackData.ContentTypeEnum.Dialog) { response = Dialogs.GetDialog(data.Id, user); } // service button pressed, there can be stored and operation // and data, retranslated to user input (used in serial dialogs) else if (data.T == CallbackData.ContentTypeEnum.Button) { Dialog currentDia = ContextController.GetUserState(user).CurrentDialog; var Operation = BotManager.Core.Operations.GetOperation(data.D); BotRequest request = new BotRequest(); // data is operation - execute it if (Operation != null) { var result = await BotManager.Core.Operations[data.D] .Execute(new Operations.OperationArgs(user, new Dictionary <string, object> { { "CurrentDialog", currentDia } })); if (result.ResultType == Operations.OperationResult.OperationResultType.Failed) { // put error to console BotManager.Core?.LogController? .LogError(new DebugMessage("Operation '{}' fails!", "BotApi.Api_OnCallbackQuery()", result.ExceptionMessage)); } response = result.Result as BotResponse; // if data as string == null, so it be null // request here used to support input awaiting request = new BotRequest(response?.Data as string, BotRequest.RequestType.CallbackData, user); // something wrong, go to root and notify user if (response == null) { // notify someone (DoTo) response = new BotResponse(null, BotResponse.ResponseType.Dialog, user, currentDia); } } // data, just data // expected using in serial dialog! else { request = new BotRequest(data.D, BotRequest.RequestType.CallbackData, user); } // if input awited - put response to user cache // and show next dialog (provided by AddRequest method) if (userState.CurrentState == UserContextState.ContextState.AwaitInput) { response = ContextController.AddRequest(user, request); } } await UiController.HandleResponse(response, e.CallbackQuery.Message.Chat.Id); ContextController.GetUserState(user).RealiseContex(); }