using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Runtime.Serialization; namespace TGS.Interface { /// /// For setting up authentication no matter the chat provider /// [DataContract] [KnownType(typeof(IRCSetupInfo))] [KnownType(typeof(DiscordSetupInfo))] public class ChatSetupInfo { 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; /// /// Starting index of which child classes should use to write their custom data to /// protected const int BaseIndex = 8; /// /// Set to if a child constructor should use the baseInfo parameter of to initialize it's property fields, otherwise /// protected readonly bool InitializeFields; /// /// Raw access to the underlying data /// [DataMember] public IList DataFields { get; protected set; } /// /// Constructs a from optional /// /// The that this is for /// Optional past data /// The number of fields in this chat provider protected internal ChatSetupInfo(ChatProvider provider, ChatSetupInfo 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; Provider = provider; Specialize(true); //to check we have a valid provider } /// /// Recreates as the correct child /// /// If , is returned provided is a valid /// A new based on the type ChatSetupInfo Specialize(bool checkOnly) { switch (Provider) { case ChatProvider.IRC: if (!checkOnly) return new IRCSetupInfo(this); break; case ChatProvider.Discord: if (!checkOnly) return new DiscordSetupInfo(this); break; default: throw new Exception("Invalid provider!"); } return null; } /// /// Properly formats a name for the /// /// The to format /// The formatted protected virtual string SanitizeChannelName(string channel) { return Specialize(false).SanitizeChannelName(channel); } /// /// Sanitizes a list of /// /// A 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 public ChatSetupInfo(IList DeserializedData) { DataFields = DeserializedData; Specialize(false); //ensure provider type is valid } /// /// The list of admin entries /// public List AdminList { get { return JsonConvert.DeserializeObject>(DataFields[AdminListIndex]); } set { DataFields[AdminListIndex] = JsonConvert.SerializeObject(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 List AdminChannels { get { return JsonConvert.DeserializeObject>(DataFields[AdminChannelIndex]); } set { SanitizeChannelNames(value); DataFields[AdminChannelIndex] = JsonConvert.SerializeObject(value); } } /// /// The channels to which repo and compile messages are sent /// public List DevChannels { get { return JsonConvert.DeserializeObject>(DataFields[DevChannelIndex]); } set { SanitizeChannelNames(value); DataFields[DevChannelIndex] = JsonConvert.SerializeObject(value); } } /// /// The channels to which watchdog messages are sent /// public List WatchdogChannels { get { return JsonConvert.DeserializeObject>(DataFields[WDChannelIndex]); } set { SanitizeChannelNames(value); DataFields[WDChannelIndex] = JsonConvert.SerializeObject(value); } } /// /// The channels to which game messages are sent /// public List GameChannels { get { return JsonConvert.DeserializeObject>(DataFields[GameChannelIndex]); } set { SanitizeChannelNames(value); DataFields[GameChannelIndex] = JsonConvert.SerializeObject(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 ChatProvider Provider { get { return (ChatProvider)Convert.ToInt32(DataFields[ProviderIndex]); } set { DataFields[ProviderIndex] = Convert.ToString((int)value); } } } /// /// Chat provider for IRC. Admin entries should be user nicknames in normal mode or required channel flags in special mode /// [DataContract] public sealed class IRCSetupInfo : ChatSetupInfo { 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 IRCSetupInfo(ChatSetupInfo baseInfo = null) : base(ChatProvider.IRC, baseInfo, FieldsLen) { if (!InitializeFields) return; 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 DiscordSetupInfo : ChatSetupInfo { 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 DiscordSetupInfo(ChatSetupInfo baseInfo = null) : base(ChatProvider.Discord, baseInfo, FieldsLen) { if (!InitializeFields) return; 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; } } } }