/***********************************************************************************************************************/ /*** DO NOT edit this file! Edit the files under `oxide/config` and/or `oxide/lang`, created once plugin has loaded. ***/ /***********************************************************************************************************************/ using System.Collections.Generic; using Oxide.Core.Libraries.Covalence; using Oxide.Core; using Oxide.Core.Libraries; using System; using System.Text; using System.Security.Cryptography; namespace Oxide.Plugins { [Info("Tip4serv", "Murgator & Duster", "1.4.8")] [Description("Allows Admin to monetize their 7 Days to die & Rust server from their Tip4serv store")] public class Tip4serv : CovalencePlugin { private class PluginConfig { public int request_interval_in_minutes; public string configkey; public string order_received_text; } [Serializable] public class ResponseData { public string date; public string action; public Dictionary cmds; public int status; public string username; } [Serializable] public class Payments { public string player; public string action; public string id; public string steamid; public PaymentCmd[] cmds; } [Serializable] public class PaymentCmd { public string str; public int id; public int state; } private String key_msg = "Please set the config key to a valid key in your config/Tip4Serv.json file. Make sure you have copied the entire key on Tip4Serv.com (Ctrl+A then CTRL+C)"; private bool Stopped = false; private Timer PaymentTimer; private PluginConfig config; protected override void LoadDefaultConfig() { LogWarning("Creating a new configuration file"); Config.WriteObject(GetDefaultConfig(), true); } private PluginConfig GetDefaultConfig() { return new PluginConfig { request_interval_in_minutes = 1, configkey = "YOUR_CONFIG_KEY", order_received_text = "[#cyan][Tip4serv][/#] You have received your order. Thank you !" }; } private Dictionary GetPlayers() { IEnumerable players = covalence.Players.Connected; Dictionary tip4customers = new Dictionary(); foreach (IPlayer player in players) { tip4customers[player.Id] = player; } return tip4customers; } private void Loaded() { #if !SEVENDAYSTODIE && !RUST LogError("This plugin only works for the 7 Days to Die or Rust Game"); Stopped = true; #else Tip4Print("Tip4serv plugin has started"); config = Config.ReadObject(); #endif } private void Unload() { key_msg = null; if(PaymentTimer != null && !PaymentTimer.Destroyed) PaymentTimer.Destroy(); } void OnServerInitialized() { if (!Stopped) { //check Tip4serv connection on script start string[] key_part = config.configkey.Split('.'); if (key_part.Length != 3) { Tip4Print(key_msg); return; } check_pending_commands(key_part, GetUnixTime(), "no"); PaymentTimer = timer.In((float)config.request_interval_in_minutes * 60f,() => PaymentChecker()); } } private void PaymentChecker() { string[] key_part = config.configkey.Split('.'); if (key_part.Length != 3) { Tip4Print(key_msg); return; } check_pending_commands(key_part, GetUnixTime(), "yes"); if(PaymentTimer!=null && !PaymentTimer.Destroyed) PaymentTimer.Destroy(); PaymentTimer = timer.In((float)config.request_interval_in_minutes * 60f,() => PaymentChecker()); } private string GetUnixTime() { long unixTime = ((DateTimeOffset)DateTime.Now).ToUnixTimeSeconds(); return unixTime.ToString(); } private void Tip4Print(string content) { LogWarning(content); } private void check_pending_commands(string[] key_parts, string timestamp, string get_cmd) { //HMAC calculation string HMAC = calculateHMAC(key_parts, timestamp); //get last infos from the json file Dictionary response = LoadFile("tip4serv_response"); string json_encoded = ""; if (response.Count > 0) { json_encoded = Utility.ConvertToJson(response); } //Url parameters string ApiUrl = "https://api.tip4serv.com/payments_api_v2.php?id=" + key_parts[0] + "&time=" + timestamp; string EntireApiUrl = ApiUrl + "&get_cmd=" + get_cmd; //Header HMAC Dictionary Headers = new Dictionary { { "Authorization", HMAC }, { "Content-Type", "application/json" } }; //Post Request webrequest.Enqueue(EntireApiUrl, json_encoded, (code, HTTPresponse) => { if (code != 200 || HTTPresponse == null) { if (get_cmd == "no") { Tip4Print("Tip4serv API is temporarily unavailable, maybe you are making too many requests. Please try again later"); } return; } //tip4serv connect if (get_cmd == "no") { Tip4Print(HTTPresponse); return; } response.Clear(); //check for errors if (HTTPresponse.Contains("No pending payments found") || get_cmd == "update") { Interface.Oxide.DataFileSystem.WriteObject("tip4serv_response", response); return; } else if (HTTPresponse.StartsWith("\"[Tip4serv ")) { Tip4Print(HTTPresponse); return; } //clear old json infos Interface.Oxide.DataFileSystem.WriteObject("tip4serv_response", response); Dictionary players = GetPlayers(); var json_decoded = Utility.ConvertFromJson>(HTTPresponse); var update_now = false; //loop customers for (int i = 0; i < json_decoded.Count; i++) { ResponseData new_obj = new ResponseData(); Dictionary new_cmds = new Dictionary(); string payment_id = json_decoded[i].id; new_obj.date = DateTime.Now.ToString(); new_obj.action = json_decoded[i].action; new_obj.username = ""; //check if player is online IPlayer player_infos = checkifPlayerIsLoaded(json_decoded[i].steamid,players); if (player_infos != null) { new_obj.username = player_infos.Name; player_infos.Message(config.order_received_text); } if (json_decoded[i].cmds.Length != 0) { for (int j = 0; j < json_decoded[i].cmds.Length; j++) { //do not run this command if the player must be online if (player_infos == null && (json_decoded[i].cmds[j].str.Contains("{") || (json_decoded[i].cmds[j].state == 1))) { new_obj.status = 14; } else { #if SEVENDAYSTODIE if (json_decoded[i].cmds[j].str.Contains("{7dtd_username}")) #elif RUST if (json_decoded[i].cmds[j].str.Contains("{rust_username}")) #endif { if (player_infos != null) { #if SEVENDAYSTODIE json_decoded[i].cmds[j].str = json_decoded[i].cmds[j].str.Replace("{7dtd_username}", player_infos.Name); #elif RUST json_decoded[i].cmds[j].str = json_decoded[i].cmds[j].str.Replace("{rust_username}", player_infos.Name); #endif } } string[] empty = { }; exe_command(json_decoded[i].cmds[j].str, empty); new_cmds[json_decoded[i].cmds[j].id] = 3; update_now = true; } } new_obj.cmds = new_cmds; if (new_obj.status == 0) { new_obj.status = 3; } response[payment_id] = new_obj; } } //save the new json file Interface.Oxide.DataFileSystem.WriteObject("tip4serv_response", response); //update commands status on tip4serv if a command has been delivered if (update_now == true){ //Json response data json_encoded = Utility.ConvertToJson(response); //Url parameters string EntireApiUrl = ApiUrl + "&get_cmd=update"; //Header HMAC Dictionary Headers = new Dictionary { { "Authorization", HMAC }, { "Content-Type", "application/json" } }; webrequest.Enqueue(EntireApiUrl, json_encoded, (code, HTTPresponse) => { if (code == 200 && HTTPresponse != null){ response.Clear(); Interface.Oxide.DataFileSystem.WriteObject("tip4serv_response", response); } return; }, this, RequestMethod.POST, Headers, 10f); } }, this, RequestMethod.POST, Headers, 10f); } private Dictionary LoadFile(string path) { Dictionary response; try { response = Interface.Oxide.DataFileSystem.ReadObject>("tip4serv_response"); if (response == null) { response = new Dictionary(); } } catch (Exception) { response = new Dictionary(); } return response; } private string calculateHMAC(string[] key_parts, string timestamp) { HMACSHA256 Encryptor = new HMACSHA256(Encoding.ASCII.GetBytes(key_parts[1])); key_parts[1] = ""; string Total_Key = string.Join("", key_parts); Total_Key += timestamp; var signature = Encryptor.ComputeHash(Encoding.ASCII.GetBytes(Total_Key)); var HMACstr = BitConverter.ToString(signature).Replace("-", "").ToLower(); HMACstr = Convert.ToBase64String(Encoding.ASCII.GetBytes(HMACstr)); return HMACstr; } private IPlayer checkifPlayerIsLoaded(string steam_id, Dictionary players) { IPlayer SteamPlayer = players.TryGetValue(steam_id, out SteamPlayer) ? SteamPlayer : null; if (SteamPlayer == null) { return null; } #if RUST if (SteamPlayer.IsConnected) { return SteamPlayer; } else { return null; } #elif SEVENDAYSTODIE try { //here is the trick we get the position //if the position of the player is returned it mean the player is connected //on the other case if Position() throw an exception it means that the player ain't connected GenericPosition JUNKPOS = SteamPlayer.Position(); return SteamPlayer; } catch(KeyNotFoundException e) { return null; } #endif return null; } private void exe_command(string cmd, string[] CmdArgs) { Tip4Print("Tip4serv execute command: "+cmd); server.Command(cmd, CmdArgs); } } }