internal void SendRequest(RestRequestArgs args, Action<XElement> success, Failed failed) { if (args == null) throw new ArgumentNullException("args"); if (success == null) throw new ArgumentNullException("success"); if (failed == null) throw new ArgumentNullException("failed"); // create a request state... RequestState state = new RequestState() { Owner = this, Args = args, Success = success, Failed = failed }; // are we authenticated? if we're not, we need to call that first... if (!(IsAuthenticated)) { // call the authenticate routine, and ask it to call the state we just setup // if authentication works... ApiService.Authenticate(new Action(state.DoRequest), failed); } else { // call the method directly... state.DoRequest(); } }
public void StartPanicking() { RestRequestArgs args = new RestRequestArgs("startpanicking"); args["ResourceId"] = PanicRuntime.ResourceId; // send... this.SendRequest(args); }
protected XmlElement SendRequest(RestRequestArgs args) { // build a url... string url = HttpHelper.BuildUrl(this.ResolvedServiceUrl, args); // make a request... HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); // headers... HttpDownloadSettings settings = this.GetDownloadSettings(); foreach(string header in settings.ExtraHeaders.Keys) request.Headers.Add(header, settings.ExtraHeaders[header]); // get a response... HttpWebResponse response = (HttpWebResponse)request.GetResponse(); if(response == null) throw new InvalidOperationException("'response' is null."); using(response) { // load the response into xml... XmlDocument doc = new XmlDocument(); using(Stream stream = response.GetResponseStream()) doc.Load(stream); // did we get an AmxResponse? XmlElement amxElement = (XmlElement)doc.SelectSingleNode("AmxResponse"); if(amxElement == null) throw new InvalidOperationException("The response did not include an AmxResponse element."); // find the error element... XmlElement hasExceptionElement = (XmlElement)amxElement.SelectSingleNode("HasException"); if(hasExceptionElement == null) throw new InvalidOperationException("The response did not include a HasException element."); // did we? if(hasExceptionElement.Value == "1" || string.Compare(hasExceptionElement.Value, "true", true) == 0) { // error... XmlElement errorElement = (XmlElement)amxElement.SelectSingleNode("Error"); if(errorElement == null) throw new InvalidOperationException("The error has not found."); else throw new InvalidOperationException(string.Format("The server returned an exception: {0}", errorElement.Value)); } else return amxElement; } }
public XmlElement SendRequest(RestRequestArgs args) { // get... XmlDocument doc = args.ToXmlDocument(); // send... HttpWebRequest request = (HttpWebRequest)WebRequest.Create(this.ServiceUrl); // bytes... byte[] bs = Encoding.UTF8.GetBytes(doc.OuterXml); request.ContentLength = bs.Length; request.ContentType = "text/xml"; using (Stream stream = request.GetRequestStream()) stream.Write(bs, 0, bs.Length); // run... HttpWebResponse response = (HttpWebResponse)request.GetResponse(); // load an answer... XmlDocument answer = new XmlDocument(); using (Stream stream = response.GetResponseStream()) answer.Load(stream); // response... XmlElement responseElement = (XmlElement)answer.SelectSingleNode("RestResponse"); if(responseElement == null) throw new InvalidOperationException("'responseElement' is null."); // find... XmlElement hasErrorElement = (XmlElement)responseElement.SelectSingleNode("HasError"); if (hasErrorElement == null) throw new InvalidOperationException("'hasErrorElement' is null."); // is there a peoblem? if (hasErrorElement.Value == "1") { XmlElement errorElement = (XmlElement)responseElement.SelectSingleNode("Error"); if (errorElement != null) throw new InvalidOperationException(errorElement.Value); else throw new InvalidOperationException("An error occurred on the server, but no additional information was provided."); } // return... return responseElement; }
public void Logon(string password, Action<LogonResponse> result, Failed failed) { // create the request... RestRequestArgs args = new RestRequestArgs("logon"); args["password"] = password; // send the request... this.SendRequest(args, (Action<XElement>)delegate(XElement element) { // walk... LogonResponse response = LogonResponse.FromXmlElement(element); if (response == null) throw new InvalidOperationException("'response' is null."); // call... result(response); }, failed); }
public string Logon(string apiPassword) { // package a request... RestRequestArgs args = new RestRequestArgs("logon"); args["password"] = apiPassword; // send it... XmlElement element = SendRequest(args); if(element == null) throw new InvalidOperationException("'element' was null."); // find it... in a real implementation we'd need to look // for logon result, but for now we'll just get the element // out. see the other implementations for how this is done // properly... XmlElement tokenElement = (XmlElement)element.SelectSingleNode("Token"); if(tokenElement == null) throw new InvalidOperationException("A token element was not found."); // return... return tokenElement.InnerText; }
private object GroupInfo(RestRequestArgs args) { var ret = GroupFind(args.Parameters); if (ret is RestObject) { return(ret); } TShockAPI.Group group = (TShockAPI.Group)ret; return(new RestObject() { { "name", group.Name }, { "parent", group.ParentName }, { "chatcolor", string.Format("{0},{1},{2}", group.R, group.G, group.B) }, { "permissions", group.permissions }, { "prefix", group.Prefix }, { "suffix", group.Suffix }, { "negatedpermissions", group.negatedpermissions }, { "totalpermissions", group.TotalPermissions } }); }
public void Logon(String username, String password, Action<LogonResponse> callback, Failed failed) { // create the request... RestRequestArgs args = new RestRequestArgs("logon"); // add the username and password... args["username"] = username; args["password"] = password; // send the request... SendRequest(args, delegate(XElement element) { // create a result from that... LogonResponse response = LogonResponse.FromXmlElement(element); if (response == null) throw new InvalidOperationException("'response' is null."); // callback... callback(response); }, failed); }
private object AllPlayersStats(RestRequestArgs args) { // Gets and checks if result (stats) are not empty var AllStats = DB.GetAllStats(); if (AllStats == null) { return(RestError("Empty database")); } var Result = new RestObject(); foreach (var Row in AllStats) { Result.Add(Row.Key, new Dictionary <string, int>() { { "tiles_destroyed", Row.Value[0] }, { "tiles_placed", Row.Value[1] }, { "deaths", Row.Value[2] } }); } return(Result); }
private object PlayerReadV2(RestRequestArgs args) { var ret = PlayerFind(args.Parameters); if (ret is RestObject) { return(ret); } TSPlayer player = (TSPlayer)ret; var activeItems = player.TPlayer.inventory.Where(p => p.active).ToList(); return(new RestObject() { { "nickname", player.Name }, { "username", null == player.UserAccountName ? "" : player.UserAccountName }, { "ip", player.IP }, { "group", player.Group.Name }, { "position", player.TileX + "," + player.TileY }, { "inventory", string.Join(", ", activeItems.Select(p => (p.name + ":" + p.stack))) }, { "buffs", string.Join(", ", player.TPlayer.buffType) } }); }
private object UserCreateV2(RestRequestArgs args) { var username = args.Parameters["user"]; if (string.IsNullOrWhiteSpace(username)) { return(RestMissingParam("user")); } var group = args.Parameters["group"]; if (string.IsNullOrWhiteSpace(group)) { group = TShock.Config.DefaultRegistrationGroupName; } var password = args.Parameters["password"]; if (string.IsNullOrWhiteSpace(password)) { return(RestMissingParam("password")); } // NOTE: ip can be blank User user = new User(username, password, "", group, "", "", ""); try { TShock.Users.AddUser(user); } catch (Exception e) { return(RestError(e.Message)); } return(RestResponse("User was successfully created")); }
private object ServerCommand(RestRequestArgs args) { if (string.IsNullOrWhiteSpace(args.Parameters["cmd"])) { return(RestMissingParam("cmd")); } Group restPlayerGroup; // TODO: Get rid of this when the old REST permission model is removed. if (TShock.Config.RestUseNewPermissionModel) { restPlayerGroup = TShock.Groups.GetGroupByName(args.TokenData.UserGroupName); } else { restPlayerGroup = new SuperAdminGroup(); } TSRestPlayer tr = new TSRestPlayer(args.TokenData.Username, restPlayerGroup); Commands.HandleCommand(tr, args.Parameters["cmd"]); return(RestResponse(string.Join("\n", tr.GetCommandOutput()))); }
private object SteamBanCreate(RestRequestArgs args) { var steamid = args.Parameters["steamid"]; var reason = args.Parameters["reason"]; if (string.IsNullOrWhiteSpace(steamid)) { return(RestMissingParam("steamid")); } if (string.IsNullOrWhiteSpace(reason)) { reason = "Steam ban"; } Int64 steamid64 = -1; if (!LookupSteam64FromSteamid(steamid, out steamid64)) { return(RestError("Invalid steamid. Valid steamids are STEAM_X:X:X or Steam64 ids.")); } try { DoBan(steamid64.ToString(), "add", reason); } catch (UserException e) { return(RestError(String.Format("SQL Error: {0}", e.Message))); } return(new RestObject("200") { Response = "Successfully banned user" }); }
public static RestObject deleteRows(RestRequestArgs args) { if (!TShock.Config.UseSqlLogs) { return new RestObject("400") { Response = "UseSqlLogs not set to true.", } } ; string sqlString = Convert.ToString(args.Parameters["delete"]); if (sqlString == null) { sqlString = ""; } string sql = "DELETE FROM logs WHERE id in " + sqlString; try { TShock.DB.Query(sql); return(RestResponse("Entries deleted.")); } catch (Exception ex) { TShock.Log.Error(ex.ToString()); Console.WriteLine(ex.StackTrace); } return(new RestObject("400") { Response = "Error in deleting entries", }); }
public static RestObject getPlayerStats(RestRequestArgs args) { string searchString = Convert.ToString(args.Parameters["search"]); if (searchString == null) { searchString = ""; } PlayerStatsList rec = null; String sql; List <PlayerStatsList> playerStatsList = new List <PlayerStatsList>(); try { sql = "SELECT * FROM PlayerStats " + searchString; using (var reader = PlayerStats.playerDb.QueryReader(searchString)) { while (reader.Read()) { rec = new PlayerStatsList(reader.Get <int>("V1"), reader.Get <string>("V2"), reader.Get <int>("V3"), reader.Get <int>("V4"), reader.Get <int>("V5")); playerStatsList.Add(rec); } } return(new RestObject() { { "Rows", playerStatsList }, { "version", Assembly.GetExecutingAssembly().GetName().Version.ToString() } }); } catch (Exception ex) { TShock.Log.Error(ex.ToString()); Console.WriteLine(ex.StackTrace); } return(null); }
private object PlayerMute(RestRequestArgs args) { return(PlayerSetMute(args.Parameters, true)); }
private object WorldSave(RestRequestArgs args) { SaveManager.Instance.SaveWorld(); return(RestResponse("World saved")); }
private object ServerReload(RestRequestArgs args) { TShock.Utils.Reload(new TSRestPlayer(args.TokenData.Username, TShock.Groups.GetGroupByName(args.TokenData.UserGroupName))); return(RestResponse("Configuration, permissions, and regions reload complete. Some changes may require a server restart.")); }
object restUpdateContributors(RestRequestArgs args) { // An UpdateContributorsV2 will eventually replace this with more precise updating instead of grab all return(RestError("This endpoint has been deprecated.")); }
object restNewTransactionV2(RestRequestArgs args) { int userID; if (!Int32.TryParse(args.Verbs["user_id"], out userID)) { return(RestInvalidParam("user_id")); } if (String.IsNullOrWhiteSpace(args.Parameters["credits"])) { return(RestMissingParam("credits")); } float credits; if (!Single.TryParse(args.Parameters["credits"], out credits)) { return(RestInvalidParam("credits")); } long dateUnix = 0; if (!String.IsNullOrWhiteSpace(args.Parameters["date"])) { Int64.TryParse(args.Parameters["date"], out dateUnix); } Contributor con = _main.Contributors.GetByXenforoId(userID); bool success = false; if (con == null) { // Transactions must never be ignored. If the contributor doesn't exist, create it con = new Contributor(0); con.XenforoId = userID; con.LastAmount = credits; if (dateUnix > 0) { con.LastDonation = dateUnix.FromUnixTime(); } con.Tier = 1; con.TotalCredits = credits; success = _main.Contributors.Add(con); if (!success) { TShock.Log.ConsoleInfo($"CTRS-WARNING: Failed to register contribution made by forum user ID [{userID}]!"); } // Fire the Transaction event (must be done after Add to include the contributor Id) Transaction?.Invoke(_main.Contributors, new TransactionEventArgs(con.Id, credits, dateUnix.FromUnixTime())); } else { ContributorUpdates updates = 0; con.LastAmount = credits; updates |= ContributorUpdates.LastAmount; if (dateUnix > 0) { con.LastDonation = dateUnix.FromUnixTime(); updates |= ContributorUpdates.LastDonation; } con.TotalCredits += credits; updates |= ContributorUpdates.TotalCredits; // Fire the Transaction event var transactionArgs = new TransactionEventArgs(con.Id, credits, dateUnix.FromUnixTime()); Transaction?.Invoke(_main.Contributors, transactionArgs); // Suppress notifications if needed if (!transactionArgs.SuppressNotifications) { con.Notifications |= Notifications.NewDonation; con.Notifications |= Notifications.TierUpdate; updates |= ContributorUpdates.Notifications; } success = _main.Contributors.Update(con, updates); } if (!success) { return(RestError("Transaction was not registered properly.")); } else { return(RestResponse("Transaction successful.")); } }
private object WorldInfo(RestRequestArgs args) { var msg = new TShockAPI.Net.WorldInfoMsg { Time = (int)Main.time, DayTime = Main.dayTime, MoonPhase = (byte)Main.moonPhase, BloodMoon = Main.bloodMoon, Eclipse = Main.eclipse, MaxTilesX = (short)Main.maxTilesX, MaxTilesY = (short)Main.maxTilesY, SpawnX = (short)Main.spawnTileX, SpawnY = (short)Main.spawnTileY, WorldSurface = (short)Main.worldSurface, RockLayer = (short)Main.rockLayer, //Sending a fake world id causes the client to not be able to find a stored spawnx/y. //This fixes the bed spawn point bug. With a fake world id it wont be able to find the bed spawn. WorldID = Main.worldID, MoonType = (byte)Main.moonType, TreeX0 = Main.treeX[0], TreeX1 = Main.treeX[1], TreeX2 = Main.treeX[2], TreeStyle0 = (byte)Main.treeStyle[0], TreeStyle1 = (byte)Main.treeStyle[1], TreeStyle2 = (byte)Main.treeStyle[2], TreeStyle3 = (byte)Main.treeStyle[3], CaveBackX0 = Main.caveBackX[0], CaveBackX1 = Main.caveBackX[1], CaveBackX2 = Main.caveBackX[2], CaveBackStyle0 = (byte)Main.caveBackStyle[0], CaveBackStyle1 = (byte)Main.caveBackStyle[1], CaveBackStyle2 = (byte)Main.caveBackStyle[2], CaveBackStyle3 = (byte)Main.caveBackStyle[3], SetBG0 = (byte)WorldGen.treeBG, SetBG1 = (byte)WorldGen.corruptBG, SetBG2 = (byte)WorldGen.jungleBG, SetBG3 = (byte)WorldGen.snowBG, SetBG4 = (byte)WorldGen.hallowBG, SetBG5 = (byte)WorldGen.crimsonBG, SetBG6 = (byte)WorldGen.desertBG, SetBG7 = (byte)WorldGen.oceanBG, IceBackStyle = (byte)Main.iceBackStyle, JungleBackStyle = (byte)Main.jungleBackStyle, HellBackStyle = (byte)Main.hellBackStyle, WindSpeed = Main.windSpeed, NumberOfClouds = (byte)Main.numClouds, BossFlags = (WorldGen.shadowOrbSmashed ? TShockAPI.Net.BossFlags.OrbSmashed : TShockAPI.Net.BossFlags.None) | (NPC.downedBoss1 ? TShockAPI.Net.BossFlags.DownedBoss1 : TShockAPI.Net.BossFlags.None) | (NPC.downedBoss2 ? TShockAPI.Net.BossFlags.DownedBoss2 : TShockAPI.Net.BossFlags.None) | (NPC.downedBoss3 ? TShockAPI.Net.BossFlags.DownedBoss3 : TShockAPI.Net.BossFlags.None) | (Main.hardMode ? TShockAPI.Net.BossFlags.HardMode : TShockAPI.Net.BossFlags.None) | (NPC.downedClown ? TShockAPI.Net.BossFlags.DownedClown : TShockAPI.Net.BossFlags.None) | (Main.ServerSideCharacter ? TShockAPI.Net.BossFlags.ServerSideCharacter : TShockAPI.Net.BossFlags.None) | (NPC.downedPlantBoss ? TShockAPI.Net.BossFlags.DownedPlantBoss : TShockAPI.Net.BossFlags.None), BossFlags2 = (NPC.downedMechBoss1 ? TShockAPI.Net.BossFlags2.DownedMechBoss1 : TShockAPI.Net.BossFlags2.None) | (NPC.downedMechBoss2 ? TShockAPI.Net.BossFlags2.DownedMechBoss2 : TShockAPI.Net.BossFlags2.None) | (NPC.downedMechBoss3 ? TShockAPI.Net.BossFlags2.DownedMechBoss3 : TShockAPI.Net.BossFlags2.None) | (NPC.downedMechBossAny ? TShockAPI.Net.BossFlags2.DownedMechBossAny : TShockAPI.Net.BossFlags2.None) | (Main.cloudBGActive == 1f ? TShockAPI.Net.BossFlags2.CloudBg : TShockAPI.Net.BossFlags2.None) | (WorldGen.crimson ? TShockAPI.Net.BossFlags2.Crimson : TShockAPI.Net.BossFlags2.None) | (Main.pumpkinMoon ? TShockAPI.Net.BossFlags2.PumpkinMoon : TShockAPI.Net.BossFlags2.None), Rain = Main.maxRaining, WorldName = TShock.Config.UseServerName ? TShock.Config.ServerName : Main.worldName }; Console.Write(msg.BloodMoon); Console.Write(msg.DayTime); return(new RestObject() { { "bloodmoon", msg.BloodMoon } }); }
public static RestObject getStats(RestRequestArgs args) { LogStat rec; string sql = ""; if (!TShock.Config.UseSqlLogs) { return new RestObject("400") { Response = "UseSqlLogs not set to true.", } } ; DatabaseStat dbStat = new DatabaseStat(); if (TShock.DB.GetSqlType() == SqlType.Mysql) { dbStat.DBType = "MySQL"; sql = "SELECT SUM( data_length + index_length) / 1024 / 1024 as Size FROM information_schema.TABLES where table_schema = \"tshock\""; try { using (var reader = TShock.DB.QueryReader(sql)) { if (reader.Read()) { dbStat.DBSize = reader.Get <double>("Size"); } } } catch (Exception ex) { TShock.Log.Error(ex.ToString()); Console.WriteLine(ex.StackTrace); } sql = "SELECT table_rows as Rows, data_length, index_length, round(((data_length + index_length) / 1024 / 1024),2) as Size FROM information_schema.TABLES WHERE table_schema = \"tshock\" and table_name = \"logs\""; try { using (var reader = TShock.DB.QueryReader(sql)) { if (reader.Read()) { dbStat.TableRows = reader.Get <int>("Rows"); dbStat.TableSize = reader.Get <double>("Size"); } } } catch (Exception ex) { TShock.Log.Error(ex.ToString()); Console.WriteLine(ex.StackTrace); } sql = "select count(id) as Count, year(timestamp) as Year, month(TimeStamp) as Month from logs group by year(timestamp), month(TimeStamp)"; List <LogStat> Loglist = new List <LogStat>(); try { using (var reader = TShock.DB.QueryReader(sql)) { if (reader.Read()) { rec = new LogStat(reader.Get <Int32>("Count"), reader.Get <Int32>("Year"), reader.Get <Int32>("Month")); Loglist.Add(rec); } } return(new RestObject() { { "Rows", Loglist }, { "Stats", dbStat }, { "version", Assembly.GetExecutingAssembly().GetName().Version.ToString() } }); } catch (Exception ex) { TShock.Log.Error(ex.ToString()); Console.WriteLine(ex.StackTrace); } } else { dbStat.DBType = "SQLite"; dbStat.DBSize = 0; dbStat.TableRows = 0; dbStat.TableSize = 0; sql = "SELECT count(*) as Rows FROM Logs"; try { using (var reader = TShock.DB.QueryReader(sql)) { if (reader.Read()) { dbStat.TableRows = reader.Get <int>("Rows"); } } } catch (Exception ex) { TShock.Log.Error(ex.ToString()); Console.WriteLine(ex.StackTrace); } sql = "select count(id) as Count, strftime('%Y',timestamp) as Year, strftime('%Y',TimeStamp) as Month from logs group by strftime('%Y',timestamp), strftime('%Y',TimeStamp)"; List <LogStat> Loglist = new List <LogStat>(); try { using (var reader = TShock.DB.QueryReader(sql)) { if (reader.Read()) { rec = new LogStat(reader.Get <Int32>("Count"), Convert.ToInt32(reader.Get <string>("Year")), Convert.ToInt32(reader.Get <string>("Month"))); Loglist.Add(rec); } } return(new RestObject() { { "Rows", Loglist }, { "Stats", dbStat }, { "version", Assembly.GetExecutingAssembly().GetName().Version.ToString() } }); } catch (Exception ex) { TShock.Log.Error(ex.ToString()); Console.WriteLine(ex.StackTrace); } } return(null); }
internal static string BuildUrl(string url, RestRequestArgs args) { StringBuilder builder = new StringBuilder(); // remove the old query... int index = url.IndexOf("?"); if(index != -1) builder.Append(url.Substring(0, index)); else builder.Append(url); // add the arguments... if (args.Count > 0) { builder.Append("?"); // params... bool first = true; foreach (string key in args.Keys) { if (first) first = false; else builder.Append("&"); builder.Append(key); builder.Append("="); builder.Append(HttpUtility.UrlEncode(args[key])); } } // return... return builder.ToString(); }
public static RestObject updateConfig(RestRequestArgs args) { if (string.IsNullOrWhiteSpace(args.Parameters["config"])) { return new RestObject("400") { Response = "No config given" } } ; String config = args.Parameters["config"]; if (config == null) { return(new RestObject("400") { Response = "No config given" }); } if (string.IsNullOrWhiteSpace(args.Parameters["configFile"])) { return(RestMissingParam("configFile")); } String configFile = args.Parameters["configFile"]; if (configFile == null) { // Console.WriteLine(parameters["config"]); return(new RestObject("400") { Response = "No config file given" }); } JObject json = JObject.Parse(config); Dictionary <string, dynamic> configList = new Dictionary <string, dynamic> { }; String configFilePath = Path.Combine(TShock.SavePath, configFile); if (File.Exists(configFilePath)) { using (var sw = new StreamWriter(configFilePath)) { string j = JsonConvert.SerializeObject(json, Formatting.Indented); sw.Write(j); sw.Flush(); sw.Close(); } // File.WriteAllText(configFilePath, config); } else { Console.WriteLine(configFilePath); return(new RestObject("400") { Response = "Invalid file path" }); } return(new RestObject() { Response = "config.json saved." }); }
private object getPlayer(RestRequestArgs args) { string strName = args.Parameters["name"]; if (string.IsNullOrEmpty(strName)) { return new RestObject() { { "error", "expected player name in 'name'" } } } ; User usr = TShock.Users.GetUserByName(strName); if (usr == null) { return new RestObject() { { "error", "player not found" } } } ; List <TSPlayer> plrs = TShock.Utils.FindPlayer(strName); // Player is online, no need to fetch from DB if (plrs.Count == 1) { return new RestObject() { { "player", RelevantInfo.GetRelevance(plrs[0].TPlayer) } } } ; try { // Fetch from DB using (var reader = TShock.DB.QueryReader("SELECT * FROM tsCharacter where account =@0", usr.ID)) { if (reader.Read()) { RelevantInfo plr; foreach (var player in TShock.Players.Where(p => null != p && p.User.Name == usr.Name)) { // Some reason we didn't get the idea, lets use the real player if they're online plr = RelevantInfo.GetRelevance(player.TPlayer); plr.Name = usr.Name; plr.ID = usr.ID; return(new RestObject() { { "player", plr } }); } // Load from DB plr = RelevantInfo.GetRelevance(reader); plr.Name = usr.Name; plr.ID = usr.ID; return(new RestObject() { { "player", plr } }); } } } catch (Exception ex) { return(new RestObject() { { "error", ex.Message } }); } return(new RestObject() { { "error", "player not found" } }); }
object restNewTransaction(RestRequestArgs args) { var ret = UserFind(args.Parameters); if (ret is RestObject) { return(ret); } User user = (User)ret; if (String.IsNullOrWhiteSpace(args.Parameters["credits"])) { return(RestMissingParam("credits")); } float credits; if (!Single.TryParse(args.Parameters["credits"], out credits)) { return(RestInvalidParam("credits")); } long dateUnix = 0; if (!String.IsNullOrWhiteSpace(args.Parameters["date"])) { Int64.TryParse(args.Parameters["date"], out dateUnix); } Contributor con = _main.Contributors.Get(user.ID); bool success = false; if (con == null) { // Transactions must never be ignored. If the contributor doesn't exist, create it con = new Contributor(user); con.LastAmount = credits; if (dateUnix > 0) { con.LastDonation = dateUnix.FromUnixTime(); } con.Tier = 1; con.TotalCredits = credits; success = _main.Contributors.AddLocal(con); if (!success) { TShock.Log.ConsoleInfo($"CTRS-WARNING: Failed to register contribution made by user '{user.Name}'!"); } } else { ContributorUpdates updates = 0; con.LastAmount = credits; updates |= ContributorUpdates.LastAmount; if (dateUnix > 0) { con.LastDonation = dateUnix.FromUnixTime(); updates |= ContributorUpdates.LastDonation; } con.TotalCredits += credits; updates |= ContributorUpdates.TotalCredits; con.Notifications |= Notifications.NewDonation; // Always prompt a tier update check here con.Notifications |= Notifications.TierUpdate; updates |= ContributorUpdates.Notifications; success = _main.Contributors.Update(con, updates); } if (!success) { return(RestError("Transaction was not registered properly.")); } else { return(RestResponse("Transaction successful.")); } }