Иконка ресурса

FREE AzSync : 1.0.0 1.0.0

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Oxide.Core;
using Oxide.Core.Database;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.SQLite.Libraries;
using Oxide.Core.Libraries;
using Oxide.Core.Plugins;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Linq;
using System.Threading.Tasks;

namespace Oxide.Plugins
{
    [Info("AzSync", "theblobinc", "1.0.0")]
    [Description("Asynchronously syncs economic and user data between Rust server and Azuriom website")]
    public class AzSync : CovalencePlugin
    {
        #region Configuration

        public class AzSyncConfig
        {
            [JsonProperty("Azuriom Settings")]
            public AzuriomSettings Azuriom { get; set; } = new AzuriomSettings();
        }

        public class AzuriomSettings
        {
            [JsonProperty("API Base URL")]
            public string BaseUrl { get; set; } = "https://yourazuriomsite.com/api/";

            [JsonProperty("Site Key (ApiToken)")]
            public string SiteKey { get; set; } = ""; // Site Key serves as the ApiToken

            [JsonProperty("API IP Address")]
            public string ApiIPAddress { get; set; } = "127.0.0.1"; // Default to localhost

            [JsonProperty("API Port")]
            public int ApiPort { get; set; } = 8181; // Default port (e.g., 8181)
        }

        public class TransactionRecord
        {
            [JsonProperty("timestamp")]
            public string Timestamp { get; set; } // ISO 8601 format

            [JsonProperty("transaction_type")]
            public string TransactionType { get; set; } // "deposit", "withdrawal", "transfer", etc.[JsonProperty("steam_id")]
            public string SteamId { get; set; } // Player initiating the transaction

            [JsonProperty("target_steam_id")]
            public string TargetSteamId { get; set; } // Player receiving funds (for transfers)

            [JsonProperty("amount")]
            public double Amount { get; set; }
        }

        public static class QueryStringParser
        {
            public static Dictionary<string, string> Parse(string query)
            {
                var queryDictionary = new Dictionary<string, string>();

                if (string.IsNullOrWhiteSpace(query))
                    return queryDictionary;

                // Remove the leading '?' if present
                if (query.StartsWith("?"))
                    query = query.Substring(1);

                var pairs = query.Split('&', StringSplitOptions.RemoveEmptyEntries);

                foreach (var pair in pairs)
                {
                    var kvp = pair.Split('=', 2);
                    if (kvp.Length == 2)
                    {
                        var key = Uri.UnescapeDataString(kvp[0]);
                        var value = Uri.UnescapeDataString(kvp[1]);
                        queryDictionary[key] = value;
                    }
                    else if (kvp.Length == 1)
                    {
                        var key = Uri.UnescapeDataString(kvp[0]);
                        queryDictionary[key] = string.Empty;
                    }
                }

                return queryDictionary;
            }
        }

        private AzSyncConfig pluginConfig;

        protected override void LoadDefaultConfig()
        {
            Puts("Creating a new configuration file.");
            pluginConfig = new AzSyncConfig();
            SaveConfig();
        }

        private void LoadConfigData()
        {
            pluginConfig = Config.ReadObject<AzSyncConfig>();
            if (pluginConfig == null)
            {
                Puts("Could not load config, creating new default config.");
                pluginConfig = new AzSyncConfig();
                SaveConfig();
            }

            // Validate essential configurations
            if (string.IsNullOrEmpty(pluginConfig.Azuriom.BaseUrl))
            {
                Puts("Azuriom BaseUrl is not set.Please configure it in the plugin config.");
            }

            if (string.IsNullOrEmpty(pluginConfig.Azuriom.SiteKey))
            {
                Puts("Azuriom Site Key is not set.Please configure it in the plugin config.");
            }

            if (string.IsNullOrEmpty(pluginConfig.Azuriom.ApiIPAddress))
            {
                Puts("API IP Address is not set.Defaulting to 127.0.0.1.");
                pluginConfig.Azuriom.ApiIPAddress = "127.0.0.1";
                SaveConfig();
            }

            if (pluginConfig.Azuriom.ApiPort <= 0 || pluginConfig.Azuriom.ApiPort > 65535)
            {
                Puts($"Invalid API Port '{pluginConfig.Azuriom.ApiPort}' in config.Defaulting to 8181.");
                pluginConfig.Azuriom.ApiPort = 8181;
                SaveConfig();
            }
        }

        protected override void SaveConfig()
        {
            Config.WriteObject(pluginConfig, true);
        }

        #endregion

        #region BalanceOperation Enum

        /// <summary>
        /// Enum representing the type of balance operation./// </summary>
        public enum BalanceOperation
        {
            Add,
            Subtract,
            Set
        }

        #endregion

        #region Fields and Properties

        private Plugin EconomicsPlugin;
        private bool isSyncing = false;

        private HttpListener _httpListener;

        private Core.SQLite.Libraries.SQLite Sqlite;
        private Connection Sqlite_conn;

