public ResponseStruct AddUser(string UserName, string Password, string GroupName)
        {
            ResponseStruct Result;

            TShockAPI.DB.User user = new TShockAPI.DB.User();

            user.Name = UserName;
            user.Group = GroupName;

            try
            {
                user.CreateBCryptHash(Password);
            }
            catch (ArgumentOutOfRangeException)
            {
                Result = new ResponseStruct() { Status = ResponseStatusType.Fail, Error = new List<string>() { "Mật khẩu không đủ dài." }, Broadcast = new List<string>() { String.Format("Mật khẩu phải bằng hoặc dài hơn {0} kí tự.", TShockAPI.TShock.Config.MinimumPasswordLength) } };
                return Result;
            }

            if (TShockAPI.TShock.Groups.GetGroupByName(user.Group) == null)
            {
                Result = new ResponseStruct() { Status = ResponseStatusType.Fail, Error = new List<string>() { "\"Group\" không tồn tại." }, Broadcast = new List<string>() { "Sử dụng \"Group\" hợp lệ." } };
            }
            else
            {
                if (TShockAPI.TShock.Users.GetUserByName(user.Name) == null && user.Name != TShockAPI.TSServerPlayer.AccountName)
                {
                    try
                    {
                        TShockAPI.TShock.Users.AddUser(user);
                        Result = new ResponseStruct() { Status = ResponseStatusType.Done, Broadcast = new List<string>() { "Tài khoản đã được khởi tạo." } };
                    }
                    catch (TShockAPI.DB.UserManagerException e)
                    {
                        Result = new ResponseStruct() { Status = ResponseStatusType.Fail, Error = new List<string>() { e.Message }, Broadcast = new List<string>() { "Báo cáo với Admin nếu có thể." } };
                    }
                }
                else
                {
                    Result = new ResponseStruct() { Status = ResponseStatusType.Fail, Error = new List<string>() { "Tài khoản đã tồn tại." }, Broadcast = new List<string>() { "Sử dụng một tên tài khoản khác." } };
                }
            }

            return Result;
        }
Example #2
0
 public void CreateAccount(Models.RootObject a)
 {
     for (int i = 0; i <= (a.docs.Count - 1); i++)
     {
         var Doc = a.docs[i];
         if (Doc.item.ToString().Contains("Join"))
         {
             var account = new TShockAPI.DB.User();
             account.Name  = Doc.redeemer.username;
             account.Group = TShock.Config.DefaultGuestGroupName;
             string password = String.Join(" ", Doc.input.ToArray());
             //Check for account already
             if (TShock.Users.GetUserByName(account.Name) == null)
             {
                 Console.WriteLine(account.Name + "User Does Not Exist");
                 //make the account
                 TShock.Users.AddUser(account);
             }
             //assign the password
             var regex = new Regex("^[a-zA-Z0-9]*$");
             if (password.Length > TShock.Config.MinimumPasswordLength)
             {
                 if (regex.IsMatch(password))
                 {
                     TShock.Users.SetUserPassword(account, password);
                     Console.WriteLine(account.Name + " has changed their password.");
                 }
                 else
                 {
                     Console.WriteLine("Password was shit.  Please try alphanumerical only.");
                 }
             }
             // Mark the redemption complete.
             MarkStreamElementsRedemptionComplete(Doc);
         }
         else
         {
             Console.WriteLine("Redemption list does not contain Join.");
         }
     }
 }
Example #3
0
        public static bool MatchUserByPlayerName(string playerName, out TShockAPI.DB.User user, TSPlayer messagesReceiver = null)
        {
            user = null;
            TShockAPI.DB.User tsUser = TShock.Users.GetUserByName(playerName);
            if (tsUser == null)
            {
                TSPlayer player;
                if (!TShockEx.MatchPlayerByName(playerName, out player, messagesReceiver))
                {
                    return(false);
                }

                user = TShock.Users.GetUserByID(player.UserID);
            }
            else
            {
                user = tsUser;
            }

            return(true);
        }
