Skip to content
This repository has been archived by the owner on Sep 1, 2021. It is now read-only.

Commit

Permalink
Fix FxCop analyzer warnings and add localization support
Browse files Browse the repository at this point in the history
  • Loading branch information
ldilley committed Jan 16, 2020
1 parent 06417b4 commit d7afbba
Show file tree
Hide file tree
Showing 21 changed files with 1,990 additions and 44 deletions.
54 changes: 41 additions & 13 deletions Terracord/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@
*/

using System;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Xml.Linq;

namespace Terracord
namespace FragLand.TerracordPlugin
{
class Config
{
Expand All @@ -35,6 +37,8 @@ class Config
public static byte[] BroadcastColor { get; private set; }
public static bool LogChat { get; private set; }
public static bool DebugMode { get; private set; }
public static string LocaleString { get; private set; }
public static CultureInfo Locale { get; private set; }
public static string TimestampFormat { get; private set; }
public static bool AbortOnError { get; private set; }

Expand All @@ -43,6 +47,9 @@ class Config
/// </summary>
public static void Parse()
{
// Set default locale if value cannot be read from terracord.xml
LocaleString = "en-US";
Locale = new CultureInfo(LocaleString);
// Set default timestamp format for Util.Log() called in exception in case terracord.xml cannot be parsed
TimestampFormat = "MM/dd/yyyy HH:mm:ss zzz";
// Do not terminate TShock by default if terracord.xml is unable to be parsed
Expand All @@ -55,24 +62,26 @@ public static void Parse()
// terracord.xml root element
XElement configOptions = configFile.Element("configuration");

BotToken = configOptions.Element("bot").Attribute("token").Value.ToString();
ChannelId = UInt64.Parse(configOptions.Element("channel").Attribute("id").Value.ToString());
CommandPrefix = Char.Parse(configOptions.Element("command").Attribute("prefix").Value.ToString());
BotGame = configOptions.Element("game").Attribute("status").Value.ToString();
TopicInterval = UInt32.Parse(configOptions.Element("topic").Attribute("interval").Value.ToString());
LocaleString = configOptions.Element("locale").Attribute("string").Value.ToString(Locale);
ChangeLocale();
BotToken = configOptions.Element("bot").Attribute("token").Value.ToString(Locale);
ChannelId = ulong.Parse(configOptions.Element("channel").Attribute("id").Value.ToString(Locale), Locale);
CommandPrefix = char.Parse(configOptions.Element("command").Attribute("prefix").Value.ToString(Locale));
BotGame = configOptions.Element("game").Attribute("status").Value.ToString(Locale);
TopicInterval = uint.Parse(configOptions.Element("topic").Attribute("interval").Value.ToString(Locale), Locale);

// Populate broadcast RGB array values
BroadcastColor = new byte[3]
{
Byte.Parse(configOptions.Element("broadcast").Attribute("red").Value.ToString()),
Byte.Parse(configOptions.Element("broadcast").Attribute("green").Value.ToString()),
Byte.Parse(configOptions.Element("broadcast").Attribute("blue").Value.ToString())
byte.Parse(configOptions.Element("broadcast").Attribute("red").Value.ToString(Locale), Locale),
byte.Parse(configOptions.Element("broadcast").Attribute("green").Value.ToString(Locale), Locale),
byte.Parse(configOptions.Element("broadcast").Attribute("blue").Value.ToString(Locale), Locale)
};

LogChat = Boolean.Parse(configOptions.Element("log").Attribute("chat").Value.ToString());
DebugMode = Boolean.Parse(configOptions.Element("debug").Attribute("mode").Value.ToString());
TimestampFormat = configOptions.Element("timestamp").Attribute("format").Value.ToString();
AbortOnError = Boolean.Parse(configOptions.Element("exception").Attribute("abort").Value.ToString());
LogChat = bool.Parse(configOptions.Element("log").Attribute("chat").Value.ToString(Locale));
DebugMode = bool.Parse(configOptions.Element("debug").Attribute("mode").Value.ToString(Locale));
TimestampFormat = configOptions.Element("timestamp").Attribute("format").Value.ToString(Locale);
AbortOnError = bool.Parse(configOptions.Element("exception").Attribute("abort").Value.ToString(Locale));
}
catch(FileNotFoundException fnfe)
{
Expand All @@ -83,13 +92,28 @@ public static void Parse()
catch(Exception e)
{
Util.Log($"Unable to parse terracord.xml: {e.Message}", Util.Severity.Error);
throw;
}
Util.Log("terracord.xml parsed.", Util.Severity.Info);
// Display configuration values
if(Config.DebugMode)
Display();
}

/// <summary>
/// Changes locale
/// </summary>
public static void ChangeLocale()
{
Locale = new CultureInfo(LocaleString);
CultureInfo.CurrentCulture = Locale;
CultureInfo.CurrentUICulture = Locale;
CultureInfo.DefaultThreadCurrentCulture = Locale;
CultureInfo.DefaultThreadCurrentUICulture = Locale;
Thread.CurrentThread.CurrentCulture = Locale;
Thread.CurrentThread.CurrentUICulture = Locale;
}

/// <summary>
/// Displays the terracord.xml configuration options
/// </summary>
Expand All @@ -105,6 +129,7 @@ public static void Display()
Util.Log($"Broadcast Color (RGB): {BroadcastColor[0]}, {BroadcastColor[1]}, {BroadcastColor[2]}", Util.Severity.Debug);
Util.Log($"Log Chat: {LogChat}", Util.Severity.Debug);
Util.Log($"Debug Mode: {DebugMode}", Util.Severity.Debug);
Util.Log($"Locale String: {LocaleString}", Util.Severity.Debug);
Util.Log($"Timestamp Format: {TimestampFormat}", Util.Severity.Debug);
Util.Log($"Exception Abort: {AbortOnError}", Util.Severity.Debug);
}
Expand Down Expand Up @@ -137,6 +162,8 @@ public static void Generate()
newConfigFile.WriteLine(" <log chat=\"true\" />\n");
newConfigFile.WriteLine(" <!-- Debug mode -->");
newConfigFile.WriteLine(" <debug mode=\"false\" />\n");
newConfigFile.WriteLine(" <!-- Locale -->");
newConfigFile.WriteLine(" <locale string=\"en-US\" />\n");
newConfigFile.WriteLine(" <!-- Timestamp format -->");
newConfigFile.WriteLine(" <timestamp format=\"MM/dd/yyyy HH:mm:ss zzz\" />\n");
newConfigFile.WriteLine(" <!-- Terminate TShock when an error is encountered -->");
Expand All @@ -153,6 +180,7 @@ public static void Generate()
Util.Log($"Unable to create terracord.xml: {e.Message}", Util.Severity.Error);
if(AbortOnError)
Environment.Exit(Util.ExitFailure);
throw;
}
}
}
Expand Down
50 changes: 30 additions & 20 deletions Terracord/Discord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
using System.Threading.Tasks;
using TShockAPI;

namespace Terracord
namespace FragLand.TerracordPlugin
{
class Discord
{
Expand Down Expand Up @@ -72,32 +72,34 @@ public async Task Connect()
try
{
// Connect to Discord
await Client.LoginAsync(TokenType.Bot, Config.BotToken);
await Client.StartAsync();
await Client.LoginAsync(TokenType.Bot, Config.BotToken).ConfigureAwait(true);
await Client.StartAsync().ConfigureAwait(true);
}
catch(Exception e)
{
Util.Log($"Unable to connect to Discord: {e.Message}", Util.Severity.Error);
if(Config.AbortOnError)
Environment.Exit(Util.ExitFailure);
throw;
}

try
{
// Set game/playing status
await Client.SetGameAsync(Config.BotGame);
await Client.SetGameAsync(Config.BotGame).ConfigureAwait(true);
}
catch(Exception e)
{
Util.Log($"Unable to set game/playing status: {e.Message}", Util.Severity.Error);
if(Config.AbortOnError)
Environment.Exit(Util.ExitFailure);
throw;
}

// Block task until program termination
//await Task.Delay(-1);
// Do not block since it prevents TShock console input when this method is called asynchronously
await Task.CompletedTask;
await Task.CompletedTask.ConfigureAwait(true);
}

/// <summary>
Expand Down Expand Up @@ -145,14 +147,15 @@ private Task Ready()
catch(Exception e)
{
Util.Log($"Unable to acquire Discord channel: {e.Message}", Util.Severity.Error);
throw;
}

if(!UpdateTopicRunning)
_ = UpdateTopic(); // fire and forget topic update thread

// The message below is sent to Discord every time the bot connects/reconnects
Util.Log($"Relay available. Connected to Discord as {Client.CurrentUser.ToString()}.", Util.Severity.Info);
Send("**:white_check_mark: Relay available.**");
Send(Properties.Strings.RelayAvailableString);
return Task.CompletedTask;
}

Expand All @@ -174,7 +177,7 @@ private Task MessageReceived(SocketMessage message)
return Task.CompletedTask;

// Handle commands
if(message.Content.StartsWith(Config.CommandPrefix.ToString()) && message.Content.Length > 1)
if(message.Content.StartsWith(Config.CommandPrefix.ToString(Config.Locale), StringComparison.InvariantCulture) && message.Content.Length > 1)
_ = CommandHandler(message.Content); // avoid blocking in MessageReceived() by using discard

// Check for mentions and convert them to friendly names if found
Expand All @@ -188,6 +191,7 @@ private Task MessageReceived(SocketMessage message)
catch(Exception e)
{
Util.Log($"Unable to broadcast TShock message: {e.Message}", Util.Severity.Error);
throw;
}

return Task.CompletedTask;
Expand All @@ -207,6 +211,7 @@ public void Send(string message)
catch(Exception e)
{
Util.Log($"Unable to send Discord message: {e.Message}", Util.Severity.Error);
throw;
}
}

Expand All @@ -224,17 +229,19 @@ private async Task CommandHandler(string command)
string playerList = $"{TShock.Utils.ActivePlayers()}/{TShock.Config.MaxSlots}\n\n";
foreach(var player in TShock.Utils.GetPlayers(false))
playerList += $"{player}\n";
await CommandResponse("Player List", playerList);
await CommandResponse("Player List", playerList).ConfigureAwait(true);
}

if(command.Equals("serverinfo", StringComparison.OrdinalIgnoreCase))
await CommandResponse("Server Information", $"**Server Name:** {TShock.Config.ServerName}\n**Players:** {TShock.Utils.ActivePlayers()}/{TShock.Config.MaxSlots}\n**TShock Version:** {TShock.VersionNum.ToString()}");
await CommandResponse("Server Information",
$"**Server Name:** {TShock.Config.ServerName}\n**Players:** {TShock.Utils.ActivePlayers()}/{TShock.Config.MaxSlots}\n**TShock Version:** {TShock.VersionNum.ToString()}")
.ConfigureAwait(true);

if(command.Equals("uptime", StringComparison.OrdinalIgnoreCase))
//Send($"**__Uptime__**\n```\n{Util.Uptime()}\n```");
await CommandResponse("Uptime", Util.Uptime());
await CommandResponse("Uptime", Util.Uptime()).ConfigureAwait(true);

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

/// <summary>
Expand All @@ -249,30 +256,31 @@ private async Task CommandResponse(string title, string description, Color? colo
try
{
Color embedColor = color ?? Color.Blue;
await channel.TriggerTypingAsync();
await Task.Delay(1500); // pause for 1.5 seconds
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.version}")
.WithFooter(footer => footer.Text = $"Terracord {Terracord.PluginVersion}")
.WithCurrentTimestamp()
.WithTitle(title);
await channel.SendMessageAsync("", false, embed.Build());
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;
await Task.CompletedTask.ConfigureAwait(true);
}

/// <summary>
/// Converts channel, role, and user mentions to friendly names before being broadcasted to TShock players
/// </summary>
/// <param name="message">message received by Discord bot</param>
/// <returns>modified message text</returns>
private string ConvertMentions(SocketMessage message)
private static string ConvertMentions(SocketMessage message)
{
StringBuilder modifiedMessageText = new StringBuilder(message.Content);

Expand Down Expand Up @@ -309,15 +317,17 @@ private async Task UpdateTopic()
{
await topicChannel.ModifyAsync(chan =>
{
chan.Topic = $"{TShock.Utils.ActivePlayers()}/{TShock.Config.MaxSlots} players online | Server online for {Util.Uptime()} | Last update: {DateTime.Now.ToString(Config.TimestampFormat)}";
});
chan.Topic = $"{TShock.Utils.ActivePlayers()}/{TShock.Config.MaxSlots} players online " +
$"| Server online for {Util.Uptime()} | Last update: {DateTime.Now.ToString(Config.TimestampFormat, Config.Locale)}";
}).ConfigureAwait(true);
try
{
await Task.Delay(Convert.ToInt32(Config.TopicInterval * 1000)); // seconds to milliseconds
await Task.Delay(Convert.ToInt32(Config.TopicInterval * 1000)).ConfigureAwait(true); // seconds to milliseconds
}
catch(OverflowException oe)
{
Util.Log($"Topic interval value exceeds limit: {oe.Message}", Util.Severity.Error);
throw;
}
}
}
Expand Down
81 changes: 81 additions & 0 deletions Terracord/Properties/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit d7afbba

Please sign in to comment.