        private Dictionary<(string path, string method), Action<HttpListenerRequest, Action<string>>> routeTable;

        // Lock object for thread safety
        private readonly object balanceLock = new object();

        // **Fields for Ledger Management**
        private string dbPath;

        // Timer for consistency checks
        private Timer consistencyTimer;

        #endregion

        #region Initialization

        private void Init()
        {
            LoadConfigData();

            // Define the path to the SQLite database file
            dbPath = Path.Combine(Interface.Oxide.DataDirectory, "azsync-transactions.db");

            // Retrieve the Sqlite library from Oxide
            Sqlite = Interface.Oxide.GetLibrary<Core.SQLite.Libraries.SQLite>();

            // Open the database connection
            Sqlite_conn = Sqlite.OpenDb(dbPath, this);

            if (Sqlite_conn == null)
            {
                Puts("Failed to open SQLite database connection.");
                return;
            }

            // Initialize the SQLite ledger database
            InitializeDatabase();

            // **Reordered: Attempt to load the Economics plugin before loading balances**
            EconomicsPlugin = plugins.Find("Economics");
            if (EconomicsPlugin == null)
            {
                Puts("Economics plugin not found.AzSync requires the Economics plugin to function.");
                return;
            }
            else
            {
                Puts("Economics plugin found and loaded successfully.");
            }

            // Load Economics plugin balances into SQLite
            LoadEconomicsBalancesToDatabase();

            // Initialize the route table
            InitializeRouteTable();

            // Start initial synchronization from website to in-game balances
            FetchAndSyncBalancesFromWebsiteAsync();

            // Start HTTP Listener for receiving data from Azuriom
            StartHttpListener();

            // Start the consistency verification timer (e.g., every 5 minutes)
            consistencyTimer = timer.Repeat(300, 0, VerifyConsistency);
            Puts("Started consistency verification timer.");

            Puts("AzSync plugin initialized.");
        }

        private void Unload()
        {
            StopHttpListener();

            if (Sqlite_conn != null)
            {
                Sqlite.CloseDb(Sqlite_conn);
                Puts("SQLite connection closed.");
            }

            // Destroy the consistency timer
            if (consistencyTimer != null)
            {
                consistencyTimer.Destroy();
                Puts("Consistency verification timer stopped.");
            }

            Puts("AzSync plugin unloaded.");
        }

        #endregion

        #region SQLite Ledger Management

        /// <summary>
        /// Initializes the SQLite database and creates the necessary tables if they don't exist./// </summary>
        private void InitializeDatabase()
        {
            var createTransactionsTableSql = @"
                CREATE TABLE IF NOT EXISTS transactions (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    timestamp TEXT NOT NULL,
                    transaction_type TEXT NOT NULL,
                    steam_id TEXT NOT NULL,
                    target_steam_id TEXT,
                    amount REAL NOT NULL
                );";

            var createUserBalancesTableSql = @"
                CREATE TABLE IF NOT EXISTS user_balances (
                    steam_id TEXT PRIMARY KEY,
                    balance REAL NOT NULL
                );";

            var createUserAccountsTableSql = @"
                CREATE TABLE IF NOT EXISTS user_accounts (
                    steam_id TEXT PRIMARY KEY
                    -- Add other relevant fields if necessary
                );";

            try
            {
                // Create transactions table
                Sqlite.ExecuteNonQuery(Core.Database.Sql.Builder.Append(createTransactionsTableSql), Sqlite_conn, (rows) =>
                {
                    Puts("SQLite transactions table ensured.");
                });

                // Create user_balances table
                Sqlite.ExecuteNonQuery(Core.Database.Sql.Builder.Append(createUserBalancesTableSql), Sqlite_conn, (rows) =>
                {
                    Puts("SQLite user_balances table ensured.");
                });

                // Create user_accounts table
                Sqlite.ExecuteNonQuery(Core.Database.Sql.Builder.Append(createUserAccountsTableSql), Sqlite_conn, (rows) =>
                {
                    Puts("SQLite user_accounts table ensured.");
                });
            }
            catch (Exception ex)
            {
                Puts($"Error creating tables: {ex.Message}");
            }
        }

        /// <summary>
        /// Inserts a new transaction record into the SQLite database./// </summary>
        /// <param name="transaction">The transaction record to insert.</param>
        private void InsertTransaction(TransactionRecord transaction)
        {
            var sql = Core.Database.Sql.Builder.Append(
                "INSERT INTO transactions (timestamp, transaction_type, steam_id, target_steam_id, amount) VALUES (@0, @1, @2, @3, @4)",
                transaction.Timestamp,
                transaction.TransactionType,
                transaction.SteamId,
                transaction.TargetSteamId,
                transaction.Amount);

            try
            {
                Sqlite.Insert(sql, Sqlite_conn, rowsAffected =>
                {
                    if (rowsAffected > 0)
                    {
                        Puts($"Transaction inserted: {transaction.TransactionType} of {transaction.Amount} by {transaction.SteamId}" +
                             (string.IsNullOrEmpty(transaction.TargetSteamId) ? "" : $" to {transaction.TargetSteamId}"));
                    }
                    else
                    {
                        Puts("Failed to insert transaction.");
                    }
                });
            }
            catch (Exception ex)
            {
                Puts($"Error inserting transaction: {ex.Message}");
            }
        }

