void HandleMessage([NotNull] string message) { if (message == null) { throw new ArgumentNullException("message"); } IRCMessage msg = IRC.MessageParser(message, ActualBotNick); var SendList = Server.Players.Where(p => !p.IsDeaf && !p.GlobalChatIgnore); #if DEBUG_IRC Logger.Log(LogType.IRC, "[{0}]: {1}", msg.Type, msg.RawMessage); #endif switch (msg.Type) { case IRCMessageType.Login: foreach (string channel in channelNames) { Send(IRCCommands.Join(channel)); } IsReady = true; AssignBotForInputParsing(); // bot should be ready to receive input after joining return; case IRCMessageType.Ping: // ping-pong Send(IRCCommands.Pong(msg.RawMessageArray[1].Substring(1))); return; case IRCMessageType.ChannelAction: case IRCMessageType.ChannelMessage: // channel chat if (!ResponsibleForInputParsing) { return; } string processedMessage = msg.Message; if (msg.Type == IRCMessageType.ChannelAction) { if (processedMessage.StartsWith("\u0001ACTION")) { processedMessage = processedMessage.Substring(8); } else { return; } } processedMessage = IRC.NonPrintableChars.Replace(processedMessage, ""); if (processedMessage.Length > 0) { if (msg.Type == IRCMessageType.ChannelAction) { SendList.Message("&g[Global] * {1} {2}", 0, ActualBotNick, msg.Nick, processedMessage); Logger.Log(LogType.GlobalChat, "[Global] * {1} {2}", ActualBotNick, msg.Nick, processedMessage); } else { SendList.Message("&g[Global] {1}: {2}", 0, ActualBotNick, msg.Nick, processedMessage); Logger.Log(LogType.GlobalChat, "[Global] {1}: {2}", ActualBotNick, msg.Nick, processedMessage); } } else if (msg.Message.StartsWith("#")) { SendList.Message("&g[Global] {1}: {2}", 0, ActualBotNick, msg.Nick, processedMessage.Substring(1)); Logger.Log(LogType.GlobalChat, "[Global] {1}: {2}", ActualBotNick, msg.Nick, processedMessage.Substring(1)); } return; case IRCMessageType.Join: if (!ResponsibleForInputParsing) { return; } if (msg.Nick.StartsWith("(")) { SendList.Message("&g[Global] Server {0} joined the GemsCraft Global Chat", 0, msg.Nick); Logger.Log(LogType.GlobalChat, "[Global] Server {0} joined the GemsCraft Global Chat", msg.Nick); } else { SendList.Message("&g[Global] {0} joined the GemsCraft Global Chat", 0, msg.Nick); Logger.Log(LogType.GlobalChat, "[Global] {0} joined the GemsCraft Global Chat", msg.Nick); } return; case IRCMessageType.Kick: string kicked = msg.RawMessageArray[3]; if (kicked == ActualBotNick) { Logger.Log(LogType.SystemActivity, "Bot was kicked from {0} by {1} ({2}), rejoining.", msg.Channel, msg.Nick, msg.Message); Thread.Sleep(ReconnectDelay); Send(IRCCommands.Join(msg.Channel)); } else { if (!ResponsibleForInputParsing) { return; } SendList.Message("&g[Global] {0} kicked {1} ({2})", 0, msg.Nick, kicked, msg.Message); Logger.Log(LogType.GlobalChat, "[Global] {0} kicked {1} ({2})", msg.Nick, kicked, msg.Message); } return; case IRCMessageType.Part: case IRCMessageType.Quit: if (!ResponsibleForInputParsing) { return; } SendList.Message("&g[Global] Server {0} left the GemsCraft Global Chat", MessageType.Chat, msg.Nick); Logger.Log(LogType.GlobalChat, "[Global] Server {0} left the GemsCraft Global Chat", msg.Nick); return; case IRCMessageType.NickChange: if (!ResponsibleForInputParsing) { return; } SendList.Message("&g[Global] {0} is now known as {1}", 0, msg.Nick, msg.Message); Logger.Log(LogType.GlobalChat, "[Global] {0} is now known as {1}", msg.Nick, msg.Message); return; case IRCMessageType.ErrorMessage: case IRCMessageType.Error: bool die = false; switch (msg.ReplyCode) { case IRCReplyCode.ErrorNicknameInUse: case IRCReplyCode.ErrorNicknameCollision: ActualBotNick = ActualBotNick.Remove(ActualBotNick.Length - 4) + "_"; Logger.Log(LogType.SystemActivity, "Error: Global Chat Nickname is already in use. Trying \"{0}\"", ActualBotNick); Send(IRCCommands.Nick(ActualBotNick)); break; case IRCReplyCode.ErrorBannedFromChannel: case IRCReplyCode.ErrorNoSuchChannel: Logger.Log(LogType.SystemActivity, "Error: {0} ({1})", msg.ReplyCode, msg.Channel); GCReady = false; die = true; break; //wont happen case IRCReplyCode.ErrorBadChannelKey: Logger.Log(LogType.SystemActivity, "Error: Channel password required for {0}. GemsCraft does not currently support passworded channels.", msg.Channel); die = true; GCReady = false; break; default: Logger.Log(LogType.SystemActivity, "Error ({0}): {1}", msg.ReplyCode, msg.RawMessage); GCReady = false; break; } if (die) { Logger.Log(LogType.SystemActivity, "Error: Disconnecting from Global Chat."); reconnect = false; DisconnectThread(); } return; case IRCMessageType.QueryAction: // TODO: PMs Logger.Log(LogType.SystemActivity, "Query: {0}", msg.RawMessage); break; case IRCMessageType.Kill: Logger.Log(LogType.SystemActivity, "Bot was killed from {0} by {1} ({2}), reconnecting.", hostName, msg.Nick, msg.Message); reconnect = true; isConnected = false; return; } }
// runs in its own thread, started from Connect() void IoThread() { string outputLine = ""; lastMessageSent = DateTime.UtcNow; do { try { ActualBotNick = desiredBotNick; reconnect = false; Logger.Log(LogType.SystemActivity, "Connecting to GemsCraft Global Chat as {2}", hostName, port, ActualBotNick); if (ActualBotNick == "Custom Minecraft Server (GemsCraft)") { Logger.Log(LogType.Error, "You must set a server name to connect to global chat."); reconnect = false; DisconnectThread(); } else { Connect(); } // register Send(IRCCommands.User(ActualBotNick, 8, ConfigKey.ServerName.GetString())); Send(IRCCommands.Nick(ActualBotNick)); while (isConnected && !reconnect) { Thread.Sleep(10); if (localQueue.Count > 0 && DateTime.UtcNow.Subtract(lastMessageSent).TotalMilliseconds >= SendDelay && localQueue.TryDequeue(out outputLine)) { writer.Write(outputLine + "\r\n"); lastMessageSent = DateTime.UtcNow; writer.Flush(); } if (OutputQueue.Count > 0 && DateTime.UtcNow.Subtract(lastMessageSent).TotalMilliseconds >= SendDelay && OutputQueue.TryDequeue(out outputLine)) { writer.Write(outputLine + "\r\n"); lastMessageSent = DateTime.UtcNow; writer.Flush(); } if (client.Client.Available > 0) { string line = reader.ReadLine(); if (line == null) { break; } HandleMessage(line); } } } catch (SocketException) { Logger.Log(LogType.Warning, "GlobalChat: Socket Error. Disconnected. Will retry in {0} seconds.", ReconnectDelay / 1000); reconnect = true; } catch (IOException) { Logger.Log(LogType.Warning, "GlobalChat: IO Error. Disconnected. Will retry in {0} seconds.", ReconnectDelay / 1000); reconnect = true; #if !DEBUG } catch (Exception ex) { Logger.Log(LogType.Error, "GlobalChat: {0}", ex); reconnect = true; #endif } if (reconnect) { Thread.Sleep(ReconnectDelay); } } while (reconnect); }