Example #4
0
        public static bool MatchUserAccountNameByPlayerName(string playerName, out string exactName, TSPlayer messagesReceiver = null)
        {
            exactName = null;
            TShockAPI.DB.User tsUser = TShock.Users.GetUserByName(playerName);
            if (tsUser == null)
            {
                TSPlayer player;
                if (!TShockEx.MatchPlayerByName(playerName, out player, messagesReceiver))
                {
                    return(false);
                }

                exactName = player.UserAccountName;
            }
            else
            {
                exactName = tsUser.Name;
            }

            return(true);
        }
Example #5
0
        public static bool IsSafe(DBInfo info, TShockAPI.DB.User acct = null)
        {
            // Copy account.
            if (acct == null)
            {
                acct = TShock.Users.GetUserByID(info.UserID);
            }

            // I love these long config value names.
            if (Config.ProtectPurgeIfLongtimeUser &&
                info.TotalTime > Config.PurgeAfterInactiveTime ||
                (TShock.Groups.GetGroupByName(acct.Group)
                 .HasPermission(Config.PurgeProtectionPermission)) ||
                (Config.EnableTimeBasedPurges && Config.PurgeAfterInactiveTime
                 < DateTime.Now - info.LastLogin) ||
                Config.WhitelistAccounts.Contains(acct.Name))
            {
                return(false);
            }

            return(true);
        }
Example #6
0
 public BridgeUser(TShockAPI.DB.User user, Discord.User discordUser) : this(user)
 {
     DiscordUser = discordUser;
     IsLoggedIn  = true;
 }
Example #7
0
 private BridgeUser(TShockAPI.DB.User user) : base(user.Name)
 {
     Group = TShock.Utils.GetGroup(user.Group);
     User  = user;
 }
        public ResponseStruct ChangeUserGroup(string UserName, string GroupName)
        {
            ResponseStruct Result;

            try
            {
                TShockAPI.DB.User aUser = new TShockAPI.DB.User();
                aUser.Name = UserName;

                TShockAPI.TShock.Users.SetUserGroup(aUser, GroupName);
                Result = new ResponseStruct() { Status = ResponseStatusType.Done };
            }
            catch (Exception ex)
            {
                Result = new ResponseStruct() { Status = ResponseStatusType.Fail, Error = new List<string>() { ex.Message } };
            }

            return Result;
        }
