using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using Telegram.Bot; using Telegram.Bot.Exceptions; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; using TelegramBotBase.Args; using TelegramBotBase.Attributes; using TelegramBotBase.Base; using TelegramBotBase.Enums; using TelegramBotBase.MessageLoops; using TelegramBotBase.Form; using TelegramBotBase.Interfaces; using TelegramBotBase.Sessions; namespace TelegramBotBase { /// /// Bot base class for full Device/Context and Messagehandling /// /// public sealed class BotBase { public MessageClient Client { get; set; } /// /// Your TelegramBot APIKey /// public String APIKey { get; set; } = ""; /// /// List of all running/active sessions /// public SessionManager Sessions { get; set; } /// /// Contains System commands which will be available at everytime and didnt get passed to forms, i.e. /start /// public Dictionary> BotCommandScopes { get; set; } = new Dictionary>(); #region "Events" private EventHandlerList __events = new EventHandlerList(); private static object __evSessionBegins = new object(); private static object __evMessage = new object(); private static object __evSystemCall = new object(); public delegate Task BotCommandEventHandler(object sender, BotCommandEventArgs e); private static object __evException = new object(); private static object __evUnhandledCall = new object(); #endregion /// /// Enable the SessionState (you need to implement on call forms the IStateForm interface) /// public IStateMachine StateMachine { get; set; } /// /// Offers functionality to manage the creation process of the start form. /// public IStartFormFactory StartFormFactory { get; set; } /// /// Contains the message loop factory, which cares about "message-management." /// public IMessageLoopFactory MessageLoopFactory { get; set; } /// /// All internal used settings. /// public Dictionary SystemSettings { get; private set; } internal BotBase() { SystemSettings = new Dictionary(); SetSetting(eSettings.MaxNumberOfRetries, 5); SetSetting(eSettings.NavigationMaximum, 10); SetSetting(eSettings.LogAllMessages, false); SetSetting(eSettings.SkipAllMessages, false); SetSetting(eSettings.SaveSessionsOnConsoleExit, false); BotCommandScopes = new Dictionary>(); Sessions = new SessionManager(this); } /// /// Start your Bot /// public async Task Start() { Client.MessageLoop += Client_MessageLoop; if (StateMachine != null) await Sessions.LoadSessionStates(StateMachine); //Enable auto session saving if (GetSetting(eSettings.SaveSessionsOnConsoleExit, false)) TelegramBotBase.Tools.Console.SetHandler(() => { Task.Run(Sessions.SaveSessionStates); }); DeviceSession.MaxNumberOfRetries = GetSetting(eSettings.MaxNumberOfRetries, 5); Client.StartReceiving(); } private async Task Client_MessageLoop(object sender, UpdateResult e) { DeviceSession ds = this.Sessions.GetSession(e.DeviceId); if (ds == null) { ds = Sessions.StartSession(e.DeviceId).GetAwaiter().GetResult(); e.Device = ds; ds.LastMessage = e.RawData.Message; OnSessionBegins(new SessionBeginEventArgs(e.DeviceId, ds)); } var mr = new MessageResult(e.RawData); int i = 0; //Should formulars get navigated (allow maximum of 10, to dont get loops) do { i++; //Reset navigation ds.FormSwitched = false; await MessageLoopFactory.MessageLoop(this, ds, e, mr); mr.IsFirstHandler = false; } while (ds.FormSwitched && i < GetSetting(eSettings.NavigationMaximum, 10)); } /// /// Stop your Bot /// public async Task Stop() { if (Client == null) return; Client.MessageLoop -= Client_MessageLoop; Client.StopReceiving(); await Sessions.SaveSessionStates(); } /// /// Send a message to all active Sessions. /// /// /// public async Task SentToAll(String message) { if (Client == null) return; foreach (var s in Sessions.SessionList) { await Client.TelegramClient.SendTextMessageAsync(s.Key, message); } } /// /// This will invoke the full message loop for the device even when no "userevent" like message or action has been raised. /// /// Contains the device/chat id of the device to update. public async Task InvokeMessageLoop(long DeviceId) { var mr = new MessageResult(); mr.UpdateData = new Update() { Message = new Message() }; await InvokeMessageLoop(DeviceId, mr); } /// /// This will invoke the full message loop for the device even when no "userevent" like message or action has been raised. /// /// Contains the device/chat id of the device to update. /// public async Task InvokeMessageLoop(long DeviceId, MessageResult e) { try { DeviceSession ds = this.Sessions.GetSession(DeviceId); e.Device = ds; await MessageLoopFactory.MessageLoop(this, ds, new UpdateResult(e.UpdateData, ds), e); //await Client_Loop(this, e); } catch (Exception ex) { DeviceSession ds = this.Sessions.GetSession(DeviceId); OnException(new SystemExceptionEventArgs(e.Message.Text, DeviceId, ds, ex)); } } /// /// Will get invoke on an unhandled call. /// /// /// public void MessageLoopFactory_UnhandledCall(object sender, UnhandledCallEventArgs e) { OnUnhandledCall(e); } /// /// This method will update all local created bot commands to the botfather. /// public async Task UploadBotCommands() { foreach (var bs in BotCommandScopes) { if (bs.Value != null) { await Client.SetBotCommands(bs.Value, bs.Key); } else { await Client.DeleteBotCommands(bs.Key); } } } /// /// Searching if parameter is a known command in all configured BotCommandScopes. /// /// /// public bool IsKnownBotCommand(String command) { foreach (var scope in BotCommandScopes) { if (scope.Value.Any(a => "/" + a.Command == command)) return true; } return false; } /// /// Could set a variety of settings to improve the bot handling. /// /// /// public void SetSetting(eSettings set, uint Value) { SystemSettings[set] = Value; } /// /// Could set a variety of settings to improve the bot handling. /// /// /// public void SetSetting(eSettings set, bool Value) { SystemSettings[set] = (Value ? 1u : 0u); } /// /// Could get the current value of a setting /// /// /// /// public uint GetSetting(eSettings set, uint defaultValue) { if (!SystemSettings.ContainsKey(set)) return defaultValue; return SystemSettings[set]; } /// /// Could get the current value of a setting /// /// /// /// public bool GetSetting(eSettings set, bool defaultValue) { if (!SystemSettings.ContainsKey(set)) return defaultValue; return SystemSettings[set] == 0u ? false : true; } #region "Events" /// /// Will be called if a session/context gets started /// public event EventHandler SessionBegins { add { __events.AddHandler(__evSessionBegins, value); } remove { __events.RemoveHandler(__evSessionBegins, value); } } public void OnSessionBegins(SessionBeginEventArgs e) { (__events[__evSessionBegins] as EventHandler)?.Invoke(this, e); } /// /// Will be called on incomming message /// public event EventHandler Message { add { __events.AddHandler(__evMessage, value); } remove { __events.RemoveHandler(__evMessage, value); } } public void OnMessage(MessageIncomeEventArgs e) { (__events[__evMessage] as EventHandler)?.Invoke(this, e); } /// /// Will be called if a bot command gets raised /// public event BotCommandEventHandler BotCommand; public async Task OnBotCommand(BotCommandEventArgs e) { if (BotCommand != null) await BotCommand(this, e); } /// /// Will be called on an inner exception /// public event EventHandler Exception { add { __events.AddHandler(__evException, value); } remove { __events.RemoveHandler(__evException, value); } } public void OnException(SystemExceptionEventArgs e) { (__events[__evException] as EventHandler)?.Invoke(this, e); } /// /// Will be called if no form handeled this call /// public event EventHandler UnhandledCall { add { __events.AddHandler(__evUnhandledCall, value); } remove { __events.RemoveHandler(__evUnhandledCall, value); } } public void OnUnhandledCall(UnhandledCallEventArgs e) { (__events[__evUnhandledCall] as EventHandler)?.Invoke(this, e); } #endregion } }