From 351dfd830cea4fb62d05bcd3907281574127ccd4 Mon Sep 17 00:00:00 2001 From: Cyberboss Date: Wed, 18 Oct 2017 13:26:54 -0400 Subject: [PATCH] Documents and splits up Chat.cs into ChatProvider.cs --- TGServiceInterface/Chat.cs | 364 +----------------- TGServiceInterface/ChatProvider.cs | 367 +++++++++++++++++++ TGServiceInterface/RootCommand.cs | 105 ++++++ TGServiceInterface/TGServiceInterface.csproj | 1 + 4 files changed, 480 insertions(+), 357 deletions(-) create mode 100644 TGServiceInterface/ChatProvider.cs create mode 100644 TGServiceInterface/RootCommand.cs diff --git a/TGServiceInterface/Chat.cs b/TGServiceInterface/Chat.cs index cfdab6d228..0868f56999 100644 --- a/TGServiceInterface/Chat.cs +++ b/TGServiceInterface/Chat.cs @@ -1,357 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; +using System.Collections.Generic; using System.ServiceModel; -using System.Web.Script.Serialization; namespace TGServiceInterface { - /// - /// The type of chat provider - /// - public enum TGChatProvider : int - { - /// - /// IRC chat provider - /// - IRC = 0, - /// - /// Discord chat provider - /// - Discord = 1, - } - - /// - /// Supported irc permission modes - /// - public enum IRCMode : int - { - /// - /// + - /// - Voice, - /// - /// % - /// - Halfop, - /// - /// @ - /// - Op, - /// - /// ~ - /// - Owner, - } - - /// - /// For setting up authentication no matter the chat provider - /// - [DataContract] - [KnownType(typeof(TGIRCSetupInfo))] - [KnownType(typeof(TGDiscordSetupInfo))] - public class TGChatSetupInfo - { - const int AdminListIndex = 0; - const int AdminModeIndex = 1; - const int AdminChannelIndex = 2; - const int DevChannelIndex = 3; - const int WDChannelIndex = 4; - const int GameChannelIndex = 5; - const int ProviderIndex = 6; - const int EnabledIndex = 7; - protected const int BaseIndex = 8; - protected readonly bool InitializeFields; - /// - /// Constructs a TGChatSetupInfo from optional past data - /// - /// Optional past data - /// The number of fields in this chat provider - protected TGChatSetupInfo(TGChatSetupInfo baseInfo, int numFields) - { - numFields += BaseIndex; - InitializeFields = baseInfo == null || baseInfo.DataFields.Count != numFields; - - if (InitializeFields) - { - DataFields = new List(numFields); - for (var I = 0; I < numFields; ++I) - DataFields.Add(null); - - AdminList = new List(); - AdminChannels = new List(); - DevChannels = new List(); - GameChannels = new List(); - WatchdogChannels = new List(); - AdminsAreSpecial = false; - Enabled = false; - } - else - DataFields = baseInfo.DataFields; - } - - TGChatSetupInfo Specialize() - { - switch (Provider) - { - case TGChatProvider.IRC: - return new TGIRCSetupInfo(this); - case TGChatProvider.Discord: - return new TGDiscordSetupInfo(this); - default: - throw new Exception("Invalid provider!"); - } - } - - //trims and adds the leading # - protected virtual string SanitizeChannelName(string working) - { - return Specialize().SanitizeChannelName(working); - } - void SanitizeChannelNames(IList working) - { - for (var I = 0; I < working.Count; ++I) - - if (String.IsNullOrWhiteSpace(working[I])) - { - working.RemoveAt(I); - --I; - } - else - working[I] = SanitizeChannelName(working[I].Trim()); - } - - /// - /// Constructs a TGChatSetupInfo from a data list - /// - /// The data - /// The chat provider - public TGChatSetupInfo(IList DeserializedData) - { - DataFields = DeserializedData; - } - /// - /// The list of admin entries - /// - public IList AdminList - { - get { return new JavaScriptSerializer().Deserialize>(DataFields[AdminListIndex]); } - set { DataFields[AdminListIndex] = new JavaScriptSerializer().Serialize(value); } - } - /// - /// If AdminList corresponds to a Provider specific recognization method - /// - public bool AdminsAreSpecial - { - get { return Convert.ToBoolean(DataFields[AdminModeIndex]); } - set { DataFields[AdminModeIndex] = Convert.ToString(value); } - } - /// - /// The channels from which admin commands/messages can be sent/received - /// - public IList AdminChannels - { - get { return new JavaScriptSerializer().Deserialize>(DataFields[AdminChannelIndex]); } - set - { - SanitizeChannelNames(value); - DataFields[AdminChannelIndex] = new JavaScriptSerializer().Serialize(value); - } - } - /// - /// The channels to which repo and compile messages are sent - /// - public IList DevChannels - { - get { return new JavaScriptSerializer().Deserialize>(DataFields[DevChannelIndex]); } - set - { - SanitizeChannelNames(value); - DataFields[DevChannelIndex] = new JavaScriptSerializer().Serialize(value); - } - } - /// - /// The channels to which watchdog messages are sent - /// - public IList WatchdogChannels - { - get { return new JavaScriptSerializer().Deserialize>(DataFields[WDChannelIndex]); } - set - { - SanitizeChannelNames(value); - DataFields[WDChannelIndex] = new JavaScriptSerializer().Serialize(value); - } - } - /// - /// The channels to which game messages are sent - /// - public IList GameChannels - { - get { return new JavaScriptSerializer().Deserialize>(DataFields[GameChannelIndex]); } - set - { - SanitizeChannelNames(value); - DataFields[GameChannelIndex] = new JavaScriptSerializer().Serialize(value); - } - } - /// - /// If this chat provider is enabled - /// - public bool Enabled - { - get { return Convert.ToBoolean(DataFields[EnabledIndex]); } - set { DataFields[EnabledIndex] = Convert.ToString(value); } - } - - /// - /// The type of provider - /// - public TGChatProvider Provider - { - get { return (TGChatProvider)Convert.ToInt32(DataFields[ProviderIndex]); } - set { DataFields[ProviderIndex] = Convert.ToString((int)value); } - } - - /// - /// Raw access to the underlying data - /// - [DataMember] - public IList DataFields { get; protected set; } - } - - /// - /// Chat provider for IRC - /// Admin entries should be user nicknames in normal mode or required flags in special mode - /// - [DataContract] - public class TGIRCSetupInfo : TGChatSetupInfo - { - const int URLIndex = 0; - const int PortIndex = 1; - const int NickIndex = 2; - const int AuthTargetIndex = 3; - const int AuthMessageIndex = 4; - const int AuthLevelIndex = 5; - const int FieldsLen = 6; - - /// - /// Construct IRC setup info from optional generic info. Defaults to TGS3 on rizons IRC server - /// - /// Optional generic info - public TGIRCSetupInfo(TGChatSetupInfo baseInfo = null) : base(baseInfo, FieldsLen) - { - Provider = TGChatProvider.IRC; - if (InitializeFields) - { - Nickname = "TGS3"; - URL = "irc.rizon.net"; - Port = 6667; - AuthTarget = ""; - AuthMessage = ""; - AdminsAreSpecial = true; - AuthLevel = IRCMode.Op; - } - } - - protected override string SanitizeChannelName(string working) - { - if (working[0] != '#') - return "#" + working; - return working; - } - - /// - /// The port of the IRC server - /// - public ushort Port { - get { return Convert.ToUInt16(DataFields[BaseIndex + PortIndex]); } - set { DataFields[BaseIndex + PortIndex] = value.ToString(); } - } - /// - /// The URL of the IRC server - /// - public string URL - { - get { return DataFields[BaseIndex + URLIndex]; } - set { DataFields[BaseIndex + URLIndex] = value; } - } - /// - /// The nickname of the IRC bot - /// - public string Nickname - { - get { return DataFields[BaseIndex + NickIndex]; } - set { DataFields[BaseIndex + NickIndex] = value; } - } - /// - /// The target for sending authentication messages - /// - public string AuthTarget - { - get { return DataFields[BaseIndex + AuthTargetIndex]; } - set { DataFields[BaseIndex + AuthTargetIndex] = value; } - } - /// - /// The authentication message - /// - public string AuthMessage - { - get { return DataFields[BaseIndex + AuthMessageIndex]; } - set { DataFields[BaseIndex + AuthMessageIndex] = value; } - } - /// - /// The minimum mode required to use admin bot commands when in special auth mode - /// - public IRCMode AuthLevel - { - get { return (IRCMode)Convert.ToInt32(DataFields[BaseIndex + AuthLevelIndex]); } - set { DataFields[BaseIndex + AuthLevelIndex] = Convert.ToString((int)value); } - } - } - - /// - /// Chat provider for Discord - /// Admin entires should be user ids in normal mode or group ids in special mode - /// - [DataContract] - public class TGDiscordSetupInfo : TGChatSetupInfo - { - const int BotTokenIndex = 0; - const int FieldsLen = 1; - /// - /// Construct Discord setup info from optional generic info. Default is not a valid discord bot tokent - /// - /// Optional generic info - public TGDiscordSetupInfo(TGChatSetupInfo baseInfo = null) : base(baseInfo, FieldsLen) - { - Provider = TGChatProvider.Discord; - if (InitializeFields) - BotToken = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; //needless to say, this is fake - } - protected override string SanitizeChannelName(string working) - { - working = working.Replace("<", "").Replace(">", "").Replace("&", ""); //filter out some stuff that can come in the copypasta - try - { - Convert.ToUInt64(working); - } - catch - { - throw new Exception("Invalid Discord channel ID!"); - } - return working; - } - - /// - /// The Discord bot token to use. See https://discordapp.com/developers/applications/me for registering bot accounts - /// - public string BotToken - { - get { return DataFields[BaseIndex + BotTokenIndex]; } - set { DataFields[BaseIndex + BotTokenIndex] = value; } - } - } - /// /// Interface for handling chat bot /// @@ -359,16 +10,15 @@ namespace TGServiceInterface public interface ITGChat { /// - /// Set the chat provider info + /// Sets a chat provider /// /// The info to set [OperationContract] string SetProviderInfo(TGChatSetupInfo info); /// - /// Returns the chat provider info + /// Returns for all s /// - /// The type of provider to get info for - /// The chat provider info for the selected provider + /// A list of all s [OperationContract] IList ProviderInfos(); @@ -376,15 +26,15 @@ namespace TGServiceInterface /// Checks connection status /// /// The type of provider to check if connected - /// true if connected, false otherwise + /// if connected, otherwise [OperationContract] bool Connected(TGChatProvider providerType); /// - /// Reconnect to the chat service + /// Reconnect a specific to it's chat service /// /// The type of provider to reconnect - /// null on success, error message on failure + /// on success, error message on failure [OperationContract] string Reconnect(TGChatProvider providerType); } diff --git a/TGServiceInterface/ChatProvider.cs b/TGServiceInterface/ChatProvider.cs new file mode 100644 index 0000000000..5c28f5b1c8 --- /dev/null +++ b/TGServiceInterface/ChatProvider.cs @@ -0,0 +1,367 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Web.Script.Serialization; + +namespace TGServiceInterface +{ + /// + /// The type of chat provider + /// + public enum TGChatProvider : int + { + /// + /// IRC chat provider + /// + IRC = 0, + /// + /// Discord chat provider + /// + Discord = 1, + } + + /// + /// Supported irc permission modes + /// + public enum IRCMode : int + { + /// + /// + + /// + Voice, + /// + /// % + /// + Halfop, + /// + /// @ + /// + Op, + /// + /// ~ + /// + Owner, + } + + /// + /// For setting up authentication no matter the chat provider + /// + [DataContract] + [KnownType(typeof(TGIRCSetupInfo))] + [KnownType(typeof(TGDiscordSetupInfo))] + public class TGChatSetupInfo + { + const int AdminListIndex = 0; + const int AdminModeIndex = 1; + const int AdminChannelIndex = 2; + const int DevChannelIndex = 3; + const int WDChannelIndex = 4; + const int GameChannelIndex = 5; + const int ProviderIndex = 6; + const int EnabledIndex = 7; + protected const int BaseIndex = 8; + protected readonly bool InitializeFields; + /// + /// Constructs a from optional + /// + /// Optional past data + /// The number of fields in this chat provider + protected TGChatSetupInfo(TGChatSetupInfo baseInfo, int numFields) + { + numFields += BaseIndex; + InitializeFields = baseInfo == null || baseInfo.DataFields.Count != numFields; + + if (InitializeFields) + { + DataFields = new List(numFields); + for (var I = 0; I < numFields; ++I) + DataFields.Add(null); + + AdminList = new List(); + AdminChannels = new List(); + DevChannels = new List(); + GameChannels = new List(); + WatchdogChannels = new List(); + AdminsAreSpecial = false; + Enabled = false; + } + else + DataFields = baseInfo.DataFields; + } + + /// + /// Recreates as the correct child + /// + /// A new based on the type + TGChatSetupInfo Specialize() + { + switch (Provider) + { + case TGChatProvider.IRC: + return new TGIRCSetupInfo(this); + case TGChatProvider.Discord: + return new TGDiscordSetupInfo(this); + default: + throw new Exception("Invalid provider!"); + } + } + + /// + /// Properly formats a name for the + /// + /// The to format + /// The formatted + protected virtual string SanitizeChannelName(string channel) + { + return Specialize().SanitizeChannelName(channel); + } + + /// + /// Sanitizes a list of + /// + /// An of strings + void SanitizeChannelNames(IList channelnames) + { + for (var I = 0; I < channelnames.Count; ++I) + + if (String.IsNullOrWhiteSpace(channelnames[I])) + { + channelnames.RemoveAt(I); + --I; + } + else + channelnames[I] = SanitizeChannelName(channelnames[I].Trim()); + } + + /// + /// Constructs a from a data list + /// + /// The data + /// The chat provider + public TGChatSetupInfo(IList DeserializedData) + { + DataFields = DeserializedData; + } + /// + /// The list of admin entries + /// + public IList AdminList + { + get { return new JavaScriptSerializer().Deserialize>(DataFields[AdminListIndex]); } + set { DataFields[AdminListIndex] = new JavaScriptSerializer().Serialize(value); } + } + /// + /// If AdminList corresponds to a Provider specific recognization method + /// + public bool AdminsAreSpecial + { + get { return Convert.ToBoolean(DataFields[AdminModeIndex]); } + set { DataFields[AdminModeIndex] = Convert.ToString(value); } + } + /// + /// The channels from which admin commands/messages can be sent/received + /// + public IList AdminChannels + { + get { return new JavaScriptSerializer().Deserialize>(DataFields[AdminChannelIndex]); } + set + { + SanitizeChannelNames(value); + DataFields[AdminChannelIndex] = new JavaScriptSerializer().Serialize(value); + } + } + /// + /// The channels to which repo and compile messages are sent + /// + public IList DevChannels + { + get { return new JavaScriptSerializer().Deserialize>(DataFields[DevChannelIndex]); } + set + { + SanitizeChannelNames(value); + DataFields[DevChannelIndex] = new JavaScriptSerializer().Serialize(value); + } + } + /// + /// The channels to which watchdog messages are sent + /// + public IList WatchdogChannels + { + get { return new JavaScriptSerializer().Deserialize>(DataFields[WDChannelIndex]); } + set + { + SanitizeChannelNames(value); + DataFields[WDChannelIndex] = new JavaScriptSerializer().Serialize(value); + } + } + /// + /// The channels to which game messages are sent + /// + public IList GameChannels + { + get { return new JavaScriptSerializer().Deserialize>(DataFields[GameChannelIndex]); } + set + { + SanitizeChannelNames(value); + DataFields[GameChannelIndex] = new JavaScriptSerializer().Serialize(value); + } + } + /// + /// If this chat provider is enabled + /// + public bool Enabled + { + get { return Convert.ToBoolean(DataFields[EnabledIndex]); } + set { DataFields[EnabledIndex] = Convert.ToString(value); } + } + + /// + /// The type of provider + /// + public TGChatProvider Provider + { + get { return (TGChatProvider)Convert.ToInt32(DataFields[ProviderIndex]); } + set { DataFields[ProviderIndex] = Convert.ToString((int)value); } + } + + /// + /// Raw access to the underlying data + /// + [DataMember] + public IList DataFields { get; protected set; } + } + + /// + /// Chat provider for IRC. Admin entries should be user nicknames in normal mode or required channel flags in special mode + /// + [DataContract] + public sealed class TGIRCSetupInfo : TGChatSetupInfo + { + const int URLIndex = 0; + const int PortIndex = 1; + const int NickIndex = 2; + const int AuthTargetIndex = 3; + const int AuthMessageIndex = 4; + const int AuthLevelIndex = 5; + const int FieldsLen = 6; + + /// + /// Construct IRC setup info from optional generic info. Defaults to TGS3 on rizons IRC server + /// + /// Optional generic info + public TGIRCSetupInfo(TGChatSetupInfo baseInfo = null) : base(baseInfo, FieldsLen) + { + Provider = TGChatProvider.IRC; + if (InitializeFields) + { + Nickname = "TGS3"; + URL = "irc.rizon.net"; + Port = 6667; + AuthTarget = ""; + AuthMessage = ""; + AdminsAreSpecial = true; + AuthLevel = IRCMode.Op; + } + } + + /// + protected override string SanitizeChannelName(string working) + { + if (working[0] != '#') + return "#" + working; + return working; + } + + /// + /// The port of the IRC server + /// + public ushort Port + { + get { return Convert.ToUInt16(DataFields[BaseIndex + PortIndex]); } + set { DataFields[BaseIndex + PortIndex] = value.ToString(); } + } + /// + /// The URL of the IRC server + /// + public string URL + { + get { return DataFields[BaseIndex + URLIndex]; } + set { DataFields[BaseIndex + URLIndex] = value; } + } + /// + /// The nickname of the IRC bot + /// + public string Nickname + { + get { return DataFields[BaseIndex + NickIndex]; } + set { DataFields[BaseIndex + NickIndex] = value; } + } + /// + /// The target for sending authentication messages + /// + public string AuthTarget + { + get { return DataFields[BaseIndex + AuthTargetIndex]; } + set { DataFields[BaseIndex + AuthTargetIndex] = value; } + } + /// + /// The authentication message + /// + public string AuthMessage + { + get { return DataFields[BaseIndex + AuthMessageIndex]; } + set { DataFields[BaseIndex + AuthMessageIndex] = value; } + } + /// + /// The minimum mode required to use admin bot commands when in special auth mode + /// + public IRCMode AuthLevel + { + get { return (IRCMode)Convert.ToInt32(DataFields[BaseIndex + AuthLevelIndex]); } + set { DataFields[BaseIndex + AuthLevelIndex] = Convert.ToString((int)value); } + } + } + + /// + /// Chat provider for Discord. Admin entires should be user ids in normal mode or group ids in special mode + /// + [DataContract] + public sealed class TGDiscordSetupInfo : TGChatSetupInfo + { + const int BotTokenIndex = 0; + const int FieldsLen = 1; + /// + /// Construct Discord setup info from optional generic info. Default is not a valid discord bot tokent + /// + /// Optional generic info + public TGDiscordSetupInfo(TGChatSetupInfo baseInfo = null) : base(baseInfo, FieldsLen) + { + Provider = TGChatProvider.Discord; + if (InitializeFields) + BotToken = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; //needless to say, this is fake + } + /// + protected override string SanitizeChannelName(string working) + { + working = working.Replace("<", "").Replace(">", "").Replace("&", ""); //filter out some stuff that can come in the copypasta + try + { + Convert.ToUInt64(working); + } + catch + { + throw new Exception("Invalid Discord channel ID!"); + } + return working; + } + + /// + /// The Discord bot token to use. See https://discordapp.com/developers/applications/me for registering bot accounts + /// + public string BotToken + { + get { return DataFields[BaseIndex + BotTokenIndex]; } + set { DataFields[BaseIndex + BotTokenIndex] = value; } + } + } +} diff --git a/TGServiceInterface/RootCommand.cs b/TGServiceInterface/RootCommand.cs new file mode 100644 index 0000000000..ac179e7fed --- /dev/null +++ b/TGServiceInterface/RootCommand.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; + +namespace TGServiceInterface +{ + /// + /// Helper for creating commands that contain sub commands + /// + public class RootCommand : Command + { + /// + /// s further down the tree from this one. Set in Constructor + /// + public Command[] Children { get; protected set; } = { }; + /// + /// If set to a multiline, detailed list of s will be printed. Otherwise a singleline list of s will be printed + /// + public static bool PrintHelpList = false; + + /// + /// Forward parameters to commands further down the tree + /// + /// List of parameters passed to the + /// The result of a sub or an appropriate if the handled it + protected override ExitCode Run(IList parameters) + { + if (parameters.Count > 0) + { + var LocalKeyword = parameters[0].Trim().ToLower(); + parameters.RemoveAt(0); + switch (LocalKeyword) + { + case "help": + case "?": + PrintHelp(); + return ExitCode.Normal; + default: + foreach (var c in Children) + if (c.Keyword == LocalKeyword) + { + if (parameters.Count > 0) + { + var possibleHelp = parameters[0].ToLower(); + if (possibleHelp == "help" || possibleHelp == "?") + { + c.PrintHelp(); + return ExitCode.Normal; + } + } + if (parameters.Count < c.RequiredParameters) + { + OutputProc("Not enough parameters!"); + return ExitCode.BadCommand; + } + return c.DoRun(parameters); + } + parameters.Insert(0, LocalKeyword); + break; + } + } + OutputProc(String.Format("Invalid command! Type '{0}?' or '{0}help' for available commands.", Keyword != null ? Keyword + " " : "")); + return ExitCode.BadCommand; + } + + /// + public override void PrintHelp() + { + var Final = new List(); + if (PrintHelpList) + { + foreach (var c in Children) + Final.Add(c.Keyword); + OutputProc("Available commands (type '?' or 'help' after command for more info): " + String.Join(", ", Final)); + } + else + { + var Prefixes = new List(); + var Postfixes = new List(); + int MaxPrefixLen = 0; + foreach (var c in Children) + { + var ns = c.Keyword + " " + c.GetArgumentString(); + MaxPrefixLen = Math.Max(MaxPrefixLen, ns.Length); + Prefixes.Add(ns); + Postfixes.Add(c.GetHelpText()); + } + + for (var I = 0; I < Prefixes.Count; ++I) + { + var lp = Prefixes[I]; + for (; lp.Length < MaxPrefixLen + 1; lp += " ") ; + Final.Add(lp + "- " + Postfixes[I]); + } + Final.Sort(); + Final.ForEach(OutputProc); + } + } + + /// + public override string GetHelpText() + { + throw new NotImplementedException(); + } + } +} diff --git a/TGServiceInterface/TGServiceInterface.csproj b/TGServiceInterface/TGServiceInterface.csproj index 25feb1ec2d..d20d665cb1 100644 --- a/TGServiceInterface/TGServiceInterface.csproj +++ b/TGServiceInterface/TGServiceInterface.csproj @@ -49,6 +49,7 @@ +