Example #9
0
        internal static void Initialize()
        {
            CmdAliasPlugin.scriptEngine = new Jint.JintEngine();
            CmdAliasPlugin.scriptEngine.DisableSecurity();
            CmdAliasPlugin.scriptEngine.SetDebugMode(true);

            ///function seconomy_transfer_async(toAccount : Journal.XBankAccount, player : TShockAPI.TSPlayer, amount : string, completedCallback : function)
            ///
            ///Asynchronously transfers money using SEconomy, then calls back to the JS function specified by completedCallback
            CmdAliasPlugin.scriptEngine.SetFunction("seconomy_transfer_async", new TransferAsyncDelegate((from, to, amount, msg, func) => {
                from.TransferToAsync(to, amount, Journal.BankAccountTransferOptions.AnnounceToSender, Message: msg).ContinueWith((task) => {
                    //callback to the JS function with the result of the transfer
                    CmdAliasPlugin.scriptEngine.CallFunction(func, task.Result);
                });
            }));

            CmdAliasPlugin.scriptEngine.SetFunction("seconomy_pay_async", new TransferAsyncDelegate((from, to, amount, msg, func) => {
                from.TransferToAsync(to, amount, Journal.BankAccountTransferOptions.AnnounceToReceiver | Journal.BankAccountTransferOptions.AnnounceToSender | Journal.BankAccountTransferOptions.IsPayment, Message: msg).ContinueWith((task) => {
                    //callback to the JS function with the result of the transfer
                    CmdAliasPlugin.scriptEngine.CallFunction(func, task.Result);
                });
            }));

            CmdAliasPlugin.scriptEngine.SetFunction("seconomy_parse_money", new Func <object, Money>((moneyString) => {
                return(Money.Parse(moneyString.ToString()));
            }));


            CmdAliasPlugin.scriptEngine.SetFunction("seconomy_get_account", new Func <object, Journal.XBankAccount>((accountName) => {
                if (accountName is TShockAPI.TSPlayer)
                {
                    return(SEconomyPlugin.GetEconomyPlayerSafe((accountName as TShockAPI.TSPlayer).Name).BankAccount);
                }
                else
                {
                    return(SEconomyPlugin.GetEconomyPlayerSafe(accountName.ToString()).BankAccount);
                }
            }));


            ///global variable: __world_account
            ///
            ///Returns a reference to the SEconomy world account
            CmdAliasPlugin.scriptEngine.SetFunction("seconomy_world_account", new Func <Journal.XBankAccount>(() => {
                return(SEconomyPlugin.WorldAccount);
            }));

            ///function create_alias(aliasName : string, commandCost : string, cooldownSeconds : integer, permissionsNeeded : string, functionToExecute : function)
            ///
            ///Creates an alias that executes the specified functionToExecute in js when it is called by a player/server.
            CmdAliasPlugin.scriptEngine.SetFunction("create_alias", new RegisterCommandDelegate((aliasname, cost, cooldown, perms, func) => {
                JScriptAliasCommand jAlias = new JScriptAliasCommand()
                {
                    CommandAlias = aliasname, CooldownSeconds = Convert.ToInt32(cooldown), Cost = cost, Permissions = perms, func = func
                };

                jsAliases.RemoveAll(i => i.CommandAlias == aliasname);
                jsAliases.Add(jAlias);
            }));

            /// function log(logEntry : string)
            ///
            ///Writes logEntry to the TShock log
            CmdAliasPlugin.scriptEngine.SetFunction <string>("log", (logText) => {
                TShockAPI.Log.ConsoleInfo(logText);
            });

            ///function get_player(playerName : string) : TShockAPI.TSPlayer
            ///
            ///Returns a TSPlayer by their characterName.  Returns undefined if the player isn't found or is offline.
            CmdAliasPlugin.scriptEngine.SetFunction("get_player", new Func <string, TShockAPI.TSPlayer>((name) => {
                return(TShockAPI.TShock.Players.FirstOrDefault(i => i.Name == name));
            }));

            ///function random(from : integer, to : integer) : integer
            ///
            ///Returns a random number between from and to.
            CmdAliasPlugin.scriptEngine.SetFunction("random", new Func <double, double, double>((rndFrom, rndTo) => {
                lock (__rndLock) {
                    int from = Convert.ToInt32(rndFrom);
                    int to   = Convert.ToInt32(rndTo);
                    return(randomGenerator.Next(from, to));
                }
            }));

            //function group_exists(groupName : string) : boolean
            //
            //Returns whether the TShock group specified by groupName exists
            CmdAliasPlugin.scriptEngine.SetFunction("group_exists", new Func <object, bool>((groupName) => {
                return(TShockAPI.TShock.Groups.Count(i => i.Name.Equals(groupName.ToString(), StringComparison.CurrentCultureIgnoreCase)) > 0);
            }));

            //tshock_group(groupName : string) : TShockAPI.Group
            //
            //Returns a TShock Group object by its name
            CmdAliasPlugin.scriptEngine.SetFunction("tshock_group", new Func <object, TShockAPI.Group>((groupName) => {
                if (groupName == null)
                {
                    return(null);
                }

                TShockAPI.Group g = TShockAPI.TShock.Groups.FirstOrDefault(i => i.Name.Equals(groupName.ToString(), StringComparison.CurrentCultureIgnoreCase));
                return(g);
            }));

            //function execute_command(player : TShockAPI.TSPlayer, command : string)
            //
            //causes player to execute provided command in the TShock execution handler.
            //this function ignores permissions, and will always execute as though the user has permissions to do it.
            CmdAliasPlugin.scriptEngine.SetFunction("execute_command", new Action <TShockAPI.TSPlayer, object>((player, cmd) => {
                string commandToExecute = "";
                if (cmd is List <string> )
                {
                    List <string> cmdList = cmd as List <string>;

                    foreach (var param in cmdList.Skip(1))
                    {
                        commandToExecute += " " + param;
                    }
                }
                else if (cmd is string)
                {
                    commandToExecute = cmd.ToString();
                }

                CmdAliasPlugin.HandleCommandWithoutPermissions(player, string.Format("{0}", commandToExecute.Trim()));
            }));

            //function change_group(player : TShockAPI.TSPlayer, groupName : string)
            //
            //Changes the player's TShock group to the group provided by groupName
            CmdAliasPlugin.scriptEngine.SetFunction("change_group", new Action <TShockAPI.TSPlayer, object>((player, group) => {
                TShockAPI.DB.User u = new TShockAPI.DB.User();
                string g            = "";

                if (group is string)
                {
                    g = group as string;
                }
                else if (group is TShockAPI.Group)
                {
                    g = (group as TShockAPI.Group).Name;
                }

                if (player != null && !string.IsNullOrEmpty(g))
                {
                    u.Name = player.UserAccountName;
                    TShockAPI.TShock.Users.SetUserGroup(u, g);
                }
            }));

            ///function msg(player : TShockAPI.TSPlayer, message : string)
            ///
            ///Sends an informational message to a player.
            CmdAliasPlugin.scriptEngine.SetFunction("msg", new Action <TShockAPI.TSPlayer, object>((player, msg) => {
                if (player != null && msg != null)
                {
                    player.SendInfoMessageFormat("{0}", msg);
                }
            }));

            ///function broadcast(msg : string)
            ///
            ///Sends a server broadcast.
            CmdAliasPlugin.scriptEngine.SetFunction("broadcast", new Action <object>((msg) => {
                if (msg != null)
                {
                    TShockAPI.TShock.Utils.Broadcast("(Server Broadcast) " + msg.ToString(), Color.Red);
                }
            }));
        }
 public Task SetData(Discord.User discordUser, TShockAPI.DB.User tshockUser)
 {
     return(Task.Run(() => File.WriteAllText(Path.Combine(DirPath, discordUser.Id.ToString()), tshockUser.ID.ToString())));
 }
