diff --git a/BetterSecondbot/OnEvents/onevent.cs b/BetterSecondbot/OnEvents/onevent.cs index 3df80c8..e61080e 100644 --- a/BetterSecondbot/OnEvents/onevent.cs +++ b/BetterSecondbot/OnEvents/onevent.cs @@ -7,6 +7,7 @@ using BetterSecondBotShared.Static; using Newtonsoft.Json; using OpenMetaverse; using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -44,6 +45,9 @@ namespace BetterSecondbot.OnEvents foreach(UUID a in purgeEntrys) { Lockout.Remove(a); + Hashtable parms = new Hashtable(); + parms.Add("uuid", a); + controler.getBot().DeleteSQL("DELETE FROM `lockout` WHERE `lockout`.`uuid` = @uuid;", parms); } } } @@ -138,11 +142,20 @@ namespace BetterSecondbot.OnEvents } if (UUID.TryParse(subbits[0],out UUID addUUID) == true) { - if(Lockout.ContainsKey(addUUID) == true) + Hashtable parms = new Hashtable(); + if (Lockout.ContainsKey(addUUID) == true) { Lockout.Remove(addUUID); + parms = new Hashtable(); + parms.Add("uuid", addUUID); + controler.getBot().DeleteSQL("DELETE FROM `lockout` WHERE `lockout`.`uuid` = @uuid;", parms); } - Lockout.Add(addUUID, helpers.UnixTimeNow() + addsecs); + long expires = helpers.UnixTimeNow() + addsecs; + Lockout.Add(addUUID, expires); + parms = new Hashtable(); + parms.Add("uuid", addUUID); + parms.Add("expires", expires); + controler.getBot().InsertSQL("INSERT INTO `lockout` (`id`, `uuid`, `expires`) VALUES(NULL, @uuid, @expires))", parms); } } } @@ -894,6 +907,42 @@ namespace BetterSecondbot.OnEvents { TriggerEvent("Clock", new Dictionary(), true); } + + protected void LoadFromDB() + { + // loads locks from SQL if enabled. + if (controler.getBot().getMyConfig.SQLDB_Enabled == false) + { + return; // :( + } + + Hashtable parms = new Hashtable(); + parms.Add("unixtime", helpers.UnixTimeNow()); + controler.getBot().DeleteSQL("DELETE FROM `lockout` WHERE `lockout`.`expires` < @unixtime;", parms); // purge old entrys + List> dataRead = controler.getBot().SelectSQL("SELECT uuid, expires FROM `lockout` LIMIT 0, 9999", new string[] { "uuid", "expires" }); + if(dataRead != null) + { + controler.getBot().Info("Loading lockouts from DB"); + foreach(Dictionary entry in dataRead) + { + if(long.TryParse(entry["expires"], out long expires) == true) + { + if(UUID.TryParse(entry["uuid"], out UUID uuid) == true) + { + if(Lockout.ContainsKey(uuid) == false) + { + Lockout.Add(uuid, 0); + } + if (Lockout[uuid] < expires) + { + Lockout[uuid] = expires; + } + } + } + } + } + } + bool attachedEvents = false; protected void statusMessage(object o, StatusMessageEvent e) { @@ -902,6 +951,7 @@ namespace BetterSecondbot.OnEvents if(attachedEvents == false) { attachedEvents = true; + LoadFromDB(); SetupMonitor(); attachEvents(); } @@ -982,5 +1032,12 @@ namespace BetterSecondbot.OnEvents } + class LockoutDBFormat + { + public int id; + public string uuid; + public int expires; + } + } diff --git a/Core/Commands/Self.cs b/Core/Commands/Self.cs index 8a5c1d7..6a66104 100644 --- a/Core/Commands/Self.cs +++ b/Core/Commands/Self.cs @@ -42,6 +42,54 @@ namespace BetterSecondBot.HttpService return BasicReply("ok", "PointAt", new [] { avatar }); } + [About("Reads a value from the KeyValue storage (temp unless SQL is enabled)")] + [ReturnHints("value")] + [ReturnHints("Unknown Key: KeyName")] + [ArgHints("Key", "URLARG", "the key we are trying to read from")] + [Route(HttpVerbs.Get, "/ReadKeyValue/{Key}/{token}")] + public object ReadKeyValue(string Key, string token) + { + if (tokens.Allow(token, "self", "ReadKeyValue", handleGetClientIP()) == false) + { + return Failure("Token not accepted", "ReadKeyValue", new[] { Key }); + } + if(bot.HaveLocalKeyStorage(Key) == true) + { + return BasicReply(bot.ReadLocalKeyStorage(Key), "ReadKeyValue"); + } + return Failure("Unknown Key: "+Key, "ReadKeyValue"); + } + + [About("sets a value for KeyValue storage (temp unless SQL is enabled)")] + [ReturnHints("ok")] + [ArgHints("Key", "URLARG", "the key we are trying to set")] + [ArgHints("Value", "string", "the value we are tring to put on the key")] + [Route(HttpVerbs.Post, "/SetKeyValue/{Key}/{token}")] + public object SetKeyValue(string Key, [FormField] string Value, string token) + { + if (tokens.Allow(token, "self", "SetKeyValue", handleGetClientIP()) == false) + { + return Failure("Token not accepted", "SetKeyValue", new[] { Key }); + } + bot.setLocalKeyStorage(Key, Value); + return BasicReply("ok", "SetKeyValue"); + } + + [About("Reads a value from the KeyValue storage (temp unless SQL is enabled)")] + [ReturnHints("ok")] + [ReturnHints("Unknown Key: KeyName")] + [ArgHints("Key", "URLARG", "the key we are trying to clear")] + [Route(HttpVerbs.Get, "/ClearKeyValue/{Key}/{token}")] + public object ClearKeyValue(string Key, string token) + { + if (tokens.Allow(token, "self", "ClearKeyValue", handleGetClientIP()) == false) + { + return Failure("Token not accepted", "ClearKeyValue", new[] { Key }); + } + bot.clearLocalKeyStorage(Key); + return BasicReply("ok", "SetKeyValue"); + } + [About("Makes the bot sit on the ground or on a object if it can see it")] [ReturnHints("ok")] [ReturnHints("Invaild object UUID")] diff --git a/Core/Core.csproj b/Core/Core.csproj index 1459662..87a8ab2 100644 --- a/Core/Core.csproj +++ b/Core/Core.csproj @@ -10,6 +10,7 @@ + diff --git a/Core/bottypes/CommandsBot.cs b/Core/bottypes/CommandsBot.cs index 6696d7a..7526ae4 100644 --- a/Core/bottypes/CommandsBot.cs +++ b/Core/bottypes/CommandsBot.cs @@ -15,7 +15,7 @@ using System.Net.Http; namespace BetterSecondBot.bottypes { - public abstract class CommandsBot : AVstorageBot + public abstract class CommandsBot : StorageBot { protected List CommandHistory = new List(); protected int commandid = 1; diff --git a/Core/bottypes/AVstorageBot.cs b/Core/bottypes/StorageBot.cs similarity index 70% rename from Core/bottypes/AVstorageBot.cs rename to Core/bottypes/StorageBot.cs index 870df10..916c12b 100644 --- a/Core/bottypes/AVstorageBot.cs +++ b/Core/bottypes/StorageBot.cs @@ -1,16 +1,18 @@ -using BetterSecondBotShared.logs; +using BetterSecondBotShared.logs; using BetterSecondBotShared.Static; using Newtonsoft.Json; using OpenMetaverse; using RestSharp; using System; +using System.Collections; using System.Collections.Generic; +using System.Data.SqlClient; using System.Linq; using System.Text; namespace BetterSecondBot.bottypes { - public abstract class AVstorageBot : AttachableBot + public abstract class StorageBot : AttachableBot { protected UUID master_uuid = UUID.Zero; public UUID getMaster_uuid { get { return master_uuid; } } @@ -27,6 +29,191 @@ namespace BetterSecondBot.bottypes protected string AVstorageBot_laststatus = ""; + protected Dictionary LocalKeyValueStorage = new Dictionary(); + + public bool HaveLocalKeyStorage(string key) + { + return LocalKeyValueStorage.ContainsKey(key); + } + + public string ReadLocalKeyStorage(string key) + { + lock (LocalKeyValueStorage) + { + if (LocalKeyValueStorage.ContainsKey(key) == true) + { + return LocalKeyValueStorage[key]; + } + return null; + } + } + + public void setLocalKeyStorage(string key, string value) + { + lock (LocalKeyValueStorage) + { + if (LocalKeyValueStorage.ContainsKey(key) == false) + { + LocalKeyValueStorage.Add(key, null); + } + LocalKeyValueStorage[key] = value; + Hashtable T = new Hashtable(); + T.Add("keyname", key); + T.Add("keyvalue", value); + InsertSQL("INSERT INTO `keypairstorage` (`id`, `name`, `value`) VALUES (NULL, @keyname, @keyvalue)", T); + } + } + + public void clearLocalKeyStorage(string key) + { + lock (LocalKeyValueStorage) + { + if (LocalKeyValueStorage.ContainsKey(key) == true) + { + LocalKeyValueStorage.Remove(key); + Hashtable T = new Hashtable(); + T.Add("keyname", key); + DeleteSQL("DELETE FROM `keypairstorage` WHERE `keypairstorage`.`name` = @keyname", T); + } + } + } + + public int InsertSQL(string sql) + { + return InsertSQL(sql, new Hashtable()); + } + public int InsertSQL(string sql, Hashtable hashtable) + { + if (myconfig.SQLDB_Enabled == false) + { + return 0; + } + try + { + SqlCommand command = new SqlCommand(sql, sqlClient); + foreach (DictionaryEntry s in hashtable) + { + command.Parameters.AddWithValue("@" + s.Key, s.Value); + } + return command.ExecuteNonQuery(); + } + catch (Exception e) + { + Warn("SQL going away: " + e.Message); + sqlStatus = false; + attemptedSQL = true; + return 0; + } + } + + public int DeleteSQL(string sql) + { + return DeleteSQL(sql, new Hashtable()); + } + public int DeleteSQL(string sql, Hashtable hashtable) + { + if (myconfig.SQLDB_Enabled == false) + { + return 0; + } + if (SqlAvailable() == false) + { + return 0; + } + try + { + SqlCommand command = new SqlCommand(sql, sqlClient); + foreach (DictionaryEntry s in hashtable) + { + command.Parameters.AddWithValue("@" + s.Key, s.Value); + } + return command.ExecuteNonQuery(); + } + catch (Exception e) + { + Warn("SQL going away: " + e.Message); + sqlStatus = false; + attemptedSQL = true; + return 0; + } + + } + + public List> SelectSQL(string sql, string[] fields) + { + return SelectSQL(sql, fields, new Hashtable()); + } + public List> SelectSQL(string sql, string[] fields, Hashtable hashtable) + { + if(myconfig.SQLDB_Enabled == false) + { + return null; + } + if(SqlAvailable() == false) + { + return null; + } + try + { + SqlCommand command = new SqlCommand(sql, sqlClient); + foreach (DictionaryEntry s in hashtable) + { + command.Parameters.AddWithValue("@" + s.Key, s.Value); + } + List> reply = new List>(); + using (SqlDataReader reader = command.ExecuteReader()) + { + Dictionary entry = new Dictionary(); + foreach (string A in fields) + { + entry.Add(A, (string)reader[A]); + } + reply.Add(entry); + } + return reply; + } + catch (Exception e) + { + Warn("SQL going away: " + e.Message); + sqlStatus = false; + attemptedSQL = true; + return null; + } + } + + + protected bool attemptedSQL = false; + protected bool sqlStatus = false; + protected SqlConnection sqlClient; + protected bool SqlAvailable() + { + if(attemptedSQL == true) + { + return sqlStatus; + } + try + { + attemptedSQL = true; + sqlClient = new SqlConnection( + "Data Source=" + myconfig.SQLDB_Host + ":" + myconfig.SQLDB_Port.ToString() + ";" + + "Initial Catalog=" + myconfig.SQLDB_Database + ";" + + "Persist Security Info=True;" + + "User ID=" + myconfig.SQLDB_Username + ";" + + "Password=" + myconfig.SQLDB_Password + ";" + + "Connection Timeout=" + myconfig.SQLDB_Timeout.ToString() + ); + sqlStatus = true; + return true; + } + catch (Exception E) + { + Crit("SQL connection failed: " + E.Message); + return false; + } + + } + + protected Dictionary FriendNames = new Dictionary(); protected override void AvatarFriendNames(object o, FriendNamesEventArgs E) { @@ -104,37 +291,37 @@ namespace BetterSecondBot.bottypes protected void ReTriggerUUIDtoName() { - lock (PendingAvatarFinds_viauuid) - { - long now = helpers.UnixTimeNow(); - List retrigger_key2name = new List(); - List giveup_key2name = new List(); - foreach (KeyValuePair> pair in PendingAvatarFinds_viauuid) - { - long dif = now - pair.Value.Key; - if (dif > 30) - { - if (pair.Value.Value < max_retry_attempts) - { - retrigger_key2name.Add(pair.Key); - } - else - { - giveup_key2name.Add(pair.Key); - } - } - } - if (retrigger_key2name.Count > 0) - { - foreach (UUID avuuid in retrigger_key2name) - { - PendingAvatarFinds_viauuid[avuuid] = new KeyValuePair(now, PendingAvatarFinds_viauuid[avuuid].Value + 1); - } - Client.Avatars.RequestAvatarNames(retrigger_key2name); - } - foreach (UUID avuuid in giveup_key2name) - { - PendingAvatarFinds_viauuid.Remove(avuuid); + lock (PendingAvatarFinds_viauuid) + { + long now = helpers.UnixTimeNow(); + List retrigger_key2name = new List(); + List giveup_key2name = new List(); + foreach (KeyValuePair> pair in PendingAvatarFinds_viauuid) + { + long dif = now - pair.Value.Key; + if (dif > 30) + { + if (pair.Value.Value < max_retry_attempts) + { + retrigger_key2name.Add(pair.Key); + } + else + { + giveup_key2name.Add(pair.Key); + } + } + } + if (retrigger_key2name.Count > 0) + { + foreach (UUID avuuid in retrigger_key2name) + { + PendingAvatarFinds_viauuid[avuuid] = new KeyValuePair(now, PendingAvatarFinds_viauuid[avuuid].Value + 1); + } + Client.Avatars.RequestAvatarNames(retrigger_key2name); + } + foreach (UUID avuuid in giveup_key2name) + { + PendingAvatarFinds_viauuid.Remove(avuuid); } } } @@ -224,10 +411,10 @@ namespace BetterSecondBot.bottypes { Client.Avatars.UUIDNameReply += Key2NameEvent; Client.Directory.DirPeopleReply += Name2KeyEvent; - } - if (helpers.notempty(myconfig.Security_MasterUsername) == true) - { - FindAvatarName2Key(myconfig.Security_MasterUsername); + } + if (helpers.notempty(myconfig.Security_MasterUsername) == true) + { + FindAvatarName2Key(myconfig.Security_MasterUsername); } base.AfterBotLoginHandler(); } @@ -251,32 +438,32 @@ namespace BetterSecondBot.bottypes { if ((av_name != "null") && (av_name != null) && (av_name.ToLowerInvariant() != "secondlife") && (av_uuid != UUID.Zero)) { - lock (AvatarStorageLastUsed) lock (AvatarKey2Name) lock (AvatarName2Key) lock (PendingAvatarFinds_viauuid) lock (PendingAvatarFinds_vianame) - { - if (AvatarStorageLastUsed.ContainsKey(av_name) == false) - { - if (av_uuid != UUID.Zero) - { - if ((AvatarKey2Name.ContainsKey(av_uuid) == false) && (AvatarName2Key.ContainsKey(av_name) == false)) - { - AvatarName2Key.Add(av_name, av_uuid); - AvatarKey2Name.Add(av_uuid, av_name); - } - AvatarStorageLastUsed.Add(av_name, helpers.UnixTimeNow()); - if (PendingAvatarFinds_vianame.ContainsKey(av_name) == true) - { - PendingAvatarFinds_vianame.Remove(av_name); - } - if (PendingAvatarFinds_viauuid.ContainsKey(av_uuid) == true) - { - PendingAvatarFinds_viauuid.Remove(av_uuid); - } - } - } - else - { - AvatarStorageLastUsed[av_name] = helpers.UnixTimeNow(); - } + lock (AvatarStorageLastUsed) lock (AvatarKey2Name) lock (AvatarName2Key) lock (PendingAvatarFinds_viauuid) lock (PendingAvatarFinds_vianame) + { + if (AvatarStorageLastUsed.ContainsKey(av_name) == false) + { + if (av_uuid != UUID.Zero) + { + if ((AvatarKey2Name.ContainsKey(av_uuid) == false) && (AvatarName2Key.ContainsKey(av_name) == false)) + { + AvatarName2Key.Add(av_name, av_uuid); + AvatarKey2Name.Add(av_uuid, av_name); + } + AvatarStorageLastUsed.Add(av_name, helpers.UnixTimeNow()); + if (PendingAvatarFinds_vianame.ContainsKey(av_name) == true) + { + PendingAvatarFinds_vianame.Remove(av_name); + } + if (PendingAvatarFinds_viauuid.ContainsKey(av_uuid) == true) + { + PendingAvatarFinds_viauuid.Remove(av_uuid); + } + } + } + else + { + AvatarStorageLastUsed[av_name] = helpers.UnixTimeNow(); + } } if (helpers.notempty(myconfig.Security_MasterUsername) == true) { @@ -329,12 +516,12 @@ namespace BetterSecondBot.bottypes { return uuid; } - lock (PendingAvatarFinds_vianame) - { - if (PendingAvatarFinds_vianame.ContainsKey(avatar_name) == false) - { - PendingAvatarFinds_vianame.Add(avatar_name, new KeyValuePair(helpers.UnixTimeNow(), 0)); - Client.Directory.StartPeopleSearch(avatar_name, 0); + lock (PendingAvatarFinds_vianame) + { + if (PendingAvatarFinds_vianame.ContainsKey(avatar_name) == false) + { + PendingAvatarFinds_vianame.Add(avatar_name, new KeyValuePair(helpers.UnixTimeNow(), 0)); + Client.Directory.StartPeopleSearch(avatar_name, 0); } } return "lookup"; @@ -362,14 +549,14 @@ namespace BetterSecondBot.bottypes if (avatar_name != "") { return avatar_name; - } - lock (PendingAvatarFinds_viauuid) - { - if (PendingAvatarFinds_viauuid.ContainsKey(avatar_uuid) == false) - { - PendingAvatarFinds_viauuid.Add(avatar_uuid, new KeyValuePair(helpers.UnixTimeNow(), 0)); - Client.Avatars.RequestAvatarName(avatar_uuid); - } + } + lock (PendingAvatarFinds_viauuid) + { + if (PendingAvatarFinds_viauuid.ContainsKey(avatar_uuid) == false) + { + PendingAvatarFinds_viauuid.Add(avatar_uuid, new KeyValuePair(helpers.UnixTimeNow(), 0)); + Client.Avatars.RequestAvatarName(avatar_uuid); + } } return "lookup"; } diff --git a/Dockerfile b/Dockerfile index eb78233..5b61736 100644 --- a/Dockerfile +++ b/Dockerfile @@ -47,6 +47,13 @@ ENV Log2File_Level='1' ENV Setting_LogCommands='false' ENV Agent_Channel='auto' ENV Agent_Version='auto' +ENV SQLDB_Enabled='false' +ENV SQLDB_Host='127.0.0.1' +ENV SQLDB_Port='3306' +ENV SQLDB_Username='' +ENV SQLDB_Password='' +ENV SQLDB_Timeout='3' +ENV SQLDB_Database='secondbot' EXPOSE 80 ENV ASPNETCORE_URLS http://+:80 diff --git a/Shared/Json/JsonConfig.cs b/Shared/Json/JsonConfig.cs index dc44cbf..7cff3f4 100644 --- a/Shared/Json/JsonConfig.cs +++ b/Shared/Json/JsonConfig.cs @@ -3,8 +3,8 @@ using System.Collections.Generic; using System.Text; namespace BetterSecondBotShared.Json -{ - +{ + public class JsonScopedTokens { public string[] ScopedTokens { get; set; } @@ -15,13 +15,13 @@ namespace BetterSecondBotShared.Json public string[] CustomCommands { get; set; } } - public class JsonCustomRelaysSet - { - public JsonCustomRelays[] Entrys {get; set;} + public class JsonCustomRelaysSet + { + public JsonCustomRelays[] Entrys {get; set;} } - public class JsonCustomRelays - { + public class JsonCustomRelays + { public bool encodeJson = false; public string sourceType = ""; public string sourceFilter = ""; @@ -29,37 +29,37 @@ namespace BetterSecondBotShared.Json public string targetConfig = ""; } - public class advertConfig - { - public string title; - public string attachment; - public string content; - public string notice; - public string enabled; - public string[] groups; - public string days; - public string hour; - public string min; - } - - public class advertsBlob - { - public advertConfig[] adverts = new advertConfig[] { }; + public class advertConfig + { + public string title; + public string attachment; + public string content; + public string notice; + public string enabled; + public string[] groups; + public string days; + public string hour; + public string min; } - public class OnEvent - { - public string title; - public bool Enabled; - public string On; - public string Monitor; - public string[] Where; - public string[] Actions; - } - - public class OnEventBlob - { - public OnEvent[] listEvents = new OnEvent[] { }; + public class advertsBlob + { + public advertConfig[] adverts = new advertConfig[] { }; + } + + public class OnEvent + { + public string title; + public bool Enabled; + public string On; + public string Monitor; + public string[] Where; + public string[] Actions; + } + + public class OnEventBlob + { + public OnEvent[] listEvents = new OnEvent[] { }; } @@ -101,13 +101,21 @@ namespace BetterSecondBotShared.Json // Logs public bool Log2File_Enable { get; set; } public int Log2File_Level { get; set; } - public bool Setting_LogCommands { get; set; } - - // agent + public bool Setting_LogCommands { get; set; } + + // agent public string Agent_Channel { get; set; } public string Agent_Version { get; set; } - + // Database + + public bool SQLDB_Enabled { get; set; } + public string SQLDB_Host { get; set; } + public int SQLDB_Port { get; set; } + public string SQLDB_Username { get; set; } + public string SQLDB_Password { get; set; } + public int SQLDB_Timeout { get; set; } + public string SQLDB_Database { get; set; } } } diff --git a/Shared/Json/JsonConfigApi.cs b/Shared/Json/JsonConfigApi.cs index 80480f8..3121684 100644 --- a/Shared/Json/JsonConfigApi.cs +++ b/Shared/Json/JsonConfigApi.cs @@ -106,6 +106,39 @@ namespace BetterSecondBotShared.Json } return ""; } + + protected string GetHelp_SQLDB(string cmd) + { + if (cmd == "SQLDB_Enabled") + { + return "Allow the bot to use mysql for keypair storage and guest logging."; + } + else if (cmd == "SQLDB_Host") + { + return "The host for the SQL server"; + } + else if (cmd == "SQLDB_Port") + { + return "The port for the SQL server"; + } + else if (cmd == "SQLDB_Username") + { + return "The username for the SQL server"; + } + else if (cmd == "SQLDB_Password") + { + return "The password for the SQL server the SQL server"; + } + else if (cmd == "SQLDB_Timeout") + { + return "How long to allow a connection to wait for before giving up in secs"; + } + else if(cmd == "SQLDB_Database") + { + return "The name of the database to use"; + } + return ""; + } protected string GetHelp_HTTP(string cmd) { // HTTP @@ -150,6 +183,7 @@ namespace BetterSecondBotShared.Json else if (cmd.StartsWith("Setting") == true) { return GetHelp_Settings(cmd); } else if (cmd.StartsWith("Discord") == true) { return GetHelp_Discord(cmd); } else if (cmd.StartsWith("Http_") == true) { return GetHelp_HTTP(cmd); } + else if (cmd.StartsWith("SQLDB_") == true) { return GetHelp_SQLDB(cmd); } else { return GetHelp_Logs(cmd); } } @@ -220,6 +254,39 @@ namespace BetterSecondBotShared.Json else if (cmd == "DiscordFull_Keep_GroupChat") { return "False"; } return ""; } + + protected string GetValueHint_SQL(string cmd) + { + if (cmd == "SQLDB_Enabled") + { + return "False"; + } + else if (cmd == "SQLDB_Host") + { + return "127.0.0.1"; + } + else if (cmd == "SQLDB_Port") + { + return "3306"; + } + else if (cmd == "SQLDB_Username") + { + return "UserName"; + } + else if (cmd == "SQLDB_Password") + { + return "PassWord"; + } + else if (cmd == "SQLDB_Timeout") + { + return "4"; + } + else if (cmd == "SQLDB_Database") + { + return "secondbot"; + } + return ""; + } protected string GetValueHint_HTTP(string cmd) { // HTTP @@ -246,6 +313,7 @@ namespace BetterSecondBotShared.Json else if (cmd.StartsWith("Setting") == true) { return GetValueHint_Settings(cmd); } else if (cmd.StartsWith("Discord") == true) { return GetValueHint_Discord(cmd); } else if (cmd.StartsWith("Http_") == true) { return GetValueHint_HTTP(cmd); } + else if(cmd.StartsWith("SQLDB_") == true) { return GetValueHint_SQL(cmd); } else { return GetValueHint_Logs(cmd); } } public override int GetCommandArgs(string cmd)