FragLand/terracord

View on GitHub
Terracord/Command.cs

Summary

Maintainability
A
0 mins
Test Coverage
/*
 * Terracord.cs - A Discord <-> Terraria bridge plugin for TShock
 * Copyright (C) 2019-2021 Lloyd Dilley
 * http://www.frag.land/
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

using Discord;
using Discord.WebSocket;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using TShockAPI;

namespace FragLand.TerracordPlugin
{
  class Command
  {
    // This class is instanced as a TShock client object that issues commands
    class TerracordPlayer:TSPlayer
    {
      public List<string> Output = new List<string>();
      public TerracordPlayer(string player):base(player){}
      public override void SendMessage(string msg, byte red, byte green, byte blue)
      {
        Output.Add(msg); // command output
      }
    }

    /// <summary>
    /// Handles Discord commands
    /// </summary>
    /// <param name="user">user issuing the command</param>
    /// <param name="channel">Discord text channel</param>
    /// <param name="command">command sent by a Discord user</param>
    /// <returns>void</returns>
    public static async Task CommandHandler(DiscordSocketClient client, SocketUser user, IMessageChannel channel, string command)
    {
      command = command.Substring(Config.CommandPrefix.Length).TrimStart(); // remove command prefix
      Util.Log($"Command sent: {command}", Util.Severity.Info);

      if(command.Equals("help", StringComparison.OrdinalIgnoreCase))
        await CommandResponse(channel, "Help", Help()).ConfigureAwait(true);
      else if(command.Equals("playerlist", StringComparison.OrdinalIgnoreCase))
        await CommandResponse(channel, "Player List", PlayerList()).ConfigureAwait(true);
      else if(command.Equals("serverinfo", StringComparison.OrdinalIgnoreCase))
        await CommandResponse(channel, "Server Information", ServerInfo()).ConfigureAwait(true);
      else if(command.Equals("uptime", StringComparison.OrdinalIgnoreCase))
        await CommandResponse(channel, "Uptime", Uptime()).ConfigureAwait(true);
      else if(command.StartsWith("setgame", StringComparison.OrdinalIgnoreCase))
      {
        string status = Regex.Replace(command, "setgame ", "", RegexOptions.IgnoreCase);
        await CommandResponse(channel, "Set Game", $"**New game status set to:** `{status}`").ConfigureAwait(true);
        await Discord.UpdateBotGame(client, status).ConfigureAwait(true);
      }
      else // let TShock attempt to handle the command
      {
        if(Config.RemoteCommands)
          _ = ExecuteTShockCommand(user, channel, command);
      }

      await Task.CompletedTask.ConfigureAwait(true);
    }

    /// <summary>
    /// Attempts to execute a TShock command sent from Discord
    /// </summary>
    /// <param name="user">user issuing the command</param>
    /// <param name="channel">Discord text channel</param>
    /// <param name="command">command sent by a Discord user</param>
    /// <returns>void</returns>
    private static async Task ExecuteTShockCommand(SocketUser user, IMessageChannel channel, string command)
    {
      if(user.Id == Config.OwnerId || Util.AuthorizedUser(user)) // check if user is authorized
      {
        TerracordPlayer terracordPlayer = new TerracordPlayer(user.Username){Group = new SuperAdminGroup()};
        //if(Commands.HandleCommand(TSPlayer.Server, $"{TShock.Config.CommandSpecifier}{command}"))
        if(Commands.HandleCommand(terracordPlayer, $"{TShock.Config.Settings.CommandSpecifier}{command}"))
        {
          foreach(string commandOutput in terracordPlayer.Output)
            await CommandResponse(channel, "Command Status", $"Command output: {commandOutput}", Color.Green).ConfigureAwait(true);
        }
        else
        {
          await CommandResponse(channel, "Command Status", $"Failed to execute: {command}", Color.Red).ConfigureAwait(true);
        }
      }
      else
        await CommandResponse(channel, "Command Status", $"Access denied for: {command}", Color.Red).ConfigureAwait(true);

      await Task.CompletedTask.ConfigureAwait(true);
    }

    /// <summary>
    /// Provides command help
    /// </summary>
    /// <returns>command details</returns>
    public static string Help()
    {
      string commandList = "__**Commands**__\n" +
                           "**help**             - Display command list\n" +
                           "**playerlist**       - Display online players\n" +
                           "**serverinfo**       - Display server details\n" +
                           "**setgame [status]** - Set bot game/playing status\n" +
                           "**uptime**           - Display plugin uptime\n\n";
      return commandList;
    }

    /// <summary>
    /// Provides player list
    /// </summary>
    /// <returns>player count and list of players</returns>
    public static string PlayerList()
    {
      string playerList = $"{TShock.Utils.GetActivePlayerCount()}/{TShock.Config.Settings.MaxSlots}\n\n";
      //foreach(var player in TShock.Utils.GetPlayers(false))
      //  playerList += $"{player.Name}\n";
      foreach(TSPlayer player in TShock.Players)
      {
        if(player != null && player.Active)
          playerList += $"{player.Name}\n";
      }
      return playerList;
    }

    /// <summary>
    /// Provides server information
    /// </summary>
    /// <returns>server information</returns>
    public static string ServerInfo()
    {
      return $"**Server Name:** {TShock.Config.Settings.ServerName}\n" +
             $"**Players:** {TShock.Utils.GetActivePlayerCount()}/{TShock.Config.Settings.MaxSlots}\n" +
             $"**TShock Version:** {TShock.VersionNum}";
    }

    /// <summary>
    /// Calculates plugin uptime
    /// </summary>
    /// <returns>uptime as a string</returns>
    public static string Uptime()
    {
      TimeSpan elapsed = DateTime.Now.Subtract(Terracord.startTime);
      return $"{elapsed.Days} day(s), {elapsed.Hours} hour(s), {elapsed.Minutes} minute(s), and {elapsed.Seconds} second(s)";
    }

    /// <summary>
    /// Responds to commands
    /// </summary>
    /// <param name="channel">Discord text channel</param>
    /// <param name="title">command title</param>
    /// <param name="description">command output</param>
    /// <param name="color">embed color</param>
    /// <returns>void</returns>
    private static async Task CommandResponse(IMessageChannel channel, string title, string description, Color? color = null)
    {
      try
      {
        Color embedColor = color ?? Color.Blue;
        await channel.TriggerTypingAsync().ConfigureAwait(true);
        await Task.Delay(1500).ConfigureAwait(true); // pause for 1.5 seconds
        EmbedBuilder embed = new EmbedBuilder();
        embed.WithColor(embedColor)
             .WithDescription(description)
             .WithFooter(footer => footer.Text = $"Terracord {Terracord.PluginVersion}")
             .WithCurrentTimestamp()
             .WithTitle(title);
        await channel.SendMessageAsync("", false, embed.Build()).ConfigureAwait(true);
      }
      catch(Exception e)
      {
        Util.Log($"Unable to send command response: {e.Message}", Util.Severity.Error);
        throw;
      }

      await Task.CompletedTask.ConfigureAwait(true);
    }
  }
}