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);
}
}
}
}
}