        /// <summary>
        /// Inserts or updates a user's balance in the SQLite database./// </summary>
        /// <param name="steamId">The player's SteamID.</param>
        /// <param name="balance">The new balance amount.</param>
        /// <returns>True if the operation was successful; otherwise, false.</returns>
        private bool UpsertUserBalance(string steamId, double balance)
        {
            var sql = Core.Database.Sql.Builder.Append(@"
                INSERT INTO user_balances (steam_id, balance) 
                VALUES (@0, @1)
                ON CONFLICT(steam_id) DO UPDATE SET balance = @1;",
                steamId, balance);

            try
            {
                Sqlite.Insert(sql, Sqlite_conn);
                Puts($"Upserted balance for SteamID: {steamId} to {balance}.");
                return true;
            }
            catch (Exception ex)
            {
                Puts($"Error upserting balance for SteamID {steamId}: {ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// Retrieves all player SteamIDs from the SQLite database./// </summary>
        /// <param name="callback">The callback to execute with the results.</param>
        private void GetAllPlayerSteamIds(Action<List<string>> callback)
        {
            var sql = Core.Database.Sql.Builder.Append("SELECT steam_id FROM user_balances");
            Sqlite.Query(sql, Sqlite_conn, list =>
            {
                var steamIds = new List<string>();
                foreach (var record in list)
                {
                    if (record.ContainsKey("steam_id"))
                    {
                        steamIds.Add(record["steam_id"].ToString());
                    }
                }
                callback(steamIds);
            });
        }

        /// <summary>
        /// Retrieves a user's balance from the SQLite database./// </summary>
        private void GetDatabaseBalance(string steamId, Action<double> callback)
        {
            var sql = Core.Database.Sql.Builder.Append("SELECT balance FROM user_balances WHERE steam_id = @0", steamId);
            Sqlite.Query(sql, Sqlite_conn, list =>
            {
                double balance = 0.0;

                if (list != null && list.Count > 0)
                {
                    var record = list[0];
                    if (record.ContainsKey("balance") && double.TryParse(record["balance"].ToString(), out double dbBalance))
                    {
                        balance = dbBalance;
                    }
                }

                callback(balance);
            });
        }

        /// <summary>
        /// Adds a new user to the user_balances table./// </summary>
        private void AddUserToDatabase(string steamId, double initialBalance = 0.0)
        {
            var sql = Core.Database.Sql.Builder.Append(@"
                INSERT INTO user_balances (steam_id, balance) 
                VALUES (@0, @1)
                ON CONFLICT(steam_id) DO NOTHING;",
                steamId, initialBalance);

            try
            {
                Sqlite.Insert(sql, Sqlite_conn);
                Puts($"Added user to database: SteamID {steamId} with balance {initialBalance}.");
            }
            catch (Exception ex)
            {
                Puts($"Error adding user {steamId} to database: {ex.Message}");
            }
        }

        /// <summary>
        /// Checks if a user exists in the user_balances table./// </summary>
        /// <param name="steamId">The SteamID to check for.</param>
        /// <param name="callback">The callback to execute with the result.</param>
        private void UserExistsInDatabase(string steamId, Action<bool> callback)
        {
            var sql = Core.Database.Sql.Builder.Append("SELECT COUNT(*) AS count FROM user_balances WHERE steam_id = @0", steamId);
            Sqlite.Query(sql, Sqlite_conn, list =>
            {
                bool exists = false;
                if (list != null && list.Count > 0)
                {
                    var record = list[0];
                    if (record.ContainsKey("count") && int.TryParse(record["count"].ToString(), out int count))
                    {
                        exists = count > 0;
                    }
                }
                callback(exists);
            });
        }

        /// <summary>
        /// Asynchronously retrieves transactions from the SQLite database based on optional time range filters and pagination./// </summary>
        private void GetTransactions(DateTime? startTime, DateTime? endTime, int limit, int offset, Action<List<TransactionRecord>> callback)
        {
            var sqlQuery = "SELECT timestamp, transaction_type, steam_id, target_steam_id, amount FROM transactions WHERE 1=1";
            var parameters = new List<object>();
            int paramIndex = 0;

            if (startTime.HasValue)
            {
                sqlQuery += $" AND timestamp >= @{paramIndex}";
                parameters.Add(startTime.Value.ToString("o")); // ISO 8601 format
                paramIndex++;
            }

            if (endTime.HasValue)
            {
                sqlQuery += $" AND timestamp <= @{paramIndex}";
                parameters.Add(endTime.Value.ToString("o")); // ISO 8601 format
                paramIndex++;
            }

            sqlQuery += $" ORDER BY timestamp DESC LIMIT @{paramIndex} OFFSET @{paramIndex + 1};";
            parameters.Add(limit);
            parameters.Add(offset);

            var sql = Core.Database.Sql.Builder.Append(sqlQuery, parameters.ToArray());

            Sqlite.Query(sql, Sqlite_conn, list =>
            {
                if (list == null || list.Count == 0)
                {
                    Puts("No transactions found or an error occurred while fetching transactions.");
                    callback(new List<TransactionRecord>());
                    return;
                }

                var results = new List<TransactionRecord>();
                foreach (var dict in list)
                {
                    var record = new TransactionRecord
                    {
                        Timestamp = dict["timestamp"].ToString(),
                        TransactionType = dict["transaction_type"].ToString(),
                        SteamId = dict["steam_id"].ToString(),
                        TargetSteamId = dict.ContainsKey("target_steam_id") ? dict["target_steam_id"]?.ToString() : "",
                        Amount = Convert.ToDouble(dict["amount"])
                    };
                    results.Add(record);
                }

                callback(results);
            });
        }

        /// <summary>
        /// Loads Economics plugin balances into SQLite database./// </summary>
        private void LoadEconomicsBalancesToDatabase()
        {
            Puts("Loading balances from Economics plugin to SQLite database.");

            if (EconomicsPlugin == null)
            {
                Puts("EconomicsPlugin is null.Cannot load balances.");
                return;
            }

            // Retrieve all players from the Economics plugin
            var allPlayers = EconomicsPlugin.Call("GetAllPlayers") as List<IPlayer>;

            if (allPlayers == null)
            {
                Puts("EconomicsPlugin returned null or invalid data for GetAllPlayers.");
                return;
            }

            foreach (var player in allPlayers)
            {
                if (player == null)
                {
                    Puts("Encountered a null player in EconomicsPlugin.GetAllPlayers().Skipping.");
                    continue;
                }

                string steamId = player.Id;
                object balanceObj = EconomicsPlugin.Call("GetBalance", player);
                double balance = balanceObj is double b ? b : 0.0;

                // Upsert the user's balance into the SQLite database
                UpsertUserBalance(steamId, balance);
                Puts($"Loaded balance for SteamID: {steamId} with amount: {balance}.");
            }

            Puts("Completed loading Economics balances into SQLite database.");
        }

        #endregion

        #region HTTP Listener

        private void StartHttpListener()
        {
            try
            {
                _httpListener = new HttpListener();

                // Retrieve the configured IP address and port
                string apiIp = pluginConfig.Azuriom.ApiIPAddress;
                int apiPort = pluginConfig.Azuriom.ApiPort;

                // Validate IP address format
                if (!IPAddress.TryParse(apiIp, out IPAddress ipAddress))
                {
                    Puts($"Invalid API IP Address '{apiIp}' in config.Defaulting to 127.0.0.1.");
                    apiIp = "127.0.0.1";
                }

                // Validate port number
                if (apiPort <= 0 || apiPort > 65535)
                {
                    Puts($"Invalid API Port '{apiPort}' in config.Defaulting to 8181.");
                    apiPort = 8181;
                }

                // Construct the prefix using the configured IP address and port
                string prefix = $"http://{apiIp}:{apiPort}/azsync/";

                _httpListener.Prefixes.Add(prefix);
                _httpListener.Start();
                _httpListener.BeginGetContext(OnRequestReceived, null);
                Puts($"HTTP listener started on {prefix}");
            }
            catch (HttpListenerException hlex)
            {
                Puts($"Failed to start HTTP listener: {hlex.Message}");
            }
            catch (Exception ex)
            {
                Puts($"Unexpected error starting HTTP listener: {ex.Message}");
            }
        }

        private void StopHttpListener()
        {
            try
            {
                if (_httpListener != null)
                {
                    _httpListener.Stop();
                    _httpListener.Close();
                    _httpListener = null;
                    Puts("HTTP listener stopped.");
                }
            }
            catch (Exception ex)
            {
                Puts($"Error stopping HTTP listener: {ex.Message}");
            }
        }

        private void InitializeRouteTable()
        {
            routeTable = new Dictionary<(string path, string method), Action<HttpListenerRequest, Action<string>>>()
            {
                { ("/azsync/set-balance", "POST"), HandleBalanceUpdateRequest },
                { ("/azsync/get-balance", "GET"), HandleGetBalanceRequest },
                { ("/azsync/user-check", "GET"), HandleUserCheckRequest },
                { ("/azsync/reset-balance", "POST"), HandleResetBalanceRequest },
                { ("/azsync/get-transactions", "GET"), HandleGetTransactions },
                { ("/azsync/add-user", "POST"), HandleAddUserRequest },
                { ("/azsync/add-balance", "POST"), HandleAddBalanceRequest },
                { ("/azsync/subtract-balance", "POST"), HandleSubtractBalanceRequest },
                { ("/azsync/get-all-balances", "GET"), HandleGetAllBalancesRequest }
                // Add more routes here as needed
            };
        }

        private void OnRequestReceived(IAsyncResult result)
        {
            if (_httpListener == null || !_httpListener.IsListening) return;

            HttpListenerContext context = null;
            try
            {
                context = _httpListener.EndGetContext(result);
            }
            catch (Exception)
            {
                // Handle if needed
                return;
            }
            finally
            {
                if (_httpListener.IsListening)
                {
                    _httpListener.BeginGetContext(OnRequestReceived, null);
                }
            }

            var request = context.Request;
            var response = context.Response;

            // API key verification
            if (request.Headers["Azuriom-Link-Token"] == null ||
                !request.Headers["Azuriom-Link-Token"].Equals(pluginConfig.Azuriom.SiteKey))
            {
                Puts($"Unauthorized access attempt from {request.RemoteEndPoint}");
                response.StatusCode = (int)HttpStatusCode.Unauthorized;
                string unauthorizedResponse = "{ \"error\": \"Unauthorized access\" }";
                SendResponseAsync(response, unauthorizedResponse);
                return;
            }

            // Route the request
            var path = request.Url.AbsolutePath.ToLower();
            var method = request.HttpMethod.ToUpper();

            try
            {
                var key = (path, method);
                if (routeTable.ContainsKey(key))
                {
                    routeTable[key](request, responseString =>
                    {
                        // Send the response
                        SendResponseAsync(response, responseString);
                    });
                }
                else
                {
                    // Endpoint not found
                    response.StatusCode = (int)HttpStatusCode.NotFound;
                    string responseString = "{ \"error\": \"Endpoint not found\" }";
                    SendResponseAsync(response, responseString);
                }
            }
            catch (Exception ex)
            {
                Puts($"Error processing request: {ex.Message}");
                response.StatusCode = (int)HttpStatusCode.InternalServerError;
                string responseString = "{ \"error\": \"Internal server error\" }";
                SendResponseAsync(response, responseString);
            }
        }

        private async void SendResponseAsync(HttpListenerResponse response, string responseString)
        {
            byte[] buffer = Encoding.UTF8.GetBytes(responseString);
            response.ContentLength64 = buffer.Length;
            response.ContentType = "application/json";
            try
            {
                await response.OutputStream.WriteAsync(buffer, 0, buffer.Length);
            }
            catch (Exception)
            {
                // Handle exception if needed
            }
            finally
            {
                response.Close();
            }
        }

        // Route handlers

        private void HandleBalanceUpdateRequest(HttpListenerRequest request, Action<string> responseCallback)
        {
            using (var reader = new StreamReader(request.InputStream, request.ContentEncoding))
            {
                string body = reader.ReadToEnd();
                var balanceData = JsonConvert.DeserializeObject<Dictionary<string, string>>(body);
                string responseString = HandleBalanceUpdate(balanceData);
                responseCallback(responseString);
            }
        }

        /// <summary>
        /// Handles the ResetBalance HTTP request to reset a user's balance./// </summary>
        private void HandleResetBalanceRequest(HttpListenerRequest request, Action<string> responseCallback)
        {
            using (var reader = new StreamReader(request.InputStream, request.ContentEncoding))
            {
                string body = reader.ReadToEnd();
                var resetData = JsonConvert.DeserializeObject<Dictionary<string, string>>(body);

                // Validate presence of "game_id"
                if (!resetData.ContainsKey("game_id") || string.IsNullOrWhiteSpace(resetData["game_id"]))
                {
                    Puts("ResetBalance request missing or has empty 'game_id'.");
                    responseCallback("{ \"error\": \"Missing or empty 'game_id' in request body.\" }");
                    return;
                }

                string steamId = resetData["game_id"];

                // Reset balance to zero using 'Set' operation
                bool setSuccess = SetEconomicsBalance(steamId, 0.0);
                if (!setSuccess)
                {
                    Puts($"Failed to reset balance for SteamID: {steamId} via Economics plugin.");
                    responseCallback("{ \"error\": \"Failed to reset balance via Economics plugin.\" }");
                    return;
                }

                // Find the player in-game
                var player = covalence.Players.FindPlayerById(steamId);
                if (player != null)
                {
                    Puts($"Reset balance for {steamId} to 0.");

                    // Send the reset balance to the website
                    SendBalanceToWebsite(player, 0.0, "set", () =>
                    {
                        // Sync the balance from the website to confirm
                        SyncPlayerBalanceWithWebsite(steamId);
                    });

                    // Add to Transaction Ledger
                    AddTransactionToLedger("reset_balance", steamId, 0.0, "Balance reset via API");

                    responseCallback("{ \"status\": \"Balance reset successfully.\" }");
                }
                else
                {
                    // Player is offline; balance is set in local storage only
                    Puts($"Player {steamId} is offline.Balance reset to 0 in database.");

                    // Even if the player is offline, send the balance to website and sync
                    SendBalanceToWebsite(null, 0.0, "set", () =>
                    {
                        SyncPlayerBalanceWithWebsite(steamId);
                    });

                    // Add to Transaction Ledger
                    AddTransactionToLedger("reset_balance_offline", steamId, 0.0, "Balance reset via API while offline");

                    responseCallback("{ \"status\": \"Balance reset successfully.\" }");
                }
            }
        }

        private void HandleGetBalanceRequest(HttpListenerRequest request, Action<string> responseCallback)
        {
            var queryParams = QueryStringParser.Parse(request.Url.Query);

            // Validate presence of "game_id"
            if (!queryParams.ContainsKey("game_id") || string.IsNullOrWhiteSpace(queryParams["game_id"]))
            {
                Puts("GetBalance request missing or has empty 'game_id'.");
                responseCallback("{ \"error\": \"Missing or empty 'game_id' in query parameters.\" }");
                return;
            }

            string steamId = queryParams["game_id"];

            // Retrieve balance from SQLite database asynchronously
            GetDatabaseBalance(steamId, balance =>
            {
                Puts($"Retrieved balance for SteamID: {steamId} - Balance: {balance}");
                string jsonResponse = $"{{ \"steam_id\": \"{steamId}\", \"balance\": {balance} }}";
                responseCallback(jsonResponse);
            });
        }

        private void HandleUserCheckRequest(HttpListenerRequest request, Action<string> responseCallback)
        {
            HandleUserCheck(responseCallback);
        }

        private void HandleGetTransactions(HttpListenerRequest request, Action<string> responseCallback)
        {
            var queryParams = QueryStringParser.Parse(request.Url.Query);

            DateTime? startTime = null;
            DateTime? endTime = null;
            int limit = 100; // Default limit
            int offset = 0;  // Default offset

            // Parse 'start_time' if provided
            if (queryParams.ContainsKey("start_time") && !string.IsNullOrWhiteSpace(queryParams["start_time"]))
            {
                if (DateTime.TryParse(queryParams["start_time"], null, System.Globalization.DateTimeStyles.RoundtripKind, out DateTime parsedStart))
                {
                    startTime = parsedStart.ToUniversalTime();
                }
                else
                {
                    Puts($"Invalid 'start_time' format received: {queryParams["start_time"]}");
                    responseCallback("{ \"error\": \"Invalid 'start_time' format.Use ISO 8601 format.\" }");
                    return;
                }
            }

            // Parse 'end_time' if provided
            if (queryParams.ContainsKey("end_time") && !string.IsNullOrWhiteSpace(queryParams["end_time"]))
            {
                if (DateTime.TryParse(queryParams["end_time"], null, System.Globalization.DateTimeStyles.RoundtripKind, out DateTime parsedEnd))
                {
                    endTime = parsedEnd.ToUniversalTime();
                }
                else
                {
                    Puts($"Invalid 'end_time' format received: {queryParams["end_time"]}");
                    responseCallback("{ \"error\": \"Invalid 'end_time' format.Use ISO 8601 format.\" }");
                    return;
                }
            }

            // Parse 'limit' if provided
            if (queryParams.ContainsKey("limit") && !string.IsNullOrWhiteSpace(queryParams["limit"]))
            {
                if (int.TryParse(queryParams["limit"], out int parsedLimit))
                {
                    if (parsedLimit > 0 && parsedLimit <= 1000)
                    {
                        limit = parsedLimit;
                    }
                    else
                    {
                        Puts($"Invalid 'limit' value received: {queryParams["limit"]}.Must be between 1 and 1000.");
                        responseCallback("{ \"error\": \"Invalid 'limit' value.Must be between 1 and 1000.\" }");
                        return;
                    }
                }
                else
                {
                    Puts($"Invalid 'limit' format received: {queryParams["limit"]}");
                    responseCallback("{ \"error\": \"Invalid 'limit' format.Must be an integer.\" }");
                    return;
                }
            }

            // Parse 'offset' if provided
            if (queryParams.ContainsKey("offset") && !string.IsNullOrWhiteSpace(queryParams["offset"]))
            {
                if (int.TryParse(queryParams["offset"], out int parsedOffset))
                {
                    if (parsedOffset >= 0)
                    {
                        offset = parsedOffset;
                    }
                    else
                    {
                        Puts($"Invalid 'offset' value received: {queryParams["offset"]}.Must be 0 or greater.");
                        responseCallback("{ \"error\": \"Invalid 'offset' value.Must be 0 or greater.\" }");
                        return;
                    }
                }
                else
                {
                    Puts($"Invalid 'offset' format received: {queryParams["offset"]}");
                    responseCallback("{ \"error\": \"Invalid 'offset' format.Must be an integer.\" }");
                    return;
                }
            }

            // Retrieve transactions from SQLite database asynchronously
            lock (balanceLock) // Ensure thread-safe access
            {
                GetTransactions(startTime, endTime, limit, offset, filteredTransactions =>
                {
                    // Serialize the filtered transactions to JSON
                    string jsonResponse = JsonConvert.SerializeObject(filteredTransactions, Formatting.Indented);

                    Puts($"Fetched {filteredTransactions.Count} transaction(s) from the ledger between " +
                         $"{(startTime.HasValue ? startTime.Value.ToString("o") : "beginning")} and " +
                         $"{(endTime.HasValue ? endTime.Value.ToString("o") : "now")} with limit {limit} and offset {offset}.");

                    responseCallback(jsonResponse);
                });
            }
        }

        // New route handlers

        /// <summary>
        /// Handles the AddUser HTTP request to add a new user./// </summary>
        private void HandleAddUserRequest(HttpListenerRequest request, Action<string> responseCallback)
        {
            using (var reader = new StreamReader(request.InputStream, request.ContentEncoding))
            {
                string body = reader.ReadToEnd();
                var userData = JsonConvert.DeserializeObject<Dictionary<string, string>>(body);

                // Validate required fields
                if (!userData.ContainsKey("steam_id"))
                {
                    Puts("AddUser request missing 'steam_id'.");
                    responseCallback("{ \"error\": \"Missing 'steam_id' in request body.\" }");
                    return;
                }

                string steamId = userData["steam_id"];

                // Optional: Initial balance
                double initialBalance = 0.0;
                if (userData.ContainsKey("balance"))
                {
                    if (!double.TryParse(userData["balance"], out initialBalance))
                    {
                        Puts($"Invalid 'balance' value received: {userData["balance"]}");
                        responseCallback("{ \"error\": \"Invalid 'balance' value.Must be a number.\" }");
                        return;
                    }
                }

                // Check if user already exists in the database
                UserExistsInDatabase(steamId, exists =>
                {
                    if (exists)
                    {
                        Puts($"User {steamId} already exists in the database.");
                        responseCallback("{ \"error\": \"User already exists.\" }");
                        return;
                    }

                    // Add user to SQLite database
                    UpsertUserBalance(steamId, initialBalance);
                    Puts($"Added user {steamId} with balance {initialBalance} to the database.");

                    // Record the addition in the transaction ledger
                    AddTransactionToLedger("add_user", steamId, initialBalance, "User added via API");

                    responseCallback("{ \"status\": \"User added successfully.\" }");
                });
            }
        }

        /// <summary>
        /// Handles the AddBalance HTTP request to add balance to an existing user./// </summary>
        private void HandleAddBalanceRequest(HttpListenerRequest request, Action<string> responseCallback)
        {
            using (var reader = new StreamReader(request.InputStream, request.ContentEncoding))
            {
                string body = reader.ReadToEnd();
                var data = JsonConvert.DeserializeObject<Dictionary<string, string>>(body);

                // Validate presence of "steam_id" and "amount"
                if (!data.ContainsKey("steam_id") || !data.ContainsKey("amount"))
                {
                    Puts("AddBalance request missing 'steam_id' or 'amount'.");
                    responseCallback("{ \"error\": \"Missing 'steam_id' or 'amount' in request body.\" }");
                    return;
                }

                string steamId = data["steam_id"];
                if (!double.TryParse(data["amount"], out double amount) || amount <= 0)
                {
                    Puts($"Invalid 'amount' value received: {data["amount"]}");
                    responseCallback("{ \"error\": \"Invalid 'amount' value.Must be a positive number.\" }");
                    return;
                }

                // Check if user exists in the database
                UserExistsInDatabase(steamId, exists =>
                {
                    if (!exists)
                    {
                        Puts($"User {steamId} does not exist in the database.");
                        responseCallback("{ \"error\": \"User does not exist.\" }");
                        return;
                    }

                    // Retrieve current balance from the database
                    GetDatabaseBalance(steamId, currentBalance =>
                    {
                        double newBalance = currentBalance + amount;

                        // Update the balance in the SQLite database
                        UpsertUserBalance(steamId, newBalance);
                        Puts($"Added {amount} to balance for SteamID: {steamId}.New balance: {newBalance}.");

                        // Find if the user is online to update in-game balance
                        var player = covalence.Players.FindPlayerById(steamId);
                        if (player != null)
                        {
                            bool economicsSuccess = SetEconomicsBalance(steamId, newBalance);
                            if (economicsSuccess)
                            {
                                Puts($"Added {amount} to in-game balance for {steamId}.");
                                SendBalanceToWebsite(player, amount, "deposit");
                            }
                            else
                            {
                                Puts($"Failed to add balance via Economics plugin for {steamId}.");
                                responseCallback("{ \"error\": \"Failed to add balance via Economics plugin.\" }");
                                return;
                            }
                        }
                        else
                        {
                            Puts($"User {steamId} is offline.Balance added to database only.");
                        }

                        // Record the addition in the transaction ledger
                        AddTransactionToLedger("add_balance", steamId, amount, "Balance added via API");

                        responseCallback("{ \"status\": \"Balance added successfully.\" }");
                    });
                });
            }
        }

        private void HandleGetAllBalancesRequest(HttpListenerRequest request, Action<string> responseCallback)
        {
            GetAllPlayerBalances(allBalances =>
            {
                string jsonResponse = JsonConvert.SerializeObject(allBalances, Formatting.Indented);
                responseCallback(jsonResponse);
            });
        }

        /// <summary>
        /// Handles the SubtractBalance HTTP request to subtract balance from an existing user./// </summary>
        private void HandleSubtractBalanceRequest(HttpListenerRequest request, Action<string> responseCallback)
        {
            using (var reader = new StreamReader(request.InputStream, request.ContentEncoding))
            {
                string body = reader.ReadToEnd();
                var data = JsonConvert.DeserializeObject<Dictionary<string, string>>(body);

                // Validate presence of "steam_id" and "amount"
                if (!data.ContainsKey("steam_id") || !data.ContainsKey("amount"))
                {
                    Puts("SubtractBalance request missing 'steam_id' or 'amount'.");
                    responseCallback("{ \"error\": \"Missing 'steam_id' or 'amount' in request body.\" }");
                    return;
                }

                string steamId = data["steam_id"];
                if (!double.TryParse(data["amount"], out double amount) || amount <= 0)
                {
                    Puts($"Invalid 'amount' value received: {data["amount"]}");
                    responseCallback("{ \"error\": \"Invalid 'amount' value.Must be a positive number.\" }");
                    return;
                }

                // Check if user exists in the database
                UserExistsInDatabase(steamId, exists =>
                {
                    if (!exists)
                    {
                        Puts($"User {steamId} does not exist in the database.");
                        responseCallback("{ \"error\": \"User does not exist.\" }");
                        return;
                    }

                    // Retrieve current balance from the database
                    GetDatabaseBalance(steamId, currentBalance =>
                    {
                        if (currentBalance < amount)
                        {
                            Puts($"User {steamId} has insufficient balance.Current: {currentBalance}, Attempted to subtract: {amount}");
                            responseCallback("{ \"error\": \"Insufficient balance.\" }");
                            return;
                        }

                        double newBalance = currentBalance - amount;

                        // Update the balance in the SQLite database
                        UpsertUserBalance(steamId, newBalance);
                        Puts($"Subtracted {amount} from balance for SteamID: {steamId}.New balance: {newBalance}.");

                        // Find if the user is online to update in-game balance
                        var player = covalence.Players.FindPlayerById(steamId);
                        if (player != null)
                        {
                            bool economicsSuccess = SetEconomicsBalance(steamId, newBalance);
                            if (economicsSuccess)
                            {
                                Puts($"Subtracted {amount} from in-game balance for {steamId}.");
                                SendBalanceToWebsite(player, amount, "withdrawal");
                            }
                            else
                            {
                                Puts($"Failed to subtract balance via Economics plugin for {steamId}.");
                                responseCallback("{ \"error\": \"Failed to subtract balance via Economics plugin.\" }");
                                return;
                            }
                        }
                        else
                        {
                            Puts($"User {steamId} is offline.Balance subtracted from database only.");
                        }

                        // Record the subtraction in the transaction ledger
                        AddTransactionToLedger("subtract_balance", steamId, amount, "Balance subtracted via API");

                        responseCallback("{ \"status\": \"Balance subtracted successfully.\" }");
                    });
                });
            }
        }

        #endregion

        #region Handling Incoming Data

        private string HandleBalanceUpdate(Dictionary<string, string> balanceData)
        {
            // Validate presence of "game_id"
            if (!balanceData.ContainsKey("game_id"))
            {
                Puts("Balance update request missing 'game_id'.");
                return "{ \"error\": \"Missing 'game_id' in balance data.\" }";
            }

            string steamId = balanceData["game_id"];

            // Validate presence of "balance"
            if (balanceData.ContainsKey("balance"))
            {
                if (double.TryParse(balanceData["balance"], out double newBalance))
                {
                    Puts($"Received balance update for SteamID: {steamId} with new balance: {newBalance}");

                    // Updated call with callback
                    SetPlayerBalance(steamId, newBalance, BalanceOperation.Set, success =>
                    {
                        if (success)
                        {
                            Puts($"Successfully set balance for SteamID: {steamId} to {newBalance}.");
                        }
                        else
                        {
                            Puts($"Failed to set balance for SteamID: {steamId}.");
                        }
                    });

                    // Find the player in-game
                    var player = covalence.Players.FindPlayerById(steamId);
                    if (player != null)
                    {
                        double currentBalance = GetEconomicsBalance(steamId);
              
// ... [Code preview truncated due to length. Full source in attached file] ...

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху