using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using TelegramBotBase.Args;
using TelegramBotBase.Attributes;
using TelegramBotBase.Base;
using TelegramBotBase.Factories;
using TelegramBotBase.Form;
using TelegramBotBase.Interfaces;
using TelegramBotBase.Sessions;
using TelegramBotBase.Tools;
namespace TelegramBotBase;
///
/// Base class for managing all active sessions
///
public class SessionManager
{
public SessionManager(BotBase botBase)
{
BotBase = botBase;
SessionList = new Dictionary();
}
///
/// The Basic message client.
///
public MessageClient Client => BotBase.Client;
///
/// A list of all active sessions.
///
public Dictionary SessionList { get; set; }
///
/// Reference to the Main BotBase instance for later use.
///
public BotBase BotBase { get; }
///
/// Get device session from Device/ChatId
///
///
///
public DeviceSession GetSession(long deviceId)
{
var ds = SessionList.FirstOrDefault(a => a.Key == deviceId).Value ?? null;
return ds;
}
///
/// Start a new session
///
///
///
///
public async Task StartSession(long deviceId)
{
var start = BotBase.StartFormFactory.CreateForm();
start.Client = Client;
var ds = new DeviceSession(deviceId, start);
start.Device = ds;
await start.OnInit(new InitEventArgs());
await start.OnOpened(EventArgs.Empty);
SessionList[deviceId] = ds;
return ds;
}
///
/// End session
///
///
public void EndSession(long deviceId)
{
var d = SessionList[deviceId];
if (d != null)
{
SessionList.Remove(deviceId);
}
}
///
/// Returns all active User Sessions.
///
///
public List GetUserSessions()
{
return SessionList.Where(a => a.Key > 0).Select(a => a.Value).ToList();
}
///
/// Returns all active Group Sessions.
///
///
public List GetGroupSessions()
{
return SessionList.Where(a => a.Key < 0).Select(a => a.Value).ToList();
}
///
/// Loads the previously saved states from the machine.
///
public async Task LoadSessionStates()
{
if (BotBase.StateMachine == null)
{
return;
}
await LoadSessionStates(BotBase.StateMachine);
}
///
/// Loads the previously saved states from the machine.
///
public async Task LoadSessionStates(IStateMachine statemachine)
{
if (statemachine == null)
{
throw new ArgumentNullException(nameof(statemachine),
"No StateMachine defined. Please set one to property BotBase.StateMachine");
}
var container = statemachine.LoadFormStates();
foreach (var s in container.States)
{
var t = Type.GetType(s.QualifiedName);
if (t == null || !t.IsSubclassOf(typeof(FormBase)))
{
continue;
}
//Key already existing
if (SessionList.ContainsKey(s.DeviceId))
{
continue;
}
FormBase form;
if (BotBase.StartFormFactory is ServiceProviderStartFormFactory diFactory)
{
form = diFactory.CreateForm(t);
}
//No default constructor, fallback
else if (t.GetConstructor(new Type[] { })?.Invoke(new object[] { }) is FormBase f)
{
form = f;
}
else
{
if (!statemachine.FallbackStateForm.IsSubclassOf(typeof(FormBase)))
{
continue;
}
form =
statemachine.FallbackStateForm.GetConstructor(new Type[] { })
?.Invoke(new object[] { }) as FormBase;
//Fallback failed, due missing default constructor
if (form == null)
{
continue;
}
}
if (s.Values != null && s.Values.Count > 0)
{
var properties = s.Values.Where(a => a.Key.StartsWith("$"));
var fields = form.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(a => a.GetCustomAttributes(typeof(SaveState), true).Length != 0).ToList();
foreach (var p in properties)
{
var f = fields.FirstOrDefault(a => a.Name == p.Key.Substring(1));
if (f == null)
{
continue;
}
try
{
if (f.PropertyType.IsEnum)
{
var ent = Enum.Parse(f.PropertyType, p.Value.ToString());
f.SetValue(form, ent);
continue;
}
f.SetValue(form, p.Value);
}
catch (ArgumentException)
{
Conversion.CustomConversionChecks(form, p, f);
}
catch
{
}
}
}
form.Client = Client;
var device = new DeviceSession(s.DeviceId, form)
{
ChatTitle = s.ChatTitle
};
SessionList.Add(s.DeviceId, device);
//Is Subclass of IStateForm
if (form is IStateForm iform)
{
var ls = new LoadStateEventArgs
{
Values = s.Values
};
await iform.LoadState(ls);
}
try
{
await form.OnInit(new InitEventArgs());
await form.OnOpened(EventArgs.Empty);
}
catch
{
//Skip on exception
SessionList.Remove(s.DeviceId);
}
}
}
///
/// Saves all open states into the machine.
///
public async Task SaveSessionStates(IStateMachine statemachine)
{
if (statemachine == null)
{
throw new ArgumentNullException(nameof(statemachine),
"No StateMachine defined. Please set one to property BotBase.StateMachine");
}
var states = new List();
foreach (var s in SessionList)
{
if (s.Value == null)
{
continue;
}
var form = s.Value.ActiveForm;
try
{
var se = new StateEntry
{
DeviceId = s.Key,
ChatTitle = s.Value.GetChatTitle(),
FormUri = form.GetType().FullName,
QualifiedName = form.GetType().AssemblyQualifiedName
};
//Skip classes where IgnoreState attribute is existing
if (form.GetType().GetCustomAttributes(typeof(IgnoreState), true).Length != 0)
{
//Skip this form, when there is no fallback state form
if (statemachine.FallbackStateForm == null)
{
continue;
}
//Replace form by default State one.
se.FormUri = statemachine.FallbackStateForm.FullName;
se.QualifiedName = statemachine.FallbackStateForm.AssemblyQualifiedName;
}
//Is Subclass of IStateForm
if (form is IStateForm iform)
{
//Loading Session states
var ssea = new SaveStateEventArgs();
await iform.SaveState(ssea);
se.Values = ssea.Values;
}
else
{
//Search for public properties with SaveState attribute
var fields = form.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(a => a.GetCustomAttributes(typeof(SaveState), true).Length != 0).ToList();
foreach (var f in fields)
{
var val = f.GetValue(form);
se.Values.Add("$" + f.Name, val);
}
}
states.Add(se);
}
catch
{
//Continue on error (skip this form)
}
}
var sc = new StateContainer
{
States = states
};
statemachine.SaveFormStates(new SaveStatesEventArgs(sc));
}
///
/// Saves all open states into the machine.
///
public async Task SaveSessionStates()
{
if (BotBase.StateMachine == null)
{
return;
}
await SaveSessionStates(BotBase.StateMachine);
}
}