diff --git a/OpenSim/Region/Environment/ModuleLoader.cs b/OpenSim/Region/Environment/ModuleLoader.cs index 09f2ddad6c..f06fa0f80b 100644 --- a/OpenSim/Region/Environment/ModuleLoader.cs +++ b/OpenSim/Region/Environment/ModuleLoader.cs @@ -93,15 +93,15 @@ namespace OpenSim.Region.Environment m_loadedSharedModules.Add(dynamicModule.Name, dynamicModule); } - ChatModule chat = new ChatModule(); - if (m_loadedSharedModules.ContainsKey(chat.Name)) - { - m_log.ErrorFormat("[MODULES]: Module name \"{0}\" already exists in module list. Module type {1} not added!", chat.Name, "ChatModule"); - } - else - { - m_loadedSharedModules.Add(chat.Name, chat); - } + // ChatModule chat = new ChatModule(); + // if (m_loadedSharedModules.ContainsKey(chat.Name)) + // { + // m_log.ErrorFormat("[MODULES]: Module name \"{0}\" already exists in module list. Module type {1} not added!", chat.Name, "ChatModule"); + // } + // else + // { + // m_loadedSharedModules.Add(chat.Name, chat); + // } InstantMessageModule imMod = new InstantMessageModule(); if (m_loadedSharedModules.ContainsKey(imMod.Name)) diff --git a/OpenSim/Region/Environment/Modules/Avatar/Chat/ChatModule.cs b/OpenSim/Region/Environment/Modules/Avatar/Chat/ChatModule.cs index 34a604e49b..5ee07ff5b5 100644 --- a/OpenSim/Region/Environment/Modules/Avatar/Chat/ChatModule.cs +++ b/OpenSim/Region/Environment/Modules/Avatar/Chat/ChatModule.cs @@ -48,6 +48,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat private const int DEBUG_CHANNEL = 2147483647; + private bool m_enabled = true; private int m_saydistance = 30; private int m_shoutdistance = 100; private int m_whisperdistance = 10; @@ -56,8 +57,23 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat internal object m_syncInit = new object(); #region IRegionModule Members - public void Initialise(Scene scene, IConfigSource config) + public virtual void Initialise(Scene scene, IConfigSource config) { + // wrap this in a try block so that defaults will work if + // the config file doesn't specify otherwise. + try + { + m_enabled = config.Configs["Chat"].GetBoolean("enabled", m_enabled); + if (!m_enabled) return; + + m_whisperdistance = config.Configs["Chat"].GetInt("whisper_distance", m_whisperdistance); + m_saydistance = config.Configs["Chat"].GetInt("say_distance", m_saydistance); + m_shoutdistance = config.Configs["Chat"].GetInt("shout_distance", m_shoutdistance); + } + catch (Exception) + { + } + lock (m_syncInit) { if (!m_scenes.Contains(scene)) @@ -69,34 +85,23 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat } } - // wrap this in a try block so that defaults will work if - // the config file doesn't specify otherwise. - try - { - m_whisperdistance = config.Configs["Chat"].GetInt("whisper_distance", m_whisperdistance); - m_saydistance = config.Configs["Chat"].GetInt("say_distance", m_saydistance); - m_shoutdistance = config.Configs["Chat"].GetInt("shout_distance", m_shoutdistance); - } - catch (Exception) - { - } m_log.InfoFormat("[CHAT] initialized for {0} w:{1} s:{2} S:{3}", scene.RegionInfo.RegionName, m_whisperdistance, m_saydistance, m_shoutdistance); } - public void PostInitialise() + public virtual void PostInitialise() { } - public void Close() + public virtual void Close() { } - public string Name + public virtual string Name { get { return "ChatModule"; } } - public bool IsSharedModule + public virtual bool IsSharedModule { get { return true; } } @@ -104,16 +109,9 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat #endregion - public void OnNewClient(IClientAPI client) + public virtual void OnNewClient(IClientAPI client) { - try - { - client.OnChatFromClient += OnChatFromClient; - } - catch (Exception ex) - { - m_log.Error("[CHAT]: NewClient exception trap:" + ex.ToString()); - } + client.OnChatFromClient += OnChatFromClient; } public virtual void OnChatFromClient(Object sender, OSChatMessage e) @@ -132,45 +130,76 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat return; } - string message = e.Message; - if (e.Channel == DEBUG_CHANNEL) e.Type = ChatTypeEnum.DebugChannel; + // string message = e.Message; + // if (e.Channel == DEBUG_CHANNEL) e.Type = ChatTypeEnum.DebugChannel; - ScenePresence avatar = scene.GetScenePresence(e.Sender.AgentId); - Vector3 fromPos = avatar.AbsolutePosition; - Vector3 regionPos = new Vector3(scene.RegionInfo.RegionLocX * Constants.RegionSize, - scene.RegionInfo.RegionLocY * Constants.RegionSize, 0); - string fromName = avatar.Firstname + " " + avatar.Lastname; - UUID fromID = e.Sender.AgentId; + // ScenePresence avatar = scene.GetScenePresence(e.Sender.AgentId); + // Vector3 fromPos = avatar.AbsolutePosition; + // Vector3 regionPos = new Vector3(scene.RegionInfo.RegionLocX * Constants.RegionSize, + // scene.RegionInfo.RegionLocY * Constants.RegionSize, 0); + // string fromName = avatar.Firstname + " " + avatar.Lastname; + // UUID fromID = e.Sender.AgentId; - DeliverChatToAvatars(fromPos, regionPos, fromID, fromName, e.Type, ChatSourceType.Agent, message); + // DeliverChatToAvatars(fromPos, regionPos, fromID, fromName, e.Type, ChatSourceType.Agent, message); + DeliverChatToAvatars(ChatSourceType.Agent, e); } - public void OnChatFromWorld(Object sender, OSChatMessage e) + public virtual void OnChatFromWorld(Object sender, OSChatMessage e) { - Scene scene = (Scene) e.Scene; - // early return if not on public or debug channel if (e.Channel != 0 && e.Channel != DEBUG_CHANNEL) return; - // Filled in since it's easier than rewriting right now. - Vector3 fromPos = e.Position; + // // Filled in since it's easier than rewriting right now. + // Vector3 fromPos = e.Position; + // Vector3 regionPos = new Vector3(scene.RegionInfo.RegionLocX * Constants.RegionSize, + // scene.RegionInfo.RegionLocY * Constants.RegionSize, 0); + + // string fromName = e.From; + // string message = e.Message; + // UUID fromID = e.SenderUUID; + + // if (e.Channel == DEBUG_CHANNEL) + // e.Type = ChatTypeEnum.DebugChannel; + + // DeliverChatToAvatars(fromPos, regionPos, fromID, fromName, e.Type, ChatSourceType.Object, message); + DeliverChatToAvatars(ChatSourceType.Object, e); + } + + protected virtual void DeliverChatToAvatars(ChatSourceType sourceType, OSChatMessage c) + { + string fromName = c.From; + UUID fromID = UUID.Zero; + string message = c.Message; + IScene scene = c.Scene; + Vector3 fromPos = c.Position; Vector3 regionPos = new Vector3(scene.RegionInfo.RegionLocX * Constants.RegionSize, scene.RegionInfo.RegionLocY * Constants.RegionSize, 0); - string fromName = e.From; - string message = e.Message; - UUID fromID = e.SenderUUID; + if (c.Channel == DEBUG_CHANNEL) c.Type = ChatTypeEnum.DebugChannel; - if (e.Channel == DEBUG_CHANNEL) - e.Type = ChatTypeEnum.DebugChannel; + switch (sourceType) + { + case ChatSourceType.Agent: + if (!(scene is Scene)) + { + m_log.WarnFormat("[CHAT] scene {0} is not a Scene object, cannot obtain scene presence for {1}", + scene.RegionInfo.RegionName, c.Sender.AgentId); + return; + } + ScenePresence avatar = (scene as Scene).GetScenePresence(c.Sender.AgentId); + fromPos = avatar.AbsolutePosition; + fromName = avatar.Firstname + " " + avatar.Lastname; + fromID = c.Sender.AgentId; - DeliverChatToAvatars(fromPos, regionPos, fromID, fromName, e.Type, ChatSourceType.Object, message); - } + break; - protected void DeliverChatToAvatars(Vector3 pos, Vector3 regionPos, UUID uuid, string name, - ChatTypeEnum chatType, ChatSourceType sourceType, string message) - { - // iterate over message + case ChatSourceType.Object: + fromID = c.SenderUUID; + + break; + } + + // TODO: iterate over message if (message.Length >= 1000) // libomv limit message = message.Substring(0, 1000); @@ -178,20 +207,42 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat { s.ForEachScenePresence(delegate(ScenePresence presence) { - TrySendChatMessage(presence, pos, regionPos, uuid, name, - chatType, message, sourceType); + TrySendChatMessage(presence, fromPos, regionPos, fromID, fromName, + c.Type, message, sourceType); }); } } + // protected virtual void DeliverChatToAvatars(Vector3 pos, Vector3 regionPos, UUID uuid, string name, + // ChatTypeEnum chatType, ChatSourceType sourceType, string message) + // { + // // iterate over message + // if (message.Length >= 1000) // libomv limit + // message = message.Substring(0, 1000); - public void OnChatBroadcast(Object sender, OSChatMessage c) + // foreach (Scene s in m_scenes) + // { + // s.ForEachScenePresence(delegate(ScenePresence presence) + // { + // TrySendChatMessage(presence, pos, regionPos, uuid, name, + // chatType, message, sourceType); + // }); + // } + // } + + + public virtual void OnChatBroadcast(Object sender, OSChatMessage c) { - // We only want to relay stuff on channel 0 and on the debug channel - if (c.Channel != 0 && c.Channel != DEBUG_CHANNEL) return; + // unless the chat to be broadcast is of type Region, we + // drop it if its channel is neither 0 nor DEBUG_CHANNEL + if (c.Channel != 0 && c.Channel != DEBUG_CHANNEL && c.Type != ChatTypeEnum.Region) return; + ChatTypeEnum cType = c.Type; if (c.Channel == DEBUG_CHANNEL) - c.Type = ChatTypeEnum.DebugChannel; + cType = ChatTypeEnum.DebugChannel; + + if (cType == ChatTypeEnum.Region) + cType = ChatTypeEnum.Say; if (c.Message.Length > 1100) c.Message = c.Message.Substring(0, 1000); @@ -199,6 +250,15 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat // broadcast chat works by redistributing every incoming chat // message to each avatar in the scene. Vector3 pos = new Vector3(128, 128, 30); + + UUID fromID = UUID.Zero; + ChatSourceType sourceType = ChatSourceType.Object; + if (null != c.Sender) + { + fromID = c.Sender.AgentId; + sourceType = ChatSourceType.Agent; + } + ((Scene)c.Scene).ForEachScenePresence( delegate(ScenePresence presence) { @@ -214,29 +274,15 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat (((SceneObjectPart)c.SenderObject).OwnerID != client.AgentId)) return; - if (null == c.SenderObject) - { - // chat from agent (avatar) - client.SendChatMessage(c.Message, (byte)c.Type, - pos, c.From, UUID.Zero, - (byte)ChatSourceType.Agent, - (byte)ChatAudibleLevel.Fully); - } - else - { - // chat from object - client.SendChatMessage(c.Message, (byte)c.Type, - pos, c.From, UUID.Zero, - (byte)ChatSourceType.Object, - (byte)ChatAudibleLevel.Fully); - } + client.SendChatMessage(c.Message, (byte)cType, pos, c.From, fromID, + (byte)sourceType, (byte)ChatAudibleLevel.Fully); }); } - private void TrySendChatMessage(ScenePresence presence, Vector3 fromPos, Vector3 regionPos, - UUID fromAgentID, string fromName, ChatTypeEnum type, - string message, ChatSourceType src) + protected virtual void TrySendChatMessage(ScenePresence presence, Vector3 fromPos, Vector3 regionPos, + UUID fromAgentID, string fromName, ChatTypeEnum type, + string message, ChatSourceType src) { // don't send stuff to child agents if (presence.IsChildAgent) return; diff --git a/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs b/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs index e992862521..05718d87e6 100644 --- a/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs +++ b/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs @@ -48,10 +48,8 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat private const int DEBUG_CHANNEL = 2147483647; - private string m_defaultzone = null; - private IRCChatModule m_irc = null; - private Thread m_irc_connector = null; + private IRCConnector m_irc = null; private string m_last_leaving_user = null; private string m_last_new_user = null; @@ -61,6 +59,12 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat internal object m_syncLogout = new object(); private IConfig m_config; + private string m_defaultzone = null; + private bool m_commandsEnabled = false; + private int m_commandChannel = -1; + private bool m_relayPrivateChannels = false; + private int m_relayChannelOut = -1; + private bool m_clientReporting = true; #region IRegionModule Members @@ -86,6 +90,15 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat return; } + m_commandsEnabled = m_config.GetBoolean("commands_enabled", m_commandsEnabled); + m_commandChannel = m_config.GetInt("commandchannel", m_commandChannel); // compat + m_commandChannel = m_config.GetInt("command_channel", m_commandChannel); + + m_relayPrivateChannels = m_config.GetBoolean("relay_private_channels", m_relayPrivateChannels); + m_relayChannelOut = m_config.GetInt("relay_private_channel_out", m_relayChannelOut); + + m_clientReporting = m_config.GetBoolean("report_clients", m_clientReporting); + lock (m_syncInit) { @@ -110,43 +123,21 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat // setup IRC Relay if (m_irc == null) { - m_irc = new IRCChatModule(config); + m_irc = new IRCConnector(config); } - - if (m_irc_connector == null) - { - m_irc_connector = new Thread(IRCConnectRun); - m_irc_connector.Name = "IRCConnectorThread"; - m_irc_connector.IsBackground = true; - } - m_log.InfoFormat("[IRC] initialized for {0}, nick: {1} ", scene.RegionInfo.RegionName, - m_defaultzone); + m_irc.AddScene(scene); + + m_log.InfoFormat("[IRC] initialized for {0}, nick: {1}, commands {2}, private channels {3}", + scene.RegionInfo.RegionName, m_defaultzone, + m_commandsEnabled ? "enabled" : "not enabled", + m_relayPrivateChannels ? "relayed" : "not relayed"); } } public void PostInitialise() { if (null == m_irc || !m_irc.Enabled) return; - - try - { - //m_irc.Connect(m_scenes); - if (m_irc_connector == null) - { - m_irc_connector = new Thread(IRCConnectRun); - m_irc_connector.Name = "IRCConnectorThread"; - m_irc_connector.IsBackground = true; - } - - if (!m_irc_connector.IsAlive) - { - m_irc_connector.Start(); - ThreadTracker.Add(m_irc_connector); - } - } - catch (Exception) - { - } + m_irc.Start(); } public void Close() @@ -172,14 +163,21 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat #region ISimChat Members - public void OnSimChat(Object sender, OSChatMessage e) + public void OnSimChat(Object sender, OSChatMessage c) { - m_log.DebugFormat("[IRC] heard on channel {0}: {1}", e.Channel.ToString(), e.Message); + // early return if nothing to forward + if (c.Message.Length == 0) return; - // We only want to relay stuff on channel 0 - if (e.Channel == m_irc.m_commandChannel) + // early return if this comes from the IRC forwarder + if (m_irc.Equals(sender)) return; + + m_log.DebugFormat("[IRC] heard on channel {0}: {1}", c.Channel, c.Message); + + // check for commands coming from avatars or in-world + // object (if commands are enabled) + if (m_commandsEnabled && c.Channel == m_commandChannel) { - string[] messages = e.Message.Split(' '); + string[] messages = c.Message.Split(' '); string command = messages[0].ToLower(); try @@ -187,146 +185,135 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat switch (command) { case "channel": - m_irc.m_channel = messages[1]; + m_irc.IrcChannel = messages[1]; break; case "close": m_irc.Close(); break; case "connect": - m_irc.Connect(m_scenes); + m_irc.Connect(); break; case "nick": - m_irc.m_nick = messages[1]; + m_irc.Nick = messages[1]; break; case "port": - m_irc.m_port = Convert.ToUInt32(messages[1]); + m_irc.Port = Convert.ToUInt32(messages[1]); break; case "reconnect": m_irc.Reconnect(); break; case "server": - m_irc.m_server = messages[1]; + m_irc.Server = messages[1]; break; - case "verbosity": - m_irc.m_verbosity = Convert.ToInt32(messages[1]); + case "client-reporting": + m_irc.ClientReporting = Convert.ToBoolean(messages[1]); + break; case "in-channel": - m_irc.m_messageOutChannel = Convert.ToInt32(messages[1]); + m_irc.RelayChannel = Convert.ToInt32(messages[1]); break; case "out-channel": - m_irc.m_messageInChannel = Convert.ToInt32(messages[1]); + m_relayChannelOut = Convert.ToInt32(messages[1]); break; default: - m_irc.Send(e.Message); + m_irc.Send(c.Message); break; } } - catch - { } + catch(Exception ex) + { + m_log.DebugFormat("[IRC] error processing in-world command channel input: {0}", ex); + } } - if (e.Channel != m_irc.m_messageOutChannel) return; - if (e.Message.Length == 0) return; - - // not interested in our own babblings - if (m_irc.Equals(sender)) return; + // drop all messages coming in on a private channel, + // except if we are relaying private channels, in which + // case we drop if the private channel is not the + // configured m_relayChannelOut + if (m_relayPrivateChannels) + { + if (c.Channel != 0 && c.Channel != DEBUG_CHANNEL && c.Channel != m_relayChannelOut) + { + m_log.DebugFormat("[IRC] dropping message {0} on channel {1}", c, c.Channel); + return; + } + } + else if (c.Channel != 0 && c.Channel != DEBUG_CHANNEL) + { + m_log.DebugFormat("[IRC] dropping message {0} on channel {1}", c, c.Channel); + return; + } ScenePresence avatar = null; - Scene scene = (Scene)e.Scene; + Scene scene = (Scene)c.Scene; if (scene == null) scene = m_scenes[0]; - // Filled in since it's easier than rewriting right now. - string fromName = e.From; + string fromName = c.From; - if (e.Sender != null) + if (c.Sender != null) { - avatar = scene.GetScenePresence(e.Sender.AgentId); + avatar = scene.GetScenePresence(c.Sender.AgentId); + if (avatar != null) fromName = avatar.Name; } - if (avatar != null) + if (!m_irc.Connected) { - fromName = avatar.Name; + m_log.WarnFormat("[IRC] IRCConnector not connected: dropping message from {0}", fromName); + return; } - // Try to reconnect to server if not connected - if (m_irc.Enabled && !m_irc.Connected) + if (null != avatar) { - // In a non-blocking way. Eventually the connector will get it started - try + string msg = c.Message; + if (msg.StartsWith("/me ")) + msg = String.Format("{0} {1}", fromName, c.Message.Substring(4)); + + m_irc.PrivMsg(fromName, scene.RegionInfo.RegionName, msg); + } + else + { + //Message came from an object + char[] splits = { ',' }; + string[] tokens = c.Message.Split(splits,3); // This is certainly wrong + if (tokens.Length == 3) { - if (m_irc_connector == null) + if (tokens[0] == m_irc.m_accessPassword) // This is my really simple check { - m_irc_connector = new Thread(IRCConnectRun); - m_irc_connector.Name = "IRCConnectorThread"; - m_irc_connector.IsBackground = true; + m_log.DebugFormat("[IRC] message from object {0}, {1}", tokens[0], tokens[1]); + m_irc.PrivMsg(tokens[1], scene.RegionInfo.RegionName, tokens[2]); } - - if (!m_irc_connector.IsAlive) + else { - m_irc_connector.Start(); - ThreadTracker.Add(m_irc_connector); + m_log.WarnFormat("[IRC] prim security key mismatch <{0}> not <{1}>", tokens[0], m_irc.m_accessPassword); } } - catch (Exception) - { - } } - - if (e.Message.StartsWith("/me ") && (null != avatar)) - e.Message = String.Format("{0} {1}", fromName, e.Message.Substring(4)); - - // this is to keep objects from talking to IRC - if (m_irc.Connected && (avatar != null)) - m_irc.PrivMsg(fromName, scene.RegionInfo.RegionName, e.Message); - - // Handle messages from objects - if (m_irc.Connected && (null == avatar)) - { - //Message came from an object - char[] splits = { ',' }; - string[] tokens = e.Message.Split(splits,3); // This is certainly wrong - if (tokens.Length == 3) - { - if (tokens[0] == m_irc.m_accessPassword) // This is my really simple check - { - m_log.DebugFormat("[IRC] message from object {0}, {1}", tokens[0], tokens[1]); - m_irc.PrivMsg(tokens[1], scene.RegionInfo.RegionName, tokens[2]); - } - else - { - m_log.WarnFormat("[IRC] prim security key mismatch <{0}> not <{1}>", tokens[0], m_irc.m_accessPassword); - } - } - } } - #endregion public void OnNewClient(IClientAPI client) { try { - // client.OnChatFromViewer += OnSimChat; client.OnLogout += OnClientLoggedOut; client.OnConnectionClosed += OnClientLoggedOut; if (client.Name != m_last_new_user) { - if ((m_irc.Enabled) && (m_irc.Connected) && (m_irc.m_verbosity >= 1)) + if ((m_irc.Enabled) && (m_irc.Connected) && (m_clientReporting)) { m_log.DebugFormat("[IRC] {0} logging on", client.Name); - m_irc.PrivMsg(m_irc.Nick, "Sim", - String.Format("notices {0} logging on", client.Name)); + m_irc.PrivMsg(m_irc.Nick, "Sim", String.Format("notices {0} logging on", client.Name)); } m_last_new_user = client.Name; } } catch (Exception ex) { - m_log.Error("[IRC]: NewClient exception trap:" + ex.ToString()); + m_log.Error("[IRC]: OnNewClient exception trap:" + ex.ToString()); } } @@ -334,7 +321,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat { try { - if ((m_irc.Enabled) && (m_irc.Connected) && (m_irc.m_verbosity >= 2)) + if ((m_irc.Enabled) && (m_irc.Connected) && (m_clientReporting)) { string regionName = presence.Scene.RegionInfo.RegionName; string clientName = String.Format("{0} {1}", presence.Firstname, presence.Lastname); @@ -351,7 +338,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat { try { - if ((m_irc.Enabled) && (m_irc.Connected) && (m_irc.m_verbosity >= 2)) + if ((m_irc.Enabled) && (m_irc.Connected) && (m_clientReporting)) { string regionName = presence.Scene.RegionInfo.RegionName; string clientName = String.Format("{0} {1}", presence.Firstname, presence.Lastname); @@ -371,16 +358,14 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat { try { - if ((m_irc.Enabled) && (m_irc.Connected) && (m_irc.m_verbosity >= 1)) + if ((m_irc.Enabled) && (m_irc.Connected) && (m_clientReporting)) { - // handles simple case. May not work for hundred connecting in per second. - // and the NewClients calles getting interleved - // but filters out multiple reports + // handles simple case. May not work for + // hundred connecting in per second. and + // OnNewClients calle getting interleaved but + // filters out multiple reports if (client.Name != m_last_leaving_user) { - // Console.WriteLine("Avatar was seen logging out."); - // Console.ReadLine(); - // Console.WriteLine(); m_last_leaving_user = client.Name; m_irc.PrivMsg(m_irc.Nick, "Sim", String.Format("notices {0} logging out", client.Name)); m_log.InfoFormat("[IRC]: {0} logging out", client.Name); @@ -396,623 +381,5 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat } } } - - // if IRC is enabled then just keep trying using a monitor thread - public void IRCConnectRun() - { - while (m_irc.Enabled) - { - if (!m_irc.Connected) - { - m_irc.Connect(m_scenes); - } - Thread.Sleep(15000); - } - } - } - - internal class IRCChatModule - { - #region ErrorReplies enum - - public enum ErrorReplies - { - NotRegistered = 451, // ":You have not registered" - NicknameInUse = 433 // " :Nickname is already in use" - } - - #endregion - - #region Replies enum - - public enum Replies - { - MotdStart = 375, // ":- Message of the day - " - Motd = 372, // ":- " - EndOfMotd = 376 // ":End of /MOTD command" - } - - #endregion - - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - private Thread listener; - - private string m_basenick = null; - public string m_channel = null; - private bool m_nrnick = false; - private bool m_connected = false; - private bool m_enabled = false; - public int m_commandChannel = -1; - - public int m_verbosity = 1; - public int m_messageOutChannel = 0; // Will be the message channel we listen to - public int m_messageInChannel = 0; // Will be the message channel where we forward msgs - public string m_accessPassword = "badkitty"; - public bool m_useWorldComm = false; // true if we want chat to be localized, false if we want to broadcast to the entire region - - public List m_last_scenes = null; - public string m_nick = null; - public uint m_port = 6668; - private string m_privmsgformat = "PRIVMSG {0} :<{1} in {2}>: {3}"; - private StreamReader m_reader; - private List m_scenes = null; - public string m_server = null; - - private NetworkStream m_stream = null; - internal object m_syncConnect = new object(); - private TcpClient m_tcp; - private string m_user = "USER OpenSimBot 8 * :I'm an OpenSim to IRC bot"; - private StreamWriter m_writer; - - private Thread pingSender; - - public IRCChatModule(IConfigSource config) - { - m_nick = "OSimBot" + Util.RandomClass.Next(1, 99); - m_tcp = null; - m_writer = null; - m_reader = null; - - // configuration in OpenSim.ini - // [IRC] - // server = chat.freenode.net - // nick = OSimBot_mysim - // nicknum = true - // ;nicknum set to true appends a 2 digit random number to the nick - // ;username = USER OpenSimBot 8 * :I'm a OpenSim to irc bot - // ; username is the IRC command line sent - // ; USER * : - // channel = #opensim-regions - // port = 6667 - // ;MSGformat fields : 0=botnick, 1=user, 2=region, 3=message - // ;for : : - // ;msgformat = "PRIVMSG {0} :<{1} in {2}>: {3}" - // ;for : - : - // ;msgformat = "PRIVMSG {0} : {3} - {1} of {2}" - // ;for : - from : - // ;msgformat = "PRIVMSG {0} : {3} - from {1}" - // Traps I/O disconnects so it does not crash the sim - // Trys to reconnect if disconnected and someone says something - // Tells IRC server "QUIT" when doing a close (just to be nice) - // Default port back to 6667 - - try - { - m_server = config.Configs["IRC"].GetString("server"); - m_nick = config.Configs["IRC"].GetString("nick"); - m_basenick = m_nick; - m_nrnick = config.Configs["IRC"].GetBoolean("nicknum", true); - m_channel = config.Configs["IRC"].GetString("channel"); - m_port = (uint)config.Configs["IRC"].GetInt("port", (int)m_port); - m_user = config.Configs["IRC"].GetString("username", m_user); - m_privmsgformat = config.Configs["IRC"].GetString("msgformat", m_privmsgformat); - m_commandChannel = config.Configs["IRC"].GetInt("commandchannel", m_commandChannel); - - m_verbosity = config.Configs["IRC"].GetInt("verbosity", m_verbosity); - m_messageOutChannel = config.Configs["IRC"].GetInt("outchannel", m_messageOutChannel); - m_messageInChannel = config.Configs["IRC"].GetInt("inchannel", m_messageInChannel); - m_accessPassword = config.Configs["IRC"].GetString("access_password",m_accessPassword); - m_useWorldComm = config.Configs["IRC"].GetBoolean("useworldcomm", m_useWorldComm); - - if (m_server != null && m_nick != null && m_channel != null) - { - if (m_nrnick == true) - { - m_nick = m_nick + Util.RandomClass.Next(1, 99); - } - m_enabled = true; - } - } - catch (Exception ex) - { - m_log.Error("[IRC]: Incomplete IRC configuration, skipping IRC bridge configuration"); - m_log.DebugFormat("[IRC] Incomplete IRC configuration: {0}", ex.ToString()); - } - } - - public bool Enabled - { - get { return m_enabled; } - } - - public bool Connected - { - get { return m_connected; } - } - - public string Nick - { - get { return m_nick; } - } - - public bool Connect(List scenes) - { - lock (m_syncConnect) - { - try - { - if (m_connected) return true; - - m_scenes = scenes; - if (m_last_scenes == null) - { - m_last_scenes = scenes; - } - - m_tcp = new TcpClient(m_server, (int)m_port); - m_stream = m_tcp.GetStream(); - m_reader = new StreamReader(m_stream); - m_writer = new StreamWriter(m_stream); - - m_log.DebugFormat("[IRC]: Connected to {0}:{1}", m_server, m_port); - - pingSender = new Thread(new ThreadStart(PingRun)); - pingSender.Name = "PingSenderThread"; - pingSender.IsBackground = true; - pingSender.Start(); - ThreadTracker.Add(pingSender); - - listener = new Thread(new ThreadStart(ListenerRun)); - listener.Name = "IRCChatModuleListenerThread"; - listener.IsBackground = true; - listener.Start(); - ThreadTracker.Add(listener); - - m_writer.WriteLine(m_user); - m_writer.Flush(); - m_writer.WriteLine(String.Format("NICK {0}", m_nick)); - m_writer.Flush(); - m_writer.WriteLine(String.Format("JOIN {0}", m_channel)); - m_writer.Flush(); - m_log.Info("[IRC]: Connection fully established"); - m_connected = true; - } - catch (Exception e) - { - m_log.ErrorFormat("[IRC] cannot connect to {0}:{1}: {2}", - m_server, m_port, e.Message); - } - m_log.Debug("[IRC] Connected"); - return m_connected; - } - } - - public void Reconnect() - { - m_connected = false; - try - { - listener.Abort(); - pingSender.Abort(); - m_writer.Close(); - m_reader.Close(); - m_tcp.Close(); - } - catch (Exception) - { - } - - if (m_enabled) - { - Connect(m_last_scenes); - } - } - - public void PrivMsg(string from, string region, string msg) - { - m_log.DebugFormat("[IRC] Sending message to IRC from {0}: {1}", from, msg); - - // One message to the IRC server - try - { - m_writer.WriteLine(m_privmsgformat, m_channel, from, region, msg); - m_writer.Flush(); - m_log.InfoFormat("[IRC]: PrivMsg {0} in {1}: {2}", from, region, msg); - } - catch (IOException) - { - m_log.Error("[IRC]: Disconnected from IRC server.(PrivMsg)"); - Reconnect(); - } - catch (Exception ex) - { - m_log.ErrorFormat("[IRC]: PrivMsg exception trap: {0}", ex.ToString()); - } - } - - public void Send(string msg) - { - try - { - m_writer.WriteLine(msg); - m_writer.Flush(); - m_log.Info("IRC: Sent command string: " + msg); - } - catch (IOException) - { - m_log.Error("[IRC]: Disconnected from IRC server.(PrivMsg)"); - Reconnect(); - } - catch (Exception ex) - { - m_log.ErrorFormat("[IRC]: PrivMsg exception trap: {0}", ex.ToString()); - } - - } - - - private Dictionary ExtractMsg(string input) - { - //examines IRC commands and extracts any private messages - // which will then be reboadcast in the Sim - - m_log.Info("[IRC]: ExtractMsg: " + input); - Dictionary result = null; - //string regex = @":(?\w*)!~(?\S*) PRIVMSG (?\S+) :(?.*)"; - string regex = @":(?[\w-]*)!(?\S*) PRIVMSG (?\S+) :(?.*)"; - Regex RE = new Regex(regex, RegexOptions.Multiline); - MatchCollection matches = RE.Matches(input); - - // Get some direct matches $1 $4 is a - if ((matches.Count == 0) || (matches.Count != 1) || (matches[0].Groups.Count != 5)) - { - m_log.Info("[IRC]: Number of matches: " + matches.Count); - if (matches.Count > 0) - { - m_log.Info("[IRC]: Number of groups: " + matches[0].Groups.Count); - } - return null; - } - - result = new Dictionary(); - result.Add("nick", matches[0].Groups[1].Value); - result.Add("user", matches[0].Groups[2].Value); - result.Add("channel", matches[0].Groups[3].Value); - result.Add("msg", matches[0].Groups[4].Value); - - return result; - } - - public void PingRun() - { - // IRC keep alive thread - // send PING ever 15 seconds - while (m_enabled) - { - try - { - if (m_connected == true) - { - m_writer.WriteLine(String.Format("PING :{0}", m_server)); - m_writer.Flush(); - Thread.Sleep(15000); - } - } - catch (IOException) - { - if (m_enabled) - { - m_log.Error("[IRC]: Disconnected from IRC server.(PingRun)"); - Reconnect(); - } - } - catch (Exception ex) - { - m_log.ErrorFormat("[IRC]: PingRun exception trap: {0}\n{1}", ex.ToString(), ex.StackTrace); - } - } - } - - static private Vector3 pos = new Vector3(128, 128, 20); - public void ListenerRun() - { - string inputLine; - - while (m_enabled) - { - try - { - while ((m_connected == true) && ((inputLine = m_reader.ReadLine()) != null)) - { - m_log.Info("[IRC]: " + inputLine); - if (inputLine.Contains(m_channel)) - { - Dictionary data = ExtractMsg(inputLine); - // Any chat ??? - if (data != null) - { - OSChatMessage c = new OSChatMessage(); - c.Message = data["msg"]; - c.Type = ChatTypeEnum.Say; - c.Channel = m_messageInChannel; - c.Position = pos; - c.From = data["nick"]; - c.Sender = null; - c.SenderUUID = UUID.Zero; - - // is message "\001ACTION foo - // bar\001"? -> "/me foo bar" - if ((1 == c.Message[0]) && c.Message.Substring(1).StartsWith("ACTION")) - c.Message = String.Format("/me {0}", c.Message.Substring(8, c.Message.Length - 9)); - - m_log.DebugFormat("[IRC] ListenerRun from: {0}, {1}", c.From, c.Message); - - foreach (Scene scene in m_scenes) - { - if (m_useWorldComm) - { - IWorldComm wComm = scene.RequestModuleInterface(); - wComm.DeliverMessage(ChatTypeEnum.Region, m_messageInChannel, c.From, UUID.Zero, c.Message); - } - else - { - c.Scene = scene; - scene.EventManager.TriggerOnChatBroadcast(this, c); - } - } - } - - Thread.Sleep(150); - continue; - } - - ProcessIRCCommand(inputLine); - Thread.Sleep(150); - } - } - catch (IOException) - { - if (m_enabled) - { - m_log.Error("[IRC]: ListenerRun IOException. Disconnected from IRC server ??? (ListenerRun)"); - Reconnect(); - } - } - catch (Exception ex) - { - m_log.ErrorFormat("[IRC]: ListenerRun exception trap: {0}\n{1}", ex.ToString(), ex.StackTrace); - } - } - } - - public void BroadcastSim(string sender, string format, params string[] args) - { - try - { - OSChatMessage c = new OSChatMessage(); - c.From = sender; - c.Message = String.Format(format, args); - c.Type = ChatTypeEnum.Region; // ChatTypeEnum.Say; - c.Channel = m_messageInChannel; - c.Position = new Vector3(128, 128, 20); - c.Sender = null; - c.SenderUUID = UUID.Zero; - - m_log.DebugFormat("[IRC] BroadcastSim from {0}: {1}", c.From, c.Message); - - foreach (Scene m_scene in m_scenes) - { - c.Scene = m_scene; - // m_scene.EventManager.TriggerOnChatBroadcast(this, c); - // m_scene.EventManager.TriggerOnChatFromWorld(this, c); - IWorldComm wComm = m_scene.RequestModuleInterface(); - wComm.DeliverMessage(ChatTypeEnum.Region, m_messageInChannel, sender, UUID.Zero, c.Message); - //IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface(); - //wComm.DeliverMessage(ChatTypeEnum.Region, channelID, m_host.Name, m_host.UUID, text); - - } - } - catch (Exception ex) // IRC gate should not crash Sim - { - m_log.ErrorFormat("[IRC]: BroadcastSim Exception Trap: {0}\n{1}", ex.ToString(), ex.StackTrace); - } - } - - public void ProcessIRCCommand(string command) - { - m_log.Debug("[IRC]: ProcessIRCCommand:" + command); - - string[] commArgs = new string[command.Split(' ').Length]; - string c_server = m_server; - - commArgs = command.Split(' '); - if (commArgs[0].Substring(0, 1) == ":") - { - commArgs[0] = commArgs[0].Remove(0, 1); - } - - if (commArgs[1] == "002") - { - // fetch the correct servername - // ex: irc.freenode.net -> brown.freenode.net/kornbluth.freenode.net/... - // irc.bluewin.ch -> irc1.bluewin.ch/irc2.bluewin.ch - - c_server = (commArgs[6].Split('['))[0]; - m_server = c_server; - } - - if (commArgs[0] == "ERROR") - { - m_log.ErrorFormat("[IRC]: IRC SERVER ERROR: {0}", command); - } - - if (commArgs[0] == "PING") - { - string p_reply = ""; - - for (int i = 1; i < commArgs.Length; i++) - { - p_reply += commArgs[i] + " "; - } - - m_writer.WriteLine(String.Format("PONG {0}", p_reply)); - m_writer.Flush(); - } - else if (commArgs[0] == c_server) - { - // server message - try - { - Int32 commandCode = Int32.Parse(commArgs[1]); - switch (commandCode) - { - case (int)ErrorReplies.NicknameInUse: - // Gen a new name - m_nick = m_basenick + Util.RandomClass.Next(1, 99); - m_log.ErrorFormat("[IRC]: IRC SERVER reports NicknameInUse, trying {0}", m_nick); - // Retry - m_writer.WriteLine(String.Format("NICK {0}", m_nick)); - m_writer.Flush(); - m_writer.WriteLine(String.Format("JOIN {0}", m_channel)); - m_writer.Flush(); - break; - case (int)ErrorReplies.NotRegistered: - break; - case (int)Replies.EndOfMotd: - break; - } - } - catch (Exception) - { - } - } - else - { - // Normal message - string commAct = commArgs[1]; - switch (commAct) - { - case "JOIN": - eventIrcJoin(commArgs); - break; - case "PART": - eventIrcPart(commArgs); - break; - case "MODE": - eventIrcMode(commArgs); - break; - case "NICK": - eventIrcNickChange(commArgs); - break; - case "KICK": - eventIrcKick(commArgs); - break; - case "QUIT": - eventIrcQuit(commArgs); - break; - case "PONG": - break; // that's nice - } - } - } - - public void eventIrcJoin(string[] commArgs) - { - string IrcChannel = commArgs[2]; - if (IrcChannel.StartsWith(":")) - IrcChannel = IrcChannel.Substring(1); - string IrcUser = commArgs[0].Split('!')[0]; - if (m_verbosity >= 1) - BroadcastSim(IrcUser, "/me joins {0}", IrcChannel); - } - - public void eventIrcPart(string[] commArgs) - { - string IrcChannel = commArgs[2]; - string IrcUser = commArgs[0].Split('!')[0]; - if (m_verbosity >= 2) - BroadcastSim(IrcUser, "/me parts {0}", IrcChannel); - } - - public void eventIrcMode(string[] commArgs) - { - string UserMode = ""; - for (int i = 3; i < commArgs.Length; i++) - { - UserMode += commArgs[i] + " "; - } - - if (UserMode.Substring(0, 1) == ":") - { - UserMode = UserMode.Remove(0, 1); - } - } - - public void eventIrcNickChange(string[] commArgs) - { - string UserOldNick = commArgs[0].Split('!')[0]; - string UserNewNick = commArgs[2].Remove(0, 1); - if (m_verbosity >= 2) - BroadcastSim(UserOldNick, "/me is now known as {0}", UserNewNick); - } - - public void eventIrcKick(string[] commArgs) - { - string UserKicker = commArgs[0].Split('!')[0]; - string UserKicked = commArgs[3]; - string IrcChannel = commArgs[2]; - string KickMessage = ""; - for (int i = 4; i < commArgs.Length; i++) - { - KickMessage += commArgs[i] + " "; - } - if (m_verbosity >= 1) - BroadcastSim(UserKicker, "/me kicks kicks {0} off {1} saying \"{2}\"", UserKicked, IrcChannel, KickMessage); - if (UserKicked == m_nick) - { - BroadcastSim(m_nick, "Hey, that was me!!!"); - } - } - - public void eventIrcQuit(string[] commArgs) - { - string IrcUser = commArgs[0].Split('!')[0]; - string QuitMessage = ""; - - for (int i = 2; i < commArgs.Length; i++) - { - QuitMessage += commArgs[i] + " "; - } - if (m_verbosity >= 1) - BroadcastSim(IrcUser, "/me quits saying \"{0}\"", QuitMessage); - } - - public void Close() - { - m_writer.WriteLine(String.Format("QUIT :{0} to {1} wormhole to {2} closing", - m_nick, m_channel, m_server)); - m_writer.Flush(); - - m_connected = false; - m_enabled = false; - - //listener.Abort(); - //pingSender.Abort(); - - m_writer.Close(); - m_reader.Close(); - m_stream.Close(); - m_tcp.Close(); - } } } diff --git a/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCConnector.cs b/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCConnector.cs new file mode 100644 index 0000000000..0eb303cf9a --- /dev/null +++ b/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCConnector.cs @@ -0,0 +1,710 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSim Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Sockets; +using System.Reflection; +using System.Text.RegularExpressions; +using System.Threading; +using OpenMetaverse; +using log4net; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Region.Environment.Interfaces; +using OpenSim.Region.Environment.Scenes; + +namespace OpenSim.Region.Environment.Modules.Avatar.Chat +{ + public class IRCConnector + { + #region ErrorReplies enum + + public enum ErrorReplies + { + NotRegistered = 451, // ":You have not registered" + NicknameInUse = 433 // " :Nickname is already in use" + } + + #endregion + + #region Replies enum + + public enum Replies + { + MotdStart = 375, // ":- Message of the day - " + Motd = 372, // ":- " + EndOfMotd = 376 // ":End of /MOTD command" + } + + #endregion + + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private Thread m_listener = null; + private Thread m_watchdog = null; + private Thread m_pinger = null; + + private bool m_randomizeNick = true; + + public string m_baseNick = null; + private string m_nick = null; + public string Nick + { + get { return m_baseNick; } + set { m_baseNick = value; } + } + + private bool m_enabled = false; + public bool Enabled + { + get { return m_enabled; } + } + + private bool m_connected = false; + public bool Connected + { + get { return m_connected; } + } + + private string m_ircChannel; + public string IrcChannel + { + get { return m_ircChannel; } + + set { m_ircChannel = value; } + } + + private bool m_relayPrivateChannels = false; + public bool RelayPrivateChannels + { + get { return m_relayPrivateChannels; } + set { m_relayPrivateChannels = value; } + } + + private int m_relayChannel = 0; + public int RelayChannel + { + get { return m_relayChannel; } + set { m_relayChannel = value; } + } + + private bool m_clientReporting = true; + public bool ClientReporting + { + get { return m_clientReporting; } + set { m_clientReporting = value; } + } + + private uint m_port = 6667; + public uint Port + { + get { return m_port; } + set { m_port = value; } + } + + private string m_server = null; + public string Server + { + get { return m_server; } + set { m_server = value; } + } + + public string m_accessPassword = "badkitty"; + + private string m_privmsgformat = "PRIVMSG {0} :<{1} in {2}>: {3}"; + private StreamReader m_reader; + private List m_scenes = new List(); + + private NetworkStream m_stream = null; + internal object m_syncConnect = new object(); + private TcpClient m_tcp; + private string m_user = "USER OpenSimBot 8 * :I'm an OpenSim to IRC bot"; + private StreamWriter m_writer; + + + public IRCConnector(IConfigSource config) + { + m_tcp = null; + m_writer = null; + m_reader = null; + + // configuration in OpenSim.ini + // [IRC] + // server = chat.freenode.net + // nick = OSimBot_mysim + // nicknum = true + // ;nicknum set to true appends a 2 digit random number to the nick + // ;username = USER OpenSimBot 8 * :I'm a OpenSim to irc bot + // ; username is the IRC command line sent + // ; USER * : + // channel = #opensim-regions + // port = 6667 + // ;MSGformat fields : 0=botnick, 1=user, 2=region, 3=message + // ;for : : + // ;msgformat = "PRIVMSG {0} :<{1} in {2}>: {3}" + // ;for : - : + // ;msgformat = "PRIVMSG {0} : {3} - {1} of {2}" + // ;for : - from : + // ;msgformat = "PRIVMSG {0} : {3} - from {1}" + // Traps I/O disconnects so it does not crash the sim + // Trys to reconnect if disconnected and someone says something + // Tells IRC server "QUIT" when doing a close (just to be nice) + // Default port back to 6667 + + try + { + m_server = config.Configs["IRC"].GetString("server"); + m_baseNick = config.Configs["IRC"].GetString("nick", "OSimBot"); + + m_randomizeNick = config.Configs["IRC"].GetBoolean("randomize_nick", m_randomizeNick); + m_randomizeNick = config.Configs["IRC"].GetBoolean("nicknum", m_randomizeNick); // compat + m_ircChannel = config.Configs["IRC"].GetString("channel"); + m_port = (uint)config.Configs["IRC"].GetInt("port", (int)m_port); + m_user = config.Configs["IRC"].GetString("username", m_user); + m_privmsgformat = config.Configs["IRC"].GetString("msgformat", m_privmsgformat); + // m_commandChannel = config.Configs["IRC"].GetInt("commandchannel", m_commandChannel); + + // m_verbosity = config.Configs["IRC"].GetInt("verbosity", m_verbosity); + m_clientReporting = config.Configs["IRC"].GetInt("verbosity", 2) > 0; + m_clientReporting = config.Configs["IRC"].GetBoolean("report_clients", m_clientReporting); + + // m_messageOutChannel = config.Configs["IRC"].GetInt("outchannel", m_messageOutChannel); + // m_messageInChannel = config.Configs["IRC"].GetInt("inchannel", m_messageInChannel); + m_relayPrivateChannels = config.Configs["IRC"].GetBoolean("relay_private_channels", m_relayPrivateChannels); + m_relayPrivateChannels = config.Configs["IRC"].GetBoolean("useworldcomm", m_relayPrivateChannels); //compat + m_relayChannel = config.Configs["IRC"].GetInt("relay_private_channel_in", m_relayChannel); + m_relayChannel = config.Configs["IRC"].GetInt("inchannel", m_relayChannel); + m_accessPassword = config.Configs["IRC"].GetString("access_password",m_accessPassword); + // m_useWorldComm = config.Configs["IRC"].GetBoolean("useworldcomm", m_useWorldComm); + + if (m_server != null && m_baseNick != null && m_ircChannel != null) + { + if (m_randomizeNick) + { + m_nick = m_baseNick + Util.RandomClass.Next(1, 99); + } + m_enabled = true; + } + } + catch (Exception ex) + { + m_log.Error("[IRCConnector]: Incomplete IRC configuration, skipping IRC bridge configuration"); + m_log.DebugFormat("[IRCConnector] Incomplete IRC configuration: {0}", ex.ToString()); + } + + if (null == m_watchdog) + { + m_watchdog = new Thread(WatchdogRun); + m_watchdog.Name = "IRCWatchdog"; + m_watchdog.IsBackground = true; + } + } + + public void Start() + { + if (!m_watchdog.IsAlive) + { + m_watchdog.Start(); + ThreadTracker.Add(m_watchdog); + } + } + + public void AddScene(Scene scene) + { + lock(m_syncConnect) m_scenes.Add(scene); + } + + public bool Connect() + { + lock (m_syncConnect) + { + try + { + if (m_connected) return true; + + m_tcp = new TcpClient(m_server, (int)m_port); + m_stream = m_tcp.GetStream(); + m_reader = new StreamReader(m_stream); + m_writer = new StreamWriter(m_stream); + + m_log.DebugFormat("[IRCConnector]: Connected to {0}:{1}", m_server, m_port); + + m_pinger = new Thread(new ThreadStart(PingRun)); + m_pinger.Name = "PingSenderThread"; + m_pinger.IsBackground = true; + m_pinger.Start(); + ThreadTracker.Add(m_pinger); + + m_listener = new Thread(new ThreadStart(ListenerRun)); + m_listener.Name = "IRCConnectorListenerThread"; + m_listener.IsBackground = true; + m_listener.Start(); + ThreadTracker.Add(m_listener); + + m_writer.WriteLine(m_user); + m_writer.Flush(); + m_writer.WriteLine(String.Format("NICK {0}", m_nick)); + m_writer.Flush(); + m_writer.WriteLine(String.Format("JOIN {0}", m_ircChannel)); + m_writer.Flush(); + m_log.Info("[IRCConnector]: Connection fully established"); + m_connected = true; + } + catch (Exception e) + { + m_log.ErrorFormat("[IRCConnector] cannot connect to {0}:{1}: {2}", + m_server, m_port, e.Message); + } + m_log.Debug("[IRCConnector] Connected"); + return m_connected; + } + } + + public void Reconnect() + { + m_connected = false; + try + { + m_listener.Abort(); + m_pinger.Abort(); + + m_writer.Close(); + m_reader.Close(); + + m_tcp.Close(); + } + catch (Exception) + { + } + + if (m_enabled) + { + Connect(); + } + } + + public void PrivMsg(string from, string region, string msg) + { + m_log.DebugFormat("[IRCConnector] Sending message to IRC from {0}: {1}", from, msg); + + // One message to the IRC server + try + { + m_writer.WriteLine(m_privmsgformat, m_ircChannel, from, region, msg); + m_writer.Flush(); + m_log.InfoFormat("[IRCConnector]: PrivMsg {0} in {1}: {2}", from, region, msg); + } + catch (IOException) + { + m_log.Error("[IRCConnector]: Disconnected from IRC server.(PrivMsg)"); + Reconnect(); + } + catch (Exception ex) + { + m_log.ErrorFormat("[IRCConnector]: PrivMsg exception trap: {0}", ex.ToString()); + } + } + + public void Send(string msg) + { + try + { + m_writer.WriteLine(msg); + m_writer.Flush(); + m_log.Info("IRC: Sent command string: " + msg); + } + catch (IOException) + { + m_log.Error("[IRCConnector]: Disconnected from IRC server.(PrivMsg)"); + Reconnect(); + } + catch (Exception ex) + { + m_log.ErrorFormat("[IRCConnector]: PrivMsg exception trap: {0}", ex.ToString()); + } + + } + + + private Dictionary ExtractMsg(string input) + { + //examines IRC commands and extracts any private messages + // which will then be reboadcast in the Sim + + m_log.Info("[IRCConnector]: ExtractMsg: " + input); + Dictionary result = null; + string regex = @":(?[\w-]*)!(?\S*) PRIVMSG (?\S+) :(?.*)"; + Regex RE = new Regex(regex, RegexOptions.Multiline); + MatchCollection matches = RE.Matches(input); + + // Get some direct matches $1 $4 is a + if ((matches.Count == 0) || (matches.Count != 1) || (matches[0].Groups.Count != 5)) + { + // m_log.Info("[IRCConnector]: Number of matches: " + matches.Count); + // if (matches.Count > 0) + // { + // m_log.Info("[IRCConnector]: Number of groups: " + matches[0].Groups.Count); + // } + return null; + } + + result = new Dictionary(); + result.Add("nick", matches[0].Groups[1].Value); + result.Add("user", matches[0].Groups[2].Value); + result.Add("channel", matches[0].Groups[3].Value); + result.Add("msg", matches[0].Groups[4].Value); + + return result; + } + + public void PingRun() + { + // IRC keep alive thread + // send PING ever 15 seconds + while (m_enabled) + { + try + { + if (m_connected == true) + { + m_writer.WriteLine(String.Format("PING :{0}", m_server)); + m_writer.Flush(); + Thread.Sleep(15000); + } + } + catch (IOException) + { + if (m_enabled) + { + m_log.Error("[IRCConnector]: Disconnected from IRC server.(PingRun)"); + Reconnect(); + } + } + catch (Exception ex) + { + m_log.ErrorFormat("[IRCConnector]: PingRun exception trap: {0}\n{1}", ex.ToString(), ex.StackTrace); + } + } + } + + static private Vector3 pos = new Vector3(128, 128, 20); + public void ListenerRun() + { + string inputLine; + + while (m_enabled) + { + try + { + while ((m_connected) && ((inputLine = m_reader.ReadLine()) != null)) + { + // m_log.Info("[IRCConnector]: " + inputLine); + + if (inputLine.Contains(m_ircChannel)) + { + Dictionary data = ExtractMsg(inputLine); + // Any chat ??? + if (data != null) + { + OSChatMessage c = new OSChatMessage(); + c.Message = data["msg"]; + c.Type = ChatTypeEnum.Region; + c.Position = pos; + c.Channel = m_relayPrivateChannels ? m_relayChannel : 0; + c.From = data["nick"]; + c.Sender = null; + c.SenderUUID = UUID.Zero; + + // is message "\001ACTION foo + // bar\001"? -> "/me foo bar" + if ((1 == c.Message[0]) && c.Message.Substring(1).StartsWith("ACTION")) + c.Message = String.Format("/me {0}", c.Message.Substring(8, c.Message.Length - 9)); + + m_log.DebugFormat("[IRCConnector] ListenerRun from: {0}, {1}", c.From, c.Message); + + foreach (Scene scene in m_scenes) + { + c.Scene = scene; + scene.EventManager.TriggerOnChatBroadcast(this, c); + } + } + + Thread.Sleep(150); + continue; + } + + ProcessIRCCommand(inputLine); + Thread.Sleep(150); + } + } + catch (IOException) + { + if (m_enabled) + { + m_log.Error("[IRCConnector]: ListenerRun IOException. Disconnected from IRC server ??? (ListenerRun)"); + Reconnect(); + } + } + catch (Exception ex) + { + m_log.ErrorFormat("[IRCConnector]: ListenerRun exception trap: {0}\n{1}", ex.ToString(), ex.StackTrace); + } + } + } + + public void BroadcastSim(string sender, string format, params string[] args) + { + try + { + OSChatMessage c = new OSChatMessage(); + c.From = sender; + c.Message = String.Format(format, args); + c.Type = ChatTypeEnum.Region; // ChatTypeEnum.Say; + c.Channel = m_relayPrivateChannels ? m_relayChannel : 0; + c.Position = new Vector3(128, 128, 20); + c.Sender = null; + c.SenderUUID = UUID.Zero; + + m_log.DebugFormat("[IRCConnector] BroadcastSim from {0}: {1}", c.From, c.Message); + + foreach (Scene scene in m_scenes) + { + c.Scene = scene; + scene.EventManager.TriggerOnChatBroadcast(this, c); + // // m_scene.EventManager.TriggerOnChatFromWorld(this, c); + // IWorldComm wComm = m_scene.RequestModuleInterface(); + // wComm.DeliverMessage(ChatTypeEnum.Region, m_messageInChannel, sender, UUID.Zero, c.Message); + // //IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface(); + // //wComm.DeliverMessage(ChatTypeEnum.Region, channelID, m_host.Name, m_host.UUID, text); + + } + } + catch (Exception ex) // IRC gate should not crash Sim + { + m_log.ErrorFormat("[IRCConnector]: BroadcastSim Exception Trap: {0}\n{1}", ex.ToString(), ex.StackTrace); + } + } + + public void ProcessIRCCommand(string command) + { + // m_log.Debug("[IRCConnector]: ProcessIRCCommand:" + command); + + string[] commArgs = new string[command.Split(' ').Length]; + string c_server = m_server; + + commArgs = command.Split(' '); + if (commArgs[0].Substring(0, 1) == ":") + { + commArgs[0] = commArgs[0].Remove(0, 1); + } + + if (commArgs[1] == "002") + { + // fetch the correct servername + // ex: irc.freenode.net -> brown.freenode.net/kornbluth.freenode.net/... + // irc.bluewin.ch -> irc1.bluewin.ch/irc2.bluewin.ch + + c_server = (commArgs[6].Split('['))[0]; + m_server = c_server; + } + + if (commArgs[0] == "ERROR") + { + m_log.ErrorFormat("[IRCConnector]: IRC SERVER ERROR: {0}", command); + } + + if (commArgs[0] == "PING") + { + string p_reply = ""; + + for (int i = 1; i < commArgs.Length; i++) + { + p_reply += commArgs[i] + " "; + } + + m_writer.WriteLine(String.Format("PONG {0}", p_reply)); + m_writer.Flush(); + } + else if (commArgs[0] == c_server) + { + // server message + try + { + Int32 commandCode = Int32.Parse(commArgs[1]); + switch (commandCode) + { + case (int)ErrorReplies.NicknameInUse: + // Gen a new name + m_nick = m_baseNick + Util.RandomClass.Next(1, 99); + m_log.ErrorFormat("[IRCConnector]: IRC SERVER reports NicknameInUse, trying {0}", m_nick); + // Retry + m_writer.WriteLine(String.Format("NICK {0}", m_nick)); + m_writer.Flush(); + m_writer.WriteLine(String.Format("JOIN {0}", m_ircChannel)); + m_writer.Flush(); + break; + case (int)ErrorReplies.NotRegistered: + break; + case (int)Replies.EndOfMotd: + break; + } + } + catch (Exception) + { + } + } + else + { + // Normal message + string commAct = commArgs[1]; + switch (commAct) + { + case "JOIN": + eventIrcJoin(commArgs); + break; + case "PART": + eventIrcPart(commArgs); + break; + case "MODE": + eventIrcMode(commArgs); + break; + case "NICK": + eventIrcNickChange(commArgs); + break; + case "KICK": + eventIrcKick(commArgs); + break; + case "QUIT": + eventIrcQuit(commArgs); + break; + case "PONG": + break; // that's nice + } + } + } + + public void eventIrcJoin(string[] commArgs) + { + string IrcChannel = commArgs[2]; + if (IrcChannel.StartsWith(":")) + IrcChannel = IrcChannel.Substring(1); + string IrcUser = commArgs[0].Split('!')[0]; + if (m_clientReporting) + BroadcastSim(IrcUser, "/me joins {0}", IrcChannel); + } + + public void eventIrcPart(string[] commArgs) + { + string IrcChannel = commArgs[2]; + string IrcUser = commArgs[0].Split('!')[0]; + if (m_clientReporting) + BroadcastSim(IrcUser, "/me parts {0}", IrcChannel); + } + + public void eventIrcMode(string[] commArgs) + { + string UserMode = ""; + for (int i = 3; i < commArgs.Length; i++) + { + UserMode += commArgs[i] + " "; + } + + if (UserMode.Substring(0, 1) == ":") + { + UserMode = UserMode.Remove(0, 1); + } + } + + public void eventIrcNickChange(string[] commArgs) + { + string UserOldNick = commArgs[0].Split('!')[0]; + string UserNewNick = commArgs[2].Remove(0, 1); + if (m_clientReporting) + BroadcastSim(UserOldNick, "/me is now known as {0}", UserNewNick); + } + + public void eventIrcKick(string[] commArgs) + { + string UserKicker = commArgs[0].Split('!')[0]; + string UserKicked = commArgs[3]; + string IrcChannel = commArgs[2]; + string KickMessage = ""; + for (int i = 4; i < commArgs.Length; i++) + { + KickMessage += commArgs[i] + " "; + } + if (m_clientReporting) + BroadcastSim(UserKicker, "/me kicks kicks {0} off {1} saying \"{2}\"", UserKicked, IrcChannel, KickMessage); + if (UserKicked == m_nick) + { + BroadcastSim(m_nick, "Hey, that was me!!!"); + } + } + + public void eventIrcQuit(string[] commArgs) + { + string IrcUser = commArgs[0].Split('!')[0]; + string QuitMessage = ""; + + for (int i = 2; i < commArgs.Length; i++) + { + QuitMessage += commArgs[i] + " "; + } + if (m_clientReporting) + BroadcastSim(IrcUser, "/me quits saying \"{0}\"", QuitMessage); + } + + public void Close() + { + m_writer.WriteLine(String.Format("QUIT :{0} to {1} wormhole to {2} closing", + m_nick, m_ircChannel, m_server)); + m_writer.Flush(); + + m_connected = false; + m_enabled = false; + + //listener.Abort(); + //pingSender.Abort(); + + m_writer.Close(); + m_reader.Close(); + m_stream.Close(); + m_tcp.Close(); + } + + protected void WatchdogRun() + { + while (m_enabled) + { + if (!m_connected) Connect(); + Thread.Sleep(15000); + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Region/Environment/Modules/Avatar/Concierge/ConciergeModule.cs b/OpenSim/Region/Environment/Modules/Avatar/Concierge/ConciergeModule.cs index d9730dda43..6cc622eaca 100644 --- a/OpenSim/Region/Environment/Modules/Avatar/Concierge/ConciergeModule.cs +++ b/OpenSim/Region/Environment/Modules/Avatar/Concierge/ConciergeModule.cs @@ -38,24 +38,28 @@ using OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.Environment.Interfaces; using OpenSim.Region.Environment.Scenes; +using OpenSim.Region.Environment.Modules.Avatar.Chat; namespace OpenSim.Region.Environment.Modules.Avatar.Concierge { - public class ConciergeModule : IRegionModule + public class ConciergeModule : ChatModule, IRegionModule { private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private const int DEBUG_CHANNEL = 2147483647; private int _conciergeChannel = 42; - private List _scenes = new List(); + private List _scenes = new List(); + private List _conciergedScenes = new List(); private IConfig _config; private string _whoami = "conferencier"; + private bool _replacingChatModule = false; + private Regex _regions = null; internal object _syncy = new object(); #region IRegionModule Members - public void Initialise(Scene scene, IConfigSource config) + public override void Initialise(Scene scene, IConfigSource config) { try { @@ -78,25 +82,58 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Concierge return; } - if (_config != null) + // check whether ChatModule has been disabled: if yes, + // then we'll "stand in" + try { - _conciergeChannel = config.Configs["Concierge"].GetInt("concierge_channel", _conciergeChannel); - _whoami = _config.GetString("whoami", "conferencier"); + if (config.Configs["Chat"] == null) + { + _replacingChatModule = false; + } + else + { + _replacingChatModule = !config.Configs["Chat"].GetBoolean("enabled", true); + } } + catch (Exception) + { + _replacingChatModule = false; + } + _log.InfoFormat("[Concierge] {0} ChatModule", _replacingChatModule ? "replacing" : "not replacing"); + + + // take note of concierge channel and of identity + _conciergeChannel = config.Configs["Concierge"].GetInt("concierge_channel", _conciergeChannel); + _whoami = _config.GetString("whoami", "conferencier"); _log.InfoFormat("[Concierge] reporting as \"{0}\" to our users", _whoami); + // calculate regions Regex + if (_regions == null) + { + string regions = _config.GetString("regions", String.Empty); + if (!String.IsNullOrEmpty(regions)) + { + _regions = new Regex(regions, RegexOptions.Compiled | RegexOptions.IgnoreCase); + } + } + lock (_syncy) { if (!_scenes.Contains(scene)) { _scenes.Add(scene); + + if (_regions.IsMatch(scene.RegionInfo.RegionName)) + _conciergedScenes.Add(scene); + // subscribe to NewClient events scene.EventManager.OnNewClient += OnNewClient; - scene.EventManager.OnNewClient += OnNewClient; - // subscribe to *Chat events and FilterChat* events - scene.EventManager.OnChatFromWorld += OnSimChat; - scene.EventManager.OnChatBroadcast += OnSimBroadcast; + // subscribe to *Chat events + scene.EventManager.OnChatFromWorld += OnChatFromWorld; + if (!_replacingChatModule) + scene.EventManager.OnChatFromClient += OnChatFromClient; + scene.EventManager.OnChatBroadcast += OnChatBroadcast; // subscribe to agent change events scene.EventManager.OnMakeRootAgent += OnMakeRootAgent; @@ -106,20 +143,20 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Concierge _log.InfoFormat("[Concierge] initialized for {0}", scene.RegionInfo.RegionName); } - public void PostInitialise() + public override void PostInitialise() { } - public void Close() + public override void Close() { } - public string Name + public override string Name { get { return "ConciergeModule"; } } - public bool IsSharedModule + public override bool IsSharedModule { get { return true; } } @@ -127,44 +164,90 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Concierge #endregion #region ISimChat Members - public void OnSimBroadcast(Object sender, OSChatMessage c) + public override void OnChatBroadcast(Object sender, OSChatMessage c) { - // log to buffer? - return; - } - - public void OnSimChat(Object sender, OSChatMessage c) - { - if (_conciergeChannel == c.Channel) - { - // concierge request: interpret - return; - } - - if (0 == c.Channel || DEBUG_CHANNEL == c.Channel) + if (_replacingChatModule) { - // if (_amplify) - // { - - // } - - // log as avatar/prim chat - return; + // distribute chat message to each and every avatar in + // the region + base.OnChatBroadcast(sender, c); } + // TODO: capture logic return; } + public override void OnChatFromClient(Object sender, OSChatMessage c) + { + if (_replacingChatModule) + { + if (_conciergedScenes.Contains(c.Scene)) + { + // replacing ChatModule: need to redistribute + // ChatFromClient to interested subscribers + Scene scene = (Scene)c.Scene; + scene.EventManager.TriggerOnChatFromClient(sender, c); + + // when we are replacing ChatModule, we treat + // OnChatFromClient like OnChatBroadcast for + // concierged regions, effectively extending the + // range of chat to cover the whole + // region. however, we don't do this for whisper + // (got to have some privacy) + if (c.Type != ChatTypeEnum.Whisper) + { + base.OnChatBroadcast(sender, c); + return; + } + } + + // redistribution will be done by base class + base.OnChatFromClient(sender, c); + } + + // TODO: capture chat + return; + } + + public override void OnChatFromWorld(Object sender, OSChatMessage c) + { + if (_replacingChatModule) + { + if (_conciergedScenes.Contains(c.Scene)) + { + // when we are replacing ChatModule, we treat + // OnChatFromClient like OnChatBroadcast for + // concierged regions, effectively extending the + // range of chat to cover the whole + // region. however, we don't do this for whisper + // (got to have some privacy) + if (c.Type != ChatTypeEnum.Whisper) + { + base.OnChatBroadcast(sender, c); + return; + } + } + + base.OnChatFromWorld(sender, c); + } + return; + } #endregion - public void OnNewClient(IClientAPI client) + public override void OnNewClient(IClientAPI client) { client.OnLogout += OnClientLoggedOut; client.OnConnectionClosed += OnClientLoggedOut; + if (_replacingChatModule) + client.OnChatFromClient += OnChatFromClient; - _log.DebugFormat("[Concierge] {0} logs on to {1}", client.Name, client.Scene.RegionInfo.RegionName); - AnnounceToAgentsRegion(client, String.Format("{0} logs on to {1}", client.Name, client.Scene.RegionInfo.RegionName)); + if (_conciergedScenes.Contains(client.Scene)) + { + _log.DebugFormat("[Concierge] {0} logs on to {1}", client.Name, client.Scene.RegionInfo.RegionName); + AnnounceToAgentsRegion(client, String.Format("{0} logs on to {1}", client.Name, + client.Scene.RegionInfo.RegionName)); + } } public void OnClientLoggedOut(IClientAPI client) @@ -172,32 +255,46 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Concierge client.OnLogout -= OnClientLoggedOut; client.OnConnectionClosed -= OnClientLoggedOut; - _log.DebugFormat("[Concierge] {0} logs off from {1}", client.Name, client.Scene.RegionInfo.RegionName); - AnnounceToAgentsRegion(client, String.Format("{0} logs off from {1}", client.Name, client.Scene.RegionInfo.RegionName)); + if (_conciergedScenes.Contains(client.Scene)) + { + _log.DebugFormat("[Concierge] {0} logs off from {1}", client.Name, client.Scene.RegionInfo.RegionName); + AnnounceToAgentsRegion(client, String.Format("{0} logs off from {1}", client.Name, + client.Scene.RegionInfo.RegionName)); + } } public void OnMakeRootAgent(ScenePresence agent) { - _log.DebugFormat("[Concierge] {0} enters {1}", agent.Name, agent.Scene.RegionInfo.RegionName); - AnnounceToAgentsRegion(agent, String.Format("{0} enters {1}", agent.Name, agent.Scene.RegionInfo.RegionName)); + if (_conciergedScenes.Contains(agent.Scene)) + { + _log.DebugFormat("[Concierge] {0} enters {1}", agent.Name, agent.Scene.RegionInfo.RegionName); + AnnounceToAgentsRegion(agent, String.Format("{0} enters {1}", agent.Name, + agent.Scene.RegionInfo.RegionName)); + } } public void OnMakeChildAgent(ScenePresence agent) { - _log.DebugFormat("[Concierge] {0} leaves {1}", agent.Name, agent.Scene.RegionInfo.RegionName); - AnnounceToAgentsRegion(agent, String.Format("{0} leaves {1}", agent.Name, agent.Scene.RegionInfo.RegionName)); + if (_conciergedScenes.Contains(agent.Scene)) + { + _log.DebugFormat("[Concierge] {0} leaves {1}", agent.Name, agent.Scene.RegionInfo.RegionName); + AnnounceToAgentsRegion(agent, String.Format("{0} leaves {1}", agent.Name, + agent.Scene.RegionInfo.RegionName)); + } } public void ClientLoggedOut(IClientAPI client) { - _log.DebugFormat("[Concierge] {0} logs out of {1}", client.Name, client.Scene.RegionInfo.RegionName); - AnnounceToAgentsRegion(client, String.Format("{0} logs out of {1}", client.Name, client.Scene.RegionInfo.RegionName)); + if (_conciergedScenes.Contains(client.Scene)) + { + _log.DebugFormat("[Concierge] {0} logs out of {1}", client.Name, client.Scene.RegionInfo.RegionName); + AnnounceToAgentsRegion(client, String.Format("{0} logs out of {1}", client.Name, client.Scene.RegionInfo.RegionName)); + } } - static private Vector3 posOfGod = new Vector3(128, 128, 9999); protected void AnnounceToAgentsRegion(IClientAPI client, string msg) diff --git a/OpenSim/Region/Environment/Modules/Scripting/WorldComm/WorldCommModule.cs b/OpenSim/Region/Environment/Modules/Scripting/WorldComm/WorldCommModule.cs index f8e933d78c..36add0643e 100644 --- a/OpenSim/Region/Environment/Modules/Scripting/WorldComm/WorldCommModule.cs +++ b/OpenSim/Region/Environment/Modules/Scripting/WorldComm/WorldCommModule.cs @@ -34,6 +34,10 @@ using OpenSim.Framework; using OpenSim.Region.Environment.Interfaces; using OpenSim.Region.Environment.Scenes; +// using log4net; +// using System.Reflection; + + /***************************************************** * * WorldCommModule @@ -83,6 +87,9 @@ namespace OpenSim.Region.Environment.Modules.Scripting.WorldComm { public class WorldCommModule : IRegionModule, IWorldComm { + // private static readonly ILog m_log = + // LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private ListenerManager m_listenerManager; private Queue m_pending; private Queue m_pendingQ; @@ -117,6 +124,7 @@ namespace OpenSim.Region.Environment.Modules.Scripting.WorldComm m_scene.RegisterModuleInterface(this); m_listenerManager = new ListenerManager(maxlisteners, maxhandles); m_scene.EventManager.OnChatFromClient += DeliverClientMessage; + m_scene.EventManager.OnChatBroadcast += DeliverClientMessage; m_pendingQ = new Queue(); m_pending = Queue.Synchronized(m_pendingQ); } @@ -197,6 +205,22 @@ namespace OpenSim.Region.Environment.Modules.Scripting.WorldComm m_listenerManager.DeleteListener(itemID); } + public void DeliverMessage(ChatTypeEnum type, int channel, string name, UUID id, string msg) + { + Vector3 position; + SceneObjectPart source; + ScenePresence avatar; + + if ((source = m_scene.GetSceneObjectPart(id)) != null) + position = source.AbsolutePosition; + else if ((avatar = m_scene.GetScenePresence(id)) != null) + position = avatar.AbsolutePosition; + else + return; + + DeliverMessage(type, channel, name, id, msg, position); + } + /// /// This method scans over the objects which registered an interest in listen callbacks. /// For everyone it finds, it checks if it fits the given filter. If it does, then @@ -210,39 +234,8 @@ namespace OpenSim.Region.Environment.Modules.Scripting.WorldComm /// name of sender (object or avatar) /// key of sender (object or avatar) /// msg to sent - public void DeliverMessage(ChatTypeEnum type, int channel, string name, UUID id, string msg) + public void DeliverMessage(ChatTypeEnum type, int channel, string name, UUID id, string msg, Vector3 position) { - SceneObjectPart source = null; - ScenePresence avatar = null; - Vector3 position; - - source = m_scene.GetSceneObjectPart(id); - if (source != null) - position = source.AbsolutePosition; - else { - avatar = m_scene.GetScenePresence(id); - if (avatar != null) - { - position = avatar.AbsolutePosition; - } - else - { - // This is potentially problematic, though I don't - // see how to take advantage of it, basically a request - // to send a message to the region does not have to come - // from something in the region (eg a plugin can send it) - if (type == ChatTypeEnum.Region) - { - position = new Vector3(128, 128, 20); - } - else - { - // bail out early, given source could not be found - return; - } - } - } - // Determine which listen event filters match the given set of arguments, this results // in a limited set of listeners, each belonging a host. If the host is in range, add them // to the pending queue. @@ -326,11 +319,6 @@ namespace OpenSim.Region.Environment.Modules.Scripting.WorldComm #endregion - // private void NewClient(IClientAPI client) - // { - // client.OnChatFromViewer += DeliverClientMessage; - // } - /******************************************************************** * * Listener Stuff @@ -339,11 +327,10 @@ namespace OpenSim.Region.Environment.Modules.Scripting.WorldComm private void DeliverClientMessage(Object sender, OSChatMessage e) { - DeliverMessage(e.Type, - e.Channel, - e.Sender.Name, - e.Sender.AgentId, - e.Message); + if (null != e.Sender) + DeliverMessage(e.Type, e.Channel, e.Sender.Name, e.Sender.AgentId, e.Message, e.Position); + else + DeliverMessage(e.Type, e.Channel, e.From, UUID.Zero, e.Message, e.Position); } public Object[] GetSerializationData(UUID itemID) diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index b3f7ba742c..4c4d1732ea 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -7238,7 +7238,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); - World.SimChatBroadcast(Utils.StringToBytes(msg), ChatTypeEnum.Owner, 0, m_host.AbsolutePosition, m_host.Name, m_host.UUID, false); + World.SimChatBroadcast(Utils.StringToBytes(msg), ChatTypeEnum.Owner, 0, + m_host.AbsolutePosition, m_host.Name, m_host.UUID, false); // IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface(); // wComm.DeliverMessage(ChatTypeEnum.Owner, 0, m_host.Name, m_host.UUID, msg); } diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 970bad1e8b..16caa0cec5 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -398,20 +398,21 @@ flush-on-error=true ;nick = OpenSimBotNameProbablyMakeThisShorter ;channel = #the_irc_channel_you_want_to_connect_to ;port = 6667 -;; channel to listen for configuration updates -;commandchannel = 2777 -;; 0 for no control messages, 2 for all control messages -;verbosity = 1 -;; bridging connections -;; outchannel -- channel to send messages out to the IRC bridge -;; inchannel -- channel to receive message from the IRC bridge +;; channel to listen for configuration commands +;commands_enabled = false +;command_channel = 2777 +;report_clients = true +;; relay private chat connections +;; relay_private_channels = true: will relay IRC chat from/to private in-world channels +;; relay_channel_out -- channel to send messages out to the IRC bridge +;; relay_channel_in -- channel to receive message from the IRC bridge ;; access_password -- simple security device -;inchannel = 2226 -;outchannel = 2225 +;relay_private_channels = false +;relay_channel_in = 2226 +;relay_channel_out = 2225 ;access_password = foobar + ;fallback_region = name of "default" region -;;useworldcomm = true will send IRC chat over the inchannel, false (default) will broadcast to all agents in the region -;useworldcomm = true ;MSGformat fields : 0=botnick, 1=user, 2=region, 3=message ; must start with "PRIVMSG {0} : " or irc server will get upset ;for : :