Example #11
0
        internal static void Initialize() {
            CmdAliasPlugin.scriptEngine = new Jint.JintEngine();
            CmdAliasPlugin.scriptEngine.DisableSecurity();
            CmdAliasPlugin.scriptEngine.SetDebugMode(true);

            ///function seconomy_transfer_async(toAccount : Journal.XBankAccount, player : TShockAPI.TSPlayer, amount : string, completedCallback : function)
            ///
            ///Asynchronously transfers money using SEconomy, then calls back to the JS function specified by completedCallback
            CmdAliasPlugin.scriptEngine.SetFunction("seconomy_transfer_async", new TransferAsyncDelegate((from, to, amount, msg, func) => {
                from.TransferToAsync(to, amount, Journal.BankAccountTransferOptions.AnnounceToSender, msg, msg).ContinueWith((task) => {
                    //callback to the JS function with the result of the transfer
                    CmdAliasPlugin.scriptEngine.CallFunction(func, task.Result);
                });
            }));

            CmdAliasPlugin.scriptEngine.SetFunction("seconomy_pay_async", new TransferAsyncDelegate((from, to, amount, msg, func) => {
                from.TransferToAsync(to, amount, Journal.BankAccountTransferOptions.AnnounceToReceiver | Journal.BankAccountTransferOptions.AnnounceToSender | Journal.BankAccountTransferOptions.IsPayment, msg, msg).ContinueWith((task) => {
                    //callback to the JS function with the result of the transfer
                    CmdAliasPlugin.scriptEngine.CallFunction(func, task.Result);
                });
            }));

            CmdAliasPlugin.scriptEngine.SetFunction("seconomy_parse_money", new Func<object, Money>((moneyString) => {
                try {
                    return Money.Parse(moneyString.ToString());
                }
                catch {
                    return 0;
                }
            }));

            ///function seconomy_valid_money(moneyString : string) : boolean
            ///
            ///Returns true if a supplied money value is valid and is parsable or not.
            CmdAliasPlugin.scriptEngine.SetFunction("seconomy_valid_money", new Func<object, bool>((moneyString) => {
                Money _money;
                return Money.TryParse(moneyString.ToString(), out _money);
            }));



            CmdAliasPlugin.scriptEngine.SetFunction("seconomy_get_account", new Func<object, Journal.XBankAccount>((accountName) => {
                Journal.XBankAccount bankAccount = null;

                if (accountName is TShockAPI.TSPlayer) {
                    Economy.EconomyPlayer ePlayer = SEconomyPlugin.GetEconomyPlayerSafe((accountName as TShockAPI.TSPlayer).Name);
                    if ( ePlayer != null ) {
                        bankAccount = ePlayer.BankAccount;
                    }
                    
                } else if ( accountName != null )  {
                    Economy.EconomyPlayer ePlayer = SEconomyPlugin.GetEconomyPlayerSafe(accountName.ToString());
                     if ( ePlayer != null ) {
                        bankAccount = ePlayer.BankAccount;
                    }
                }

                return bankAccount;
            }));


            ///global variable: __world_account
            ///
            ///Returns a reference to the SEconomy world account
            CmdAliasPlugin.scriptEngine.SetFunction("seconomy_world_account", new Func<Journal.XBankAccount>(() => {
                return SEconomyPlugin.WorldAccount;
            }));

            ///function create_alias(aliasName : string, commandCost : string, cooldownSeconds : integer, permissionsNeeded : string, functionToExecute : function)
            ///
            ///Creates an alias that executes the specified functionToExecute in js when it is called by a player/server.
            CmdAliasPlugin.scriptEngine.SetFunction("create_alias", new RegisterCommandDelegate((aliasname, cost, cooldown, perms, func) => {
                JScriptAliasCommand jAlias = new JScriptAliasCommand() { CommandAlias = aliasname, CooldownSeconds = Convert.ToInt32(cooldown), Cost = cost, Permissions = perms, func = func };

                jsAliases.RemoveAll(i => i.CommandAlias == aliasname);
                jsAliases.Add(jAlias);
            }));

            /// function log(logEntry : string)
            /// 
            ///Writes logEntry to the TShock log
            CmdAliasPlugin.scriptEngine.SetFunction<string>("log", (logText) => {
                TShockAPI.Log.ConsoleInfo(logText);
            });

            ///function get_player(playerName : string) : TShockAPI.TSPlayer
            ///
            ///Returns a TSPlayer by their characterName.  Returns undefined if the player isn't found or is offline.
            CmdAliasPlugin.scriptEngine.SetFunction("get_player", new Func<object, TShockAPI.TSPlayer>((name) => {
                TShockAPI.TSPlayer returnPlayer = null;

                if (name != null) {
                    if (name is string) {
                        returnPlayer = TShockAPI.TShock.Players.FirstOrDefault(i => i != null && i.Name == name.ToString());
                        if (returnPlayer == null) {
                            returnPlayer = TShockAPI.TShock.Players.FirstOrDefault(i => i != null && i.UserAccountName == name.ToString());
                        }
                    } else if (name is Journal.XBankAccount) {
                        Journal.XBankAccount acct = name as Journal.XBankAccount;

                        if (acct.Owner != null) {
                            returnPlayer = acct.Owner.TSPlayer;
                        }
                    }
                }

                return returnPlayer;
            }));

            ///function random(from : integer, to : integer) : integer
            ///
            ///Returns a random number between from and to.
            CmdAliasPlugin.scriptEngine.SetFunction("random", new Func<double, double, double>((rndFrom, rndTo) => {
                lock (__rndLock) {
                    int from = Convert.ToInt32(rndFrom);
                    int to = Convert.ToInt32(rndTo);
                    return randomGenerator.Next(from, to);
                }
            }));

            //function group_exists(groupName : string) : boolean
            //
            //Returns whether the TShock group specified by groupName exists
            CmdAliasPlugin.scriptEngine.SetFunction("group_exists", new Func<object, bool>((groupName) => {
                return TShockAPI.TShock.Groups.Count(i => i.Name.Equals(groupName.ToString(), StringComparison.CurrentCultureIgnoreCase)) > 0;
            }));

            //tshock_group(groupName : string) : TShockAPI.Group
            //
            //Returns a TShock Group object by its name
            CmdAliasPlugin.scriptEngine.SetFunction("tshock_group", new Func<object, TShockAPI.Group>((groupName) => {
                if (groupName == null) {
                    return null;
                }

                TShockAPI.Group g = TShockAPI.TShock.Groups.FirstOrDefault(i => i.Name.Equals(groupName.ToString(), StringComparison.CurrentCultureIgnoreCase));
                return g;
            }));

            //function execute_command(player : TShockAPI.TSPlayer, command : string)
            //
            //causes player to execute provided command in the TShock execution handler.
            //this function ignores permissions, and will always execute as though the user has permissions to do it.
            CmdAliasPlugin.scriptEngine.SetFunction("execute_command", new Action<TShockAPI.TSPlayer, object>((player, cmd) => {
                string commandToExecute = "";
                if (cmd is List<string>) {
                    List<string> cmdList = cmd as List<string>;

                    foreach (var param in cmdList.Skip(1)) {
                        commandToExecute += " " + param;
                    }
                } else if (cmd is string) {
                    commandToExecute = cmd.ToString();
                }
                
                CmdAliasPlugin.HandleCommandWithoutPermissions(player, string.Format("{0}", commandToExecute.Trim()));
            }));

            //function change_group(player : TShockAPI.TSPlayer, groupName : string)
            //
            //Changes the player's TShock group to the group provided by groupName
            CmdAliasPlugin.scriptEngine.SetFunction("change_group", new Action<TShockAPI.TSPlayer, object>((player, group) => {
                TShockAPI.DB.User u = new TShockAPI.DB.User();
                string g = "";

                if (group is string) {
                    g = group as string;
                } else if (group is TShockAPI.Group) {
                    g = (group as TShockAPI.Group).Name;
                }

                if (player != null && !string.IsNullOrEmpty(g)) {
                    u.Name = player.UserAccountName;
                    TShockAPI.TShock.Users.SetUserGroup(u, g);
                }

            }));

            ///function msg(player : TShockAPI.TSPlayer, message : string)
            ///
            ///Sends an informational message to a player.
            CmdAliasPlugin.scriptEngine.SetFunction("msg", new Action<TShockAPI.TSPlayer, object>((player, msg) => {
                if (player != null && msg != null) {
                    player.SendInfoMessageFormat("{0}", msg);
                }
            }));

            ///function broadcast(msg : string)
            ///
            ///Sends a server broadcast.
            CmdAliasPlugin.scriptEngine.SetFunction("broadcast", new Action<object>((msg) => {
                if (msg != null) {
                    TShockAPI.TShock.Utils.Broadcast("(Server Broadcast) " + msg.ToString(), Color.Red);
                }
            }));
        }
Example #12
0
        private void initDiscordCommands()
        {
            // Note: ParameterType.Unparsed catches all remaining text as a single optional parameter
            Client.GetService <CommandService>().CreateCommand("do")
            .Alias("execute", "run")
            .Description("Executes a TShock command.")
            .Parameter("command", ParameterType.Required)
            .Parameter("parameters", ParameterType.Unparsed)
            .Do(async e =>
            {
                BridgeUser player = await Client.LoadUser(e.User);

                if (!player.IsLoggedIn)
                {
                    await e.User.SendMessage("You must be logged in to use TShock commands.\n"
                                             + $"Message me with `{Config.BotPrefix}login <username> <password>` using your TShock credentials to begin.");
                    return;
                }

                // Blacklist commands which must be run through their discord command counterparts
                var blacklist = new List <string>
                {
                    "login",
                    "logout"
                };

                if (blacklist.Contains(e.GetArg("command")))
                {
                    await e.Channel.SendMessage($"This is a discord command, so use `{Config.BotPrefix}{e.GetArg("command")}` (without the `{Config.BotPrefix}do` prefix) instead.");
                    return;
                }

                TSCommand command = Commands.ChatCommands.Find(c => !c.Names.Contains("login") &&
                                                               !c.Names.Contains("logout") &&
                                                               c.Names.Contains(e.GetArg("command")));

                if (command == null && !player.AwaitingResponse.ContainsKey(e.GetArg("command")))
                {
                    await e.Channel.SendMessage($"`{e.GetArg("command")}` is not a TShock command.");
                    return;
                }

                var sb = new StringBuilder();

                if (!e.GetArg("command").StartsWith(Commands.Specifier) && !e.GetArg("command").StartsWith(Commands.Specifier))
                {
                    sb.Append(Commands.Specifier);
                }

                // Temporarily set their command channel so that messages end in the right place
                player.CommandChannel = e.Channel;

                // Disable auto flush to reduce consumption of the discord API for multiple messages
                player.AutoFlush = false;

                if (Commands.HandleCommand(player, sb.Append(e.GetArg("command")).Append(' ').Append(e.GetArg("parameters")).ToString()))
                {
                    await player.FlushMessages();
                }
                else
                {
                    await e.Channel.SendMessage("Command failed, check logs for details.");
                }

                player.AutoFlush      = true;
                player.CommandChannel = null;
            });

            #region Account Commands

            Client.GetService <CommandService>().CreateCommand("login")
            .Description("Log in to a TShock user account to use its permissions when using the `do` command.")
            .Parameter("username", ParameterType.Required)
            .Parameter("password", ParameterType.Required)
            .Do(async e =>
            {
                BridgeUser player = await Client.LoadUser(e.User);

                if (e.Channel != e.User.PrivateChannel)
                {
                    // Delete the message
                    await e.Message.Delete();
                }

                if (player.IsLoggedIn)
                {
                    await e.Channel.SendMessage($"You are already logged in. Use `{Config.BotPrefix}logout` first if you wish to log in to a different account.");
                    return;
                }

                string username = e.GetArg("username");
                string password = e.GetArg("password");

                TShockAPI.DB.User user = TShock.Users.GetUserByName(username);
                if (user == null)
                {
                    await e.Channel.SendMessage("A user by that name does not exist.");
                }

                else if (!user.VerifyPassword(password))
                {
                    await e.Channel.SendMessage("Invalid password!");
                }

                else
                {
                    await Logins.SetData(e.User, user);
                    player = await Logins.Authenticate(e.User.Id);
                    await e.Channel.SendMessage($"Authenticated as {player.Name} successfully.");
                }
            });

            Client.GetService <CommandService>().CreateCommand("logout")
            .Description("Log out of your current TShock user account.")
            .Do(async e =>
            {
                BridgeUser player = await Client.LoadUser(e.User);

                if (!player.IsLoggedIn)
                {
                    await e.Channel.SendMessage("You are not logged in.");
                    return;
                }

                await Logins.RemoveData(e.User.Id);
                await Logins.Authenticate(e.User.Id);
                await e.Channel.SendMessage("You have been successfully logged out of your account.");
            });

            #endregion

            #region Administrative Commands

            Client.GetService <CommandService>().CreateCommand("echo")
            .Alias("bc", "say")
            .Description("Make this bot say something.")
            .Parameter("text", ParameterType.Unparsed)
            .AddCheck((c, u, ch) => !ch.IsPrivate)
            .AddCheck((cmd, user, channel) => user.ServerPermissions.Administrator)
            .Do(async e => await e.Channel.SendMessage(e.GetArg("text") ?? "Hi!"));

            Client.GetService <CommandService>().CreateCommand("addbot")
            .Description("Add another connected Discord Bridge bot to the list of ServerBots for multi-server broadcasting.")
            .Parameter("name", ParameterType.Required)
            .AddCheck((cmd, user, channel) => user.ServerPermissions.Administrator)
            .Do(async e =>
            {
                User botUser = Client.CurrentServer.FindUsers(e.GetArg("name"), true).FirstOrDefault();

                if (botUser == null &&
                    (botUser = Client.CurrentServer.Users.FirstOrDefault(u => u.Nickname.Equals(e.GetArg("name"), StringComparison.OrdinalIgnoreCase))) == null)
                {
                    await e.Channel.SendMessage($"User `{e.GetArg("name")}` is not on this server.");
                    return;
                }

                if (!botUser.IsBot)
                {
                    await e.Channel.SendMessage($"`{e.GetArg("name")}` is not a bot.");
                    return;
                }

                string mention = String.IsNullOrWhiteSpace(botUser.Nickname) ? botUser.Mention : botUser.NicknameMention;
                if (Config.AddBot(botUser.Id))
                {
                    await e.Channel.SendMessage($"Added {mention} to the broadcasting list.");
                }
                else
                {
                    await e.Channel.SendMessage($"{mention} is already on the broadcast list.");
                }
            });

            #endregion
        }