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 class BotBase { public MessageClient Client { get; set; } /// /// Your TelegramBot APIKey /// public String APIKey { get; set; } = ""; /// /// List of all running/active sessions /// public SessionBase 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; } public BotBase() { this.SystemSettings = new Dictionary(); SetSetting(eSettings.MaxNumberOfRetries, 5); SetSetting(eSettings.NavigationMaximum, 10); SetSetting(eSettings.LogAllMessages, false); SetSetting(eSettings.SkipAllMessages, false); SetSetting(eSettings.SaveSessionsOnConsoleExit, false); this.BotCommandScopes = new Dictionary>(); this.Sessions = new SessionBase(); this.Sessions.BotBase = this; } /// /// Start your Bot /// public void Start() { if (this.Client == null) return; this.Client.MessageLoop += Client_MessageLoop; if (this.StateMachine != null) { this.Sessions.LoadSessionStates(this.StateMachine); } //Enable auto session saving if (this.GetSetting(eSettings.SaveSessionsOnConsoleExit, false)) { TelegramBotBase.Tools.Console.SetHandler(() => { this.Sessions.SaveSessionStates(); }); } DeviceSession.MaxNumberOfRetries = this.GetSetting(eSettings.MaxNumberOfRetries, 5); this.Client.StartReceiving(); } private async Task Client_MessageLoop(object sender, UpdateResult e) { DeviceSession ds = this.Sessions.GetSession(e.DeviceId); if (ds == null) { ds = this.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 < this.GetSetting(eSettings.NavigationMaximum, 10)); } /// /// Stop your Bot /// public void Stop() { if (this.Client == null) return; this.Client.MessageLoop -= Client_MessageLoop; this.Client.StopReceiving(); this.Sessions.SaveSessionStates(); } /// /// Send a message to all active Sessions. /// /// /// public async Task SentToAll(String message) { if (this.Client == null) return; foreach (var s in this.Sessions.SessionList) { await this.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(); 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 this.BotCommandScopes) { if(bs.Value !=null) { await this.Client.SetBotCommands(bs.Value, bs.Key); } else { await this.Client.DeleteBotCommands(bs.Key); } } } /// /// Searching if parameter is a known command in all configured BotCommandScopes. /// /// /// public bool IsKnownBotCommand(String command) { foreach (var scope in this.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) { this.SystemSettings[set] = Value; } /// /// Could set a variety of settings to improve the bot handling. /// /// /// public void SetSetting(eSettings set, bool Value) { this.SystemSettings[set] = (Value ? 1u : 0u); } /// /// Could get the current value of a setting /// /// /// /// public uint GetSetting(eSettings set, uint defaultValue) { if (!this.SystemSettings.ContainsKey(set)) return defaultValue; return this.SystemSettings[set]; } /// /// Could get the current value of a setting /// /// /// /// public bool GetSetting(eSettings set, bool defaultValue) { if (!this.SystemSettings.ContainsKey(set)) return defaultValue; return this.SystemSettings[set] == 0u ? false : true; } #region "Events" /// /// Will be called if a session/context gets started /// public event EventHandler SessionBegins { add { this.__Events.AddHandler(__evSessionBegins, value); } remove { this.__Events.RemoveHandler(__evSessionBegins, value); } } public void OnSessionBegins(SessionBeginEventArgs e) { (this.__Events[__evSessionBegins] as EventHandler)?.Invoke(this, e); } /// /// Will be called on incomming message /// public event EventHandler Message { add { this.__Events.AddHandler(__evMessage, value); } remove { this.__Events.RemoveHandler(__evMessage, value); } } public void OnMessage(MessageIncomeEventArgs e) { (this.__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 (this.BotCommand != null) await BotCommand(this, e); } /// /// Will be called on an inner exception /// public event EventHandler Exception { add { this.__Events.AddHandler(__evException, value); } remove { this.__Events.RemoveHandler(__evException, value); } } public void OnException(SystemExceptionEventArgs e) { (this.__Events[__evException] as EventHandler)?.Invoke(this, e); } /// /// Will be called if no form handeled this call /// public event EventHandler UnhandledCall { add { this.__Events.AddHandler(__evUnhandledCall, value); } remove { this.__Events.RemoveHandler(__evUnhandledCall, value); } } public void OnUnhandledCall(UnhandledCallEventArgs e) { (this.__Events[__evUnhandledCall] as EventHandler)?.Invoke(this, e); } #endregion } }