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 @@
+