using System; using System.Linq; using System.Collections.Generic; using System.Web.Script.Serialization; using TGServerService.ChatCommands; using TGServerService.ChatProviders; using TGServiceInterface; using TGServiceInterface.Components; namespace TGServerService { sealed partial class ServerInstance : ITGChat { /// /// Used for indicating unintialized encrypted data /// public const string UninitializedString = "NEEDS INITIALIZING"; /// /// List of s for the /// IList ChatProviders; /// /// Used for multithreading safety /// object ChatLock = new object(); /// /// Set up the for the /// public void InitChat() { var infos = InitProviderInfos(); ChatProviders = new List(infos.Count); foreach (var info in infos) { IChatProvider chatProvider; try { switch (info.Provider) { case ChatProvider.Discord: chatProvider = new DiscordChatProvider(info); break; case ChatProvider.IRC: chatProvider = new IRCChatProvider(info); break; default: WriteError(String.Format("Invalid chat provider: {0}", info.Provider), EventID.InvalidChatProvider); continue; } } catch (Exception e) { WriteError(String.Format("Failed to start chat provider {0}! Error: {1}", info.Provider, e.ToString()), EventID.ChatProviderStartFail); continue; } chatProvider.OnChatMessage += ChatProvider_OnChatMessage; var res = chatProvider.Connect(); if (res != null) WriteWarning(String.Format("Unable to connect to chat! Provider {0}, Error: {1}", chatProvider.GetType().ToString(), res), EventID.ChatConnectFail); ChatProviders.Add(chatProvider); } } /// /// Implementation of that recieves messages from all channels of all connected /// /// The that heard the /// The user who wrote the /// The channel the is from /// The recieved message /// if is considered a chat admin, otherwise /// if is an admin channel, otherwise private void ChatProvider_OnChatMessage(IChatProvider ChatProvider, string speaker, string channel, string message, bool isAdmin, bool isAdminChannel) { var splits = message.Trim().Split(' '); if (splits.Length == 1 && splits[0] == "") { ChatProvider.SendMessageDirect("Hi!", channel); return; } var asList = new List(splits); Command.OutputProcVar.Value = (m) => ChatProvider.SendMessageDirect(m, channel); ChatCommand.CommandInfo.Value = new CommandInfo() { IsAdmin = isAdmin, IsAdminChannel = isAdminChannel, Speaker = speaker, Server = this, }; WriteInfo(String.Format("Chat Command from {0} ({2}): {1}", speaker, String.Join(" ", asList), channel), EventID.ChatCommand); if (ServerChatCommands == null) LoadServerChatCommands(); new RootChatCommand(ServerChatCommands).DoRun(asList); } /// /// Properly shuts down all /// void DisposeChat() { var infosList = new List>(); foreach (var ChatProvider in ChatProviders) { infosList.Add(ChatProvider.ProviderInfo().DataFields); ChatProvider.Dispose(); } ChatProviders = null; var rawdata = new JavaScriptSerializer().Serialize(infosList); Config.ChatProviderData = Helpers.EncryptData(rawdata, out string entrp); Config.ChatProviderEntropy = entrp; } /// public IList ProviderInfos() { var infosList = new List(); foreach (var chatProvider in ChatProviders) infosList.Add(chatProvider.ProviderInfo()); return infosList; } /// /// Returns a list of s loaded from the config or the defaults if none are set /// /// A list of s loaded from the config or the defaults if none are set IList InitProviderInfos() { lock (ChatLock) { var rawdata = Config.ChatProviderData; if (rawdata == UninitializedString) return new List() { new IRCSetupInfo() { Nickname = Config.Name }, new DiscordSetupInfo() }; string plaintext; try { plaintext = Helpers.DecryptData(rawdata, Config.ChatProviderEntropy); var lists = new JavaScriptSerializer().Deserialize>>(plaintext); var output = new List(lists.Count); var foundirc = 0; var founddiscord = 0; foreach (var l in lists) { var info = new ChatSetupInfo(l); if (info.Provider == ChatProvider.Discord) ++founddiscord; else if (info.Provider == ChatProvider.IRC) ++foundirc; output.Add(info); } if (foundirc != 1 || founddiscord != 1) throw new Exception(); return output; } catch { Config.ChatProviderData = UninitializedString; } } //if we get here we want to retry return InitProviderInfos(); } /// public string SetProviderInfo(ChatSetupInfo info) { info.AdminList.RemoveAll(x => String.IsNullOrWhiteSpace(x)); info.AdminChannels.RemoveAll(x => String.IsNullOrWhiteSpace(x)); info.GameChannels.RemoveAll(x => String.IsNullOrWhiteSpace(x)); info.DevChannels.RemoveAll(x => String.IsNullOrWhiteSpace(x)); info.WatchdogChannels.RemoveAll(x => String.IsNullOrWhiteSpace(x)); try { lock (ChatLock) { foreach (var ChatProvider in ChatProviders) if (info.Provider == ChatProvider.ProviderInfo().Provider) return ChatProvider.SetProviderInfo(info); return "Error: Invalid provider: " + info.Provider.ToString(); } } catch (Exception e) { return e.ToString(); } } /// public bool Connected(ChatProvider providerType) { foreach (var I in ChatProviders) if (I.ProviderInfo().Provider == providerType) return I.Connected(); return false; } /// /// Reconnect servers that are enabled and disconnected. Checked every time DreamDaemon reboots /// void ChatConnectivityCheck() { foreach (ChatProvider I in Enum.GetValues(typeof(ChatProvider))) if(!Connected(I)) Reconnect(I); } /// public string Reconnect(ChatProvider providerType) { foreach (var I in ChatProviders) if (I.ProviderInfo().Provider == providerType) return I.Reconnect(); return "Could not find specified provider!"; } /// /// Broadcast a message to appropriate channels based on the message type /// /// The message to send /// The message type public void SendMessage(string msg, MessageType mt) { lock (ChatLock) { foreach (var ChatProvider in ChatProviders) try { ChatProvider.SendMessage(msg, mt); } catch (Exception e) { WriteWarning(String.Format("Chat broadcast failed (Provider: {3}) (Flags: {0}) (Message: {1}): {2}", mt, msg, e.ToString(), ChatProvider.ProviderInfo().Provider), EventID.ChatBroadcastFail); } } } } }