diff --git a/TelegramBotBase.Extensions.Serializer.Database.MSSQL/BotBaseBuilderExtensions.cs b/TelegramBotBase.Extensions.Serializer.Database.MSSQL/BotBaseBuilderExtensions.cs new file mode 100644 index 0000000..fbf22db --- /dev/null +++ b/TelegramBotBase.Extensions.Serializer.Database.MSSQL/BotBaseBuilderExtensions.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TelegramBotBase.Builder; +using TelegramBotBase.Builder.Interfaces; + +namespace TelegramBotBase.Extensions.Serializer.Database.MSSQL +{ + public static class BotBaseBuilderExtensions + { + + /// + /// Uses an Microsoft SQL Server Database to save and restore sessions. + /// + /// + /// + /// + /// + /// + public static ILanguageSelectionStage UseSQLDatabase(this ISessionSerializationStage builder, String ConnectionString, Type fallbackForm = null, String tablePrefix = "tgb_") + { + var serializer = new MSSQLSerializer(ConnectionString, tablePrefix, fallbackForm); + + builder.UseSerialization(serializer); + + return builder as BotBaseBuilder; + } + + + /// + /// Uses an Microsoft SQL Server Database to save and restore sessions. + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static ILanguageSelectionStage UseSQLDatabase(this ISessionSerializationStage builder, String HostOrIP, String DatabaseName, String UserId, String Password, Type fallbackForm = null, String tablePrefix = "tgb_") + { + var connectionString = $"Server={HostOrIP}; Database={DatabaseName}; User Id={UserId}; Password={Password}; TrustServerCertificate=true;"; + + var serializer = new MSSQLSerializer(connectionString, tablePrefix, fallbackForm); + + builder.UseSerialization(serializer); + + return builder as BotBaseBuilder; + } + + /// + /// Uses an Microsoft SQL Server Database with Windows Authentication to save and restore sessions. + /// + /// + /// + /// + /// + /// + /// + public static ILanguageSelectionStage UseSQLDatabase(this ISessionSerializationStage builder, String HostOrIP, String DatabaseName, bool IntegratedSecurity = true, Type fallbackForm = null, String tablePrefix = "tgb_") + { + if (!IntegratedSecurity) + throw new ArgumentOutOfRangeException(); + + var connectionString = $"Server={HostOrIP}; Database={DatabaseName}; Integrated Security=true; TrustServerCertificate=true;"; + + var serializer = new MSSQLSerializer(connectionString, tablePrefix, fallbackForm); + + builder.UseSerialization(serializer); + + return builder as BotBaseBuilder; + } + } +} diff --git a/TelegramBotBase.Extensions.Serializer.Database.MSSQL/MSSQLSerializer.cs b/TelegramBotBase.Extensions.Serializer.Database.MSSQL/MSSQLSerializer.cs new file mode 100644 index 0000000..5aca6e4 --- /dev/null +++ b/TelegramBotBase.Extensions.Serializer.Database.MSSQL/MSSQLSerializer.cs @@ -0,0 +1,186 @@ +using TelegramBotBase.Interfaces; +using TelegramBotBase.Builder.Interfaces; +using System; +using TelegramBotBase.Base; +using TelegramBotBase.Args; +using TelegramBotBase.Form; +using Microsoft.Data.SqlClient; +using System.Data; + +namespace TelegramBotBase.Extensions.Serializer.Database.MSSQL +{ + public class MSSQLSerializer : IStateMachine + { + public Type FallbackStateForm { get; set; } + public string ConnectionString { get; } + public String TablePrefix { get; set; } + + /// + /// Will initialize the state machine. + /// + /// Path of the file and name where to save the session details. + /// Type of Form which will be saved instead of Form which has attribute declared. Needs to be subclass of . + /// Declares of the file could be overwritten. + public MSSQLSerializer(String ConnectionString, String tablePrefix = "tgb_", Type fallbackStateForm = null) + { + if (ConnectionString is null) + { + throw new ArgumentNullException(nameof(ConnectionString)); + } + + this.ConnectionString = ConnectionString; + + this.TablePrefix = tablePrefix; + + this.FallbackStateForm = fallbackStateForm; + + if (this.FallbackStateForm != null && !this.FallbackStateForm.IsSubclassOf(typeof(FormBase))) + { + throw new ArgumentException("FallbackStateForm is not a subclass of FormBase"); + } + } + + public StateContainer LoadFormStates() + { + var sc = new StateContainer(); + + using (var connection = new SqlConnection(ConnectionString)) + { + connection.Open(); + + var command = connection.CreateCommand(); + command.CommandText = "SELECT deviceId, deviceTitle, FormUri, QualifiedName FROM " + TablePrefix + "devices_sessions"; + + var dataTable = new DataTable(); + using (var dataAdapter = new SqlDataAdapter(command)) + { + dataAdapter.Fill(dataTable); + + foreach (DataRow r in dataTable.Rows) + { + var se = new StateEntry() + { + DeviceId = (long)r["deviceId"], + ChatTitle = r["deviceTitle"].ToString(), + FormUri = r["FormUri"].ToString(), + QualifiedName = r["QualifiedName"].ToString() + }; + + sc.States.Add(se); + + if (se.DeviceId > 0) + { + sc.ChatIds.Add(se.DeviceId); + } + else + { + sc.GroupIds.Add(se.DeviceId); + } + + var data_command = connection.CreateCommand(); + data_command.CommandText = "SELECT [key], value, type FROM " + TablePrefix + "devices_sessions_data WHERE deviceId = @deviceId"; + data_command.Parameters.Add(new SqlParameter("@deviceId", r["deviceId"])); + + var data_table = new DataTable(); + using (var dataAdapter2 = new SqlDataAdapter(data_command)) + { + dataAdapter2.Fill(data_table); + + foreach (DataRow r2 in data_table.Rows) + { + var key = r2["key"].ToString(); + var type = Type.GetType(r2["type"].ToString()); + + var value = Newtonsoft.Json.JsonConvert.DeserializeObject(r2["value"].ToString(), type); + + se.Values.Add(key, value); + } + } + + } + + } + + + + connection.Close(); + } + + return sc; + } + + public void SaveFormStates(SaveStatesEventArgs e) + { + var container = e.States; + + using (var connection = new SqlConnection(ConnectionString)) + { + connection.Open(); + + //Cleanup old Session data + var clear_command = connection.CreateCommand(); + + clear_command.CommandText = $"DELETE FROM {TablePrefix}devices_sessions_data"; + + clear_command.ExecuteNonQuery(); + + clear_command.CommandText = $"DELETE FROM {TablePrefix}devices_sessions"; + + clear_command.ExecuteNonQuery(); + + //Prepare new session commands + var session_command = connection.CreateCommand(); + var data_command = connection.CreateCommand(); + + session_command.CommandText = "INSERT INTO " + TablePrefix + "devices_sessions (deviceId, deviceTitle, FormUri, QualifiedName) VALUES (@deviceId, @deviceTitle, @FormUri, @QualifiedName)"; + session_command.Parameters.Add(new SqlParameter("@deviceId", "")); + session_command.Parameters.Add(new SqlParameter("@deviceTitle", "")); + session_command.Parameters.Add(new SqlParameter("@FormUri", "")); + session_command.Parameters.Add(new SqlParameter("@QualifiedName", "")); + + data_command.CommandText = "INSERT INTO " + TablePrefix + "devices_sessions_data (deviceId, [key], value, type) VALUES (@deviceId, @key, @value, @type)"; + data_command.Parameters.Add(new SqlParameter("@deviceId", "")); + data_command.Parameters.Add(new SqlParameter("@key", "")); + data_command.Parameters.Add(new SqlParameter("@value", "")); + data_command.Parameters.Add(new SqlParameter("@type", "")); + + //Store session data in database + foreach (var state in container.States) + { + session_command.Parameters["@deviceId"].Value = state.DeviceId; + session_command.Parameters["@deviceTitle"].Value = state.ChatTitle ?? ""; + session_command.Parameters["@FormUri"].Value = state.FormUri; + session_command.Parameters["@QualifiedName"].Value = state.QualifiedName; + + session_command.ExecuteNonQuery(); + + foreach (var data in state.Values) + { + data_command.Parameters["@deviceId"].Value = state.DeviceId; + data_command.Parameters["@key"].Value = data.Key; + + var type = data.Value.GetType(); + + if (type.IsPrimitive || type.Equals(typeof(string))) + { + data_command.Parameters["@value"].Value = data.Value; + } + else + { + data_command.Parameters["@value"].Value = Newtonsoft.Json.JsonConvert.SerializeObject(data.Value); + } + + data_command.Parameters["@type"].Value = type.AssemblyQualifiedName; + + data_command.ExecuteNonQuery(); + } + + } + + connection.Close(); + } + + + } + } +} \ No newline at end of file diff --git a/TelegramBotBase.Extensions.Serializer.Database.MSSQL/README.md b/TelegramBotBase.Extensions.Serializer.Database.MSSQL/README.md new file mode 100644 index 0000000..565bc57 --- /dev/null +++ b/TelegramBotBase.Extensions.Serializer.Database.MSSQL/README.md @@ -0,0 +1,8 @@ +# TelegramBotBase.Extensions.Images + +[![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/v/TelegramBotBase.Extensions.Serializer.Database.MSSQL.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.MSSQL/) +[![telegram chat](https://img.shields.io/badge/Support_Chat-Telegram-blue.svg?style=flat-square)](https://www.t.me/tgbotbase) + + +[![license](https://img.shields.io/github/license/MajMcCloud/telegrambotframework.svg?style=flat-square&maxAge=2592000&label=License)](https://raw.githubusercontent.com/MajMcCloud/TelegramBotFramework/master/LICENCE.md) +[![downloads](https://img.shields.io/nuget/dt/TelegramBotBase.Extensions.Serializer.Database.MSSQL.svg?style=flat-square&label=Package%20Downloads)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.MSSQL) diff --git a/TelegramBotBase.Extensions.Serializer.Database.MSSQL/TelegramBotBase.Extensions.Serializer.Database.MSSQL.csproj b/TelegramBotBase.Extensions.Serializer.Database.MSSQL/TelegramBotBase.Extensions.Serializer.Database.MSSQL.csproj new file mode 100644 index 0000000..c8b7f27 --- /dev/null +++ b/TelegramBotBase.Extensions.Serializer.Database.MSSQL/TelegramBotBase.Extensions.Serializer.Database.MSSQL.csproj @@ -0,0 +1,25 @@ + + + + netstandard2.0;net5;netcoreapp3.1;net6 + True + https://github.com/MajMcCloud/TelegramBotFramework + https://github.com/MajMcCloud/TelegramBotFramework + MIT + true + snupkg + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/TelegramBotBase.Extensions.Serializer.Database.MSSQL/create_tables.sql b/TelegramBotBase.Extensions.Serializer.Database.MSSQL/create_tables.sql new file mode 100644 index 0000000..587eb4f --- /dev/null +++ b/TelegramBotBase.Extensions.Serializer.Database.MSSQL/create_tables.sql @@ -0,0 +1,37 @@ +USE [telegram_bot] +GO +/****** Object: Table [dbo].[tgb_devices_sessions] Script Date: 30.06.2022 16:22:09 ******/ +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +CREATE TABLE [dbo].[tgb_devices_sessions]( + [deviceId] [bigint] NOT NULL, + [deviceTitle] [nvarchar](512) NOT NULL, + [FormUri] [nvarchar](512) NOT NULL, + [QualifiedName] [nvarchar](512) NOT NULL, + CONSTRAINT [PK_tgb_devices_sessions_1] PRIMARY KEY CLUSTERED +( + [deviceId] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY] +GO +/****** Object: Table [dbo].[tgb_devices_sessions_data] Script Date: 30.06.2022 16:22:09 ******/ +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +CREATE TABLE [dbo].[tgb_devices_sessions_data]( + [Id] [uniqueidentifier] NOT NULL, + [deviceId] [bigint] NOT NULL, + [key] [nvarchar](512) NOT NULL, + [value] [nvarchar](max) NOT NULL, + [type] [nvarchar](512) NOT NULL, + CONSTRAINT [PK_tgb_devices_session_data] PRIMARY KEY CLUSTERED +( + [Id] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +GO +ALTER TABLE [dbo].[tgb_devices_sessions_data] ADD CONSTRAINT [DF_tgb_devices_session_data_Id] DEFAULT (newid()) FOR [Id] +GO