cleaning up IRCBridgeModule to allow for configuration from in-world,

chat relaying via private channels, and old IRCBridgeModule
behaviour. also cleaning up IRCBridgeModule's OpenSim.ini
configuration variable names (still supporting "old" variable
names). refactored IRCChatModule into IRCConnector and incorporating
watchdog from IRCBridgeModule into IRCConnector.

enabling ChatModule to be used as a super-class and utilizing it in
ConciergeModule.
0.6.0-stable
Dr Scofield 2008-10-20 17:31:54 +00:00
parent caf10245af
commit 72a388a7b6
8 changed files with 1131 additions and 922 deletions

View File

@ -93,15 +93,15 @@ namespace OpenSim.Region.Environment
m_loadedSharedModules.Add(dynamicModule.Name, dynamicModule); m_loadedSharedModules.Add(dynamicModule.Name, dynamicModule);
} }
ChatModule chat = new ChatModule(); // ChatModule chat = new ChatModule();
if (m_loadedSharedModules.ContainsKey(chat.Name)) // 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"); // m_log.ErrorFormat("[MODULES]: Module name \"{0}\" already exists in module list. Module type {1} not added!", chat.Name, "ChatModule");
} // }
else // else
{ // {
m_loadedSharedModules.Add(chat.Name, chat); // m_loadedSharedModules.Add(chat.Name, chat);
} // }
InstantMessageModule imMod = new InstantMessageModule(); InstantMessageModule imMod = new InstantMessageModule();
if (m_loadedSharedModules.ContainsKey(imMod.Name)) if (m_loadedSharedModules.ContainsKey(imMod.Name))

View File

@ -48,6 +48,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
private const int DEBUG_CHANNEL = 2147483647; private const int DEBUG_CHANNEL = 2147483647;
private bool m_enabled = true;
private int m_saydistance = 30; private int m_saydistance = 30;
private int m_shoutdistance = 100; private int m_shoutdistance = 100;
private int m_whisperdistance = 10; private int m_whisperdistance = 10;
@ -56,8 +57,23 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
internal object m_syncInit = new object(); internal object m_syncInit = new object();
#region IRegionModule Members #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) lock (m_syncInit)
{ {
if (!m_scenes.Contains(scene)) 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_log.InfoFormat("[CHAT] initialized for {0} w:{1} s:{2} S:{3}", scene.RegionInfo.RegionName,
m_whisperdistance, m_saydistance, m_shoutdistance); 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"; } get { return "ChatModule"; }
} }
public bool IsSharedModule public virtual bool IsSharedModule
{ {
get { return true; } get { return true; }
} }
@ -104,16 +109,9 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
#endregion #endregion
public void OnNewClient(IClientAPI client) public virtual void OnNewClient(IClientAPI client)
{ {
try client.OnChatFromClient += OnChatFromClient;
{
client.OnChatFromClient += OnChatFromClient;
}
catch (Exception ex)
{
m_log.Error("[CHAT]: NewClient exception trap:" + ex.ToString());
}
} }
public virtual void OnChatFromClient(Object sender, OSChatMessage e) public virtual void OnChatFromClient(Object sender, OSChatMessage e)
@ -132,45 +130,76 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
return; return;
} }
string message = e.Message; // string message = e.Message;
if (e.Channel == DEBUG_CHANNEL) e.Type = ChatTypeEnum.DebugChannel; // if (e.Channel == DEBUG_CHANNEL) e.Type = ChatTypeEnum.DebugChannel;
ScenePresence avatar = scene.GetScenePresence(e.Sender.AgentId); // ScenePresence avatar = scene.GetScenePresence(e.Sender.AgentId);
Vector3 fromPos = avatar.AbsolutePosition; // Vector3 fromPos = avatar.AbsolutePosition;
Vector3 regionPos = new Vector3(scene.RegionInfo.RegionLocX * Constants.RegionSize, // Vector3 regionPos = new Vector3(scene.RegionInfo.RegionLocX * Constants.RegionSize,
scene.RegionInfo.RegionLocY * Constants.RegionSize, 0); // scene.RegionInfo.RegionLocY * Constants.RegionSize, 0);
string fromName = avatar.Firstname + " " + avatar.Lastname; // string fromName = avatar.Firstname + " " + avatar.Lastname;
UUID fromID = e.Sender.AgentId; // 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 // early return if not on public or debug channel
if (e.Channel != 0 && e.Channel != DEBUG_CHANNEL) return; if (e.Channel != 0 && e.Channel != DEBUG_CHANNEL) return;
// Filled in since it's easier than rewriting right now. // // Filled in since it's easier than rewriting right now.
Vector3 fromPos = e.Position; // 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, Vector3 regionPos = new Vector3(scene.RegionInfo.RegionLocX * Constants.RegionSize,
scene.RegionInfo.RegionLocY * Constants.RegionSize, 0); scene.RegionInfo.RegionLocY * Constants.RegionSize, 0);
string fromName = e.From; if (c.Channel == DEBUG_CHANNEL) c.Type = ChatTypeEnum.DebugChannel;
string message = e.Message;
UUID fromID = e.SenderUUID;
if (e.Channel == DEBUG_CHANNEL) switch (sourceType)
e.Type = ChatTypeEnum.DebugChannel; {
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, case ChatSourceType.Object:
ChatTypeEnum chatType, ChatSourceType sourceType, string message) fromID = c.SenderUUID;
{
// iterate over message break;
}
// TODO: iterate over message
if (message.Length >= 1000) // libomv limit if (message.Length >= 1000) // libomv limit
message = message.Substring(0, 1000); message = message.Substring(0, 1000);
@ -178,20 +207,42 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
{ {
s.ForEachScenePresence(delegate(ScenePresence presence) s.ForEachScenePresence(delegate(ScenePresence presence)
{ {
TrySendChatMessage(presence, pos, regionPos, uuid, name, TrySendChatMessage(presence, fromPos, regionPos, fromID, fromName,
chatType, message, sourceType); 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 // unless the chat to be broadcast is of type Region, we
if (c.Channel != 0 && c.Channel != DEBUG_CHANNEL) return; // 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) if (c.Channel == DEBUG_CHANNEL)
c.Type = ChatTypeEnum.DebugChannel; cType = ChatTypeEnum.DebugChannel;
if (cType == ChatTypeEnum.Region)
cType = ChatTypeEnum.Say;
if (c.Message.Length > 1100) if (c.Message.Length > 1100)
c.Message = c.Message.Substring(0, 1000); 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 // broadcast chat works by redistributing every incoming chat
// message to each avatar in the scene. // message to each avatar in the scene.
Vector3 pos = new Vector3(128, 128, 30); 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( ((Scene)c.Scene).ForEachScenePresence(
delegate(ScenePresence presence) delegate(ScenePresence presence)
{ {
@ -214,29 +274,15 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
(((SceneObjectPart)c.SenderObject).OwnerID != client.AgentId)) (((SceneObjectPart)c.SenderObject).OwnerID != client.AgentId))
return; return;
if (null == c.SenderObject) client.SendChatMessage(c.Message, (byte)cType, pos, c.From, fromID,
{ (byte)sourceType, (byte)ChatAudibleLevel.Fully);
// 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);
}
}); });
} }
private void TrySendChatMessage(ScenePresence presence, Vector3 fromPos, Vector3 regionPos, protected virtual void TrySendChatMessage(ScenePresence presence, Vector3 fromPos, Vector3 regionPos,
UUID fromAgentID, string fromName, ChatTypeEnum type, UUID fromAgentID, string fromName, ChatTypeEnum type,
string message, ChatSourceType src) string message, ChatSourceType src)
{ {
// don't send stuff to child agents // don't send stuff to child agents
if (presence.IsChildAgent) return; if (presence.IsChildAgent) return;

View File

@ -48,10 +48,8 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
private const int DEBUG_CHANNEL = 2147483647; private const int DEBUG_CHANNEL = 2147483647;
private string m_defaultzone = null;
private IRCChatModule m_irc = null; private IRCConnector m_irc = null;
private Thread m_irc_connector = null;
private string m_last_leaving_user = null; private string m_last_leaving_user = null;
private string m_last_new_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(); internal object m_syncLogout = new object();
private IConfig m_config; 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 #region IRegionModule Members
@ -86,6 +90,15 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
return; 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) lock (m_syncInit)
{ {
@ -110,43 +123,21 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
// setup IRC Relay // setup IRC Relay
if (m_irc == null) if (m_irc == null)
{ {
m_irc = new IRCChatModule(config); m_irc = new IRCConnector(config);
} }
m_irc.AddScene(scene);
if (m_irc_connector == null)
{ m_log.InfoFormat("[IRC] initialized for {0}, nick: {1}, commands {2}, private channels {3}",
m_irc_connector = new Thread(IRCConnectRun); scene.RegionInfo.RegionName, m_defaultzone,
m_irc_connector.Name = "IRCConnectorThread"; m_commandsEnabled ? "enabled" : "not enabled",
m_irc_connector.IsBackground = true; m_relayPrivateChannels ? "relayed" : "not relayed");
}
m_log.InfoFormat("[IRC] initialized for {0}, nick: {1} ", scene.RegionInfo.RegionName,
m_defaultzone);
} }
} }
public void PostInitialise() public void PostInitialise()
{ {
if (null == m_irc || !m_irc.Enabled) return; if (null == m_irc || !m_irc.Enabled) return;
m_irc.Start();
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)
{
}
} }
public void Close() public void Close()
@ -172,14 +163,21 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
#region ISimChat Members #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 // early return if this comes from the IRC forwarder
if (e.Channel == m_irc.m_commandChannel) 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(); string command = messages[0].ToLower();
try try
@ -187,146 +185,135 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
switch (command) switch (command)
{ {
case "channel": case "channel":
m_irc.m_channel = messages[1]; m_irc.IrcChannel = messages[1];
break; break;
case "close": case "close":
m_irc.Close(); m_irc.Close();
break; break;
case "connect": case "connect":
m_irc.Connect(m_scenes); m_irc.Connect();
break; break;
case "nick": case "nick":
m_irc.m_nick = messages[1]; m_irc.Nick = messages[1];
break; break;
case "port": case "port":
m_irc.m_port = Convert.ToUInt32(messages[1]); m_irc.Port = Convert.ToUInt32(messages[1]);
break; break;
case "reconnect": case "reconnect":
m_irc.Reconnect(); m_irc.Reconnect();
break; break;
case "server": case "server":
m_irc.m_server = messages[1]; m_irc.Server = messages[1];
break; break;
case "verbosity": case "client-reporting":
m_irc.m_verbosity = Convert.ToInt32(messages[1]); m_irc.ClientReporting = Convert.ToBoolean(messages[1]);
break; break;
case "in-channel": case "in-channel":
m_irc.m_messageOutChannel = Convert.ToInt32(messages[1]); m_irc.RelayChannel = Convert.ToInt32(messages[1]);
break; break;
case "out-channel": case "out-channel":
m_irc.m_messageInChannel = Convert.ToInt32(messages[1]); m_relayChannelOut = Convert.ToInt32(messages[1]);
break; break;
default: default:
m_irc.Send(e.Message); m_irc.Send(c.Message);
break; 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; // drop all messages coming in on a private channel,
if (e.Message.Length == 0) return; // except if we are relaying private channels, in which
// case we drop if the private channel is not the
// not interested in our own babblings // configured m_relayChannelOut
if (m_irc.Equals(sender)) return; 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; ScenePresence avatar = null;
Scene scene = (Scene)e.Scene; Scene scene = (Scene)c.Scene;
if (scene == null) if (scene == null)
scene = m_scenes[0]; scene = m_scenes[0];
// Filled in since it's easier than rewriting right now. string fromName = c.From;
string fromName = e.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 (null != avatar)
if (m_irc.Enabled && !m_irc.Connected)
{ {
// In a non-blocking way. Eventually the connector will get it started string msg = c.Message;
try 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_log.DebugFormat("[IRC] message from object {0}, {1}", tokens[0], tokens[1]);
m_irc_connector.Name = "IRCConnectorThread"; m_irc.PrivMsg(tokens[1], scene.RegionInfo.RegionName, tokens[2]);
m_irc_connector.IsBackground = true;
} }
else
if (!m_irc_connector.IsAlive)
{ {
m_irc_connector.Start(); m_log.WarnFormat("[IRC] prim security key mismatch <{0}> not <{1}>", tokens[0], m_irc.m_accessPassword);
ThreadTracker.Add(m_irc_connector);
} }
} }
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 #endregion
public void OnNewClient(IClientAPI client) public void OnNewClient(IClientAPI client)
{ {
try try
{ {
// client.OnChatFromViewer += OnSimChat;
client.OnLogout += OnClientLoggedOut; client.OnLogout += OnClientLoggedOut;
client.OnConnectionClosed += OnClientLoggedOut; client.OnConnectionClosed += OnClientLoggedOut;
if (client.Name != m_last_new_user) 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_log.DebugFormat("[IRC] {0} logging on", client.Name);
m_irc.PrivMsg(m_irc.Nick, "Sim", m_irc.PrivMsg(m_irc.Nick, "Sim", String.Format("notices {0} logging on", client.Name));
String.Format("notices {0} logging on", client.Name));
} }
m_last_new_user = client.Name; m_last_new_user = client.Name;
} }
} }
catch (Exception ex) 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 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 regionName = presence.Scene.RegionInfo.RegionName;
string clientName = String.Format("{0} {1}", presence.Firstname, presence.Lastname); string clientName = String.Format("{0} {1}", presence.Firstname, presence.Lastname);
@ -351,7 +338,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
{ {
try 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 regionName = presence.Scene.RegionInfo.RegionName;
string clientName = String.Format("{0} {1}", presence.Firstname, presence.Lastname); string clientName = String.Format("{0} {1}", presence.Firstname, presence.Lastname);
@ -371,16 +358,14 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
{ {
try 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. // handles simple case. May not work for
// and the NewClients calles getting interleved // hundred connecting in per second. and
// but filters out multiple reports // OnNewClients calle getting interleaved but
// filters out multiple reports
if (client.Name != m_last_leaving_user) 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_last_leaving_user = client.Name;
m_irc.PrivMsg(m_irc.Nick, "Sim", String.Format("notices {0} logging out", 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); 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 // "<nick> :Nickname is already in use"
}
#endregion
#region Replies enum
public enum Replies
{
MotdStart = 375, // ":- <server> Message of the day - "
Motd = 372, // ":- <text>"
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<Scene> 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<Scene> 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 <irc_user> <visible=8,invisible=0> * : <IRC_realname>
// channel = #opensim-regions
// port = 6667
// ;MSGformat fields : 0=botnick, 1=user, 2=region, 3=message
// ;for <bot>:<user in region> :<message>
// ;msgformat = "PRIVMSG {0} :<{1} in {2}>: {3}"
// ;for <bot>:<message> - <user of region> :
// ;msgformat = "PRIVMSG {0} : {3} - {1} of {2}"
// ;for <bot>:<message> - from <user> :
// ;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<Scene> 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<string, string> 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<string, string> result = null;
//string regex = @":(?<nick>\w*)!~(?<user>\S*) PRIVMSG (?<channel>\S+) :(?<msg>.*)";
string regex = @":(?<nick>[\w-]*)!(?<user>\S*) PRIVMSG (?<channel>\S+) :(?<msg>.*)";
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<string, string>();
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<string, string> 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<IWorldComm>();
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<IWorldComm>();
wComm.DeliverMessage(ChatTypeEnum.Region, m_messageInChannel, sender, UUID.Zero, c.Message);
//IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
//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();
}
} }
} }

View File

@ -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 // "<nick> :Nickname is already in use"
}
#endregion
#region Replies enum
public enum Replies
{
MotdStart = 375, // ":- <server> Message of the day - "
Motd = 372, // ":- <text>"
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<Scene> m_scenes = new List<Scene>();
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 <irc_user> <visible=8,invisible=0> * : <IRC_realname>
// channel = #opensim-regions
// port = 6667
// ;MSGformat fields : 0=botnick, 1=user, 2=region, 3=message
// ;for <bot>:<user in region> :<message>
// ;msgformat = "PRIVMSG {0} :<{1} in {2}>: {3}"
// ;for <bot>:<message> - <user of region> :
// ;msgformat = "PRIVMSG {0} : {3} - {1} of {2}"
// ;for <bot>:<message> - from <user> :
// ;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<string, string> 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<string, string> result = null;
string regex = @":(?<nick>[\w-]*)!(?<user>\S*) PRIVMSG (?<channel>\S+) :(?<msg>.*)";
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<string, string>();
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<string, string> 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<IWorldComm>();
// wComm.DeliverMessage(ChatTypeEnum.Region, m_messageInChannel, sender, UUID.Zero, c.Message);
// //IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
// //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);
}
}
}
}

View File

@ -38,24 +38,28 @@ using OpenMetaverse;
using OpenSim.Framework; using OpenSim.Framework;
using OpenSim.Region.Environment.Interfaces; using OpenSim.Region.Environment.Interfaces;
using OpenSim.Region.Environment.Scenes; using OpenSim.Region.Environment.Scenes;
using OpenSim.Region.Environment.Modules.Avatar.Chat;
namespace OpenSim.Region.Environment.Modules.Avatar.Concierge 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 static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private const int DEBUG_CHANNEL = 2147483647; private const int DEBUG_CHANNEL = 2147483647;
private int _conciergeChannel = 42; private int _conciergeChannel = 42;
private List<Scene> _scenes = new List<Scene>(); private List<IScene> _scenes = new List<IScene>();
private List<IScene> _conciergedScenes = new List<IScene>();
private IConfig _config; private IConfig _config;
private string _whoami = "conferencier"; private string _whoami = "conferencier";
private bool _replacingChatModule = false;
private Regex _regions = null;
internal object _syncy = new object(); internal object _syncy = new object();
#region IRegionModule Members #region IRegionModule Members
public void Initialise(Scene scene, IConfigSource config) public override void Initialise(Scene scene, IConfigSource config)
{ {
try try
{ {
@ -78,25 +82,58 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Concierge
return; 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); if (config.Configs["Chat"] == null)
_whoami = _config.GetString("whoami", "conferencier"); {
_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); _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) lock (_syncy)
{ {
if (!_scenes.Contains(scene)) if (!_scenes.Contains(scene))
{ {
_scenes.Add(scene); _scenes.Add(scene);
if (_regions.IsMatch(scene.RegionInfo.RegionName))
_conciergedScenes.Add(scene);
// subscribe to NewClient events // subscribe to NewClient events
scene.EventManager.OnNewClient += OnNewClient; scene.EventManager.OnNewClient += OnNewClient;
scene.EventManager.OnNewClient += OnNewClient;
// subscribe to *Chat events and FilterChat* events // subscribe to *Chat events
scene.EventManager.OnChatFromWorld += OnSimChat; scene.EventManager.OnChatFromWorld += OnChatFromWorld;
scene.EventManager.OnChatBroadcast += OnSimBroadcast; if (!_replacingChatModule)
scene.EventManager.OnChatFromClient += OnChatFromClient;
scene.EventManager.OnChatBroadcast += OnChatBroadcast;
// subscribe to agent change events // subscribe to agent change events
scene.EventManager.OnMakeRootAgent += OnMakeRootAgent; scene.EventManager.OnMakeRootAgent += OnMakeRootAgent;
@ -106,20 +143,20 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Concierge
_log.InfoFormat("[Concierge] initialized for {0}", scene.RegionInfo.RegionName); _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"; } get { return "ConciergeModule"; }
} }
public bool IsSharedModule public override bool IsSharedModule
{ {
get { return true; } get { return true; }
} }
@ -127,44 +164,90 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Concierge
#endregion #endregion
#region ISimChat Members #region ISimChat Members
public void OnSimBroadcast(Object sender, OSChatMessage c) public override void OnChatBroadcast(Object sender, OSChatMessage c)
{ {
// log to buffer? if (_replacingChatModule)
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 (_amplify) // distribute chat message to each and every avatar in
// { // the region
base.OnChatBroadcast(sender, c);
// }
// log as avatar/prim chat
return;
} }
// TODO: capture logic
return; 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 #endregion
public void OnNewClient(IClientAPI client) public override void OnNewClient(IClientAPI client)
{ {
client.OnLogout += OnClientLoggedOut; client.OnLogout += OnClientLoggedOut;
client.OnConnectionClosed += OnClientLoggedOut; client.OnConnectionClosed += OnClientLoggedOut;
if (_replacingChatModule)
client.OnChatFromClient += OnChatFromClient;
_log.DebugFormat("[Concierge] {0} logs on to {1}", client.Name, client.Scene.RegionInfo.RegionName); if (_conciergedScenes.Contains(client.Scene))
AnnounceToAgentsRegion(client, String.Format("{0} logs on to {1}", client.Name, client.Scene.RegionInfo.RegionName)); {
_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) public void OnClientLoggedOut(IClientAPI client)
@ -172,32 +255,46 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Concierge
client.OnLogout -= OnClientLoggedOut; client.OnLogout -= OnClientLoggedOut;
client.OnConnectionClosed -= OnClientLoggedOut; client.OnConnectionClosed -= OnClientLoggedOut;
_log.DebugFormat("[Concierge] {0} logs off from {1}", client.Name, client.Scene.RegionInfo.RegionName); if (_conciergedScenes.Contains(client.Scene))
AnnounceToAgentsRegion(client, String.Format("{0} logs off from {1}", client.Name, client.Scene.RegionInfo.RegionName)); {
_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) public void OnMakeRootAgent(ScenePresence agent)
{ {
_log.DebugFormat("[Concierge] {0} enters {1}", agent.Name, agent.Scene.RegionInfo.RegionName); if (_conciergedScenes.Contains(agent.Scene))
AnnounceToAgentsRegion(agent, String.Format("{0} enters {1}", agent.Name, agent.Scene.RegionInfo.RegionName)); {
_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) public void OnMakeChildAgent(ScenePresence agent)
{ {
_log.DebugFormat("[Concierge] {0} leaves {1}", agent.Name, agent.Scene.RegionInfo.RegionName); if (_conciergedScenes.Contains(agent.Scene))
AnnounceToAgentsRegion(agent, String.Format("{0} leaves {1}", agent.Name, agent.Scene.RegionInfo.RegionName)); {
_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) public void ClientLoggedOut(IClientAPI client)
{ {
_log.DebugFormat("[Concierge] {0} logs out of {1}", client.Name, client.Scene.RegionInfo.RegionName); if (_conciergedScenes.Contains(client.Scene))
AnnounceToAgentsRegion(client, String.Format("{0} logs out of {1}", client.Name, client.Scene.RegionInfo.RegionName)); {
_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); static private Vector3 posOfGod = new Vector3(128, 128, 9999);
protected void AnnounceToAgentsRegion(IClientAPI client, string msg) protected void AnnounceToAgentsRegion(IClientAPI client, string msg)

View File

@ -34,6 +34,10 @@ using OpenSim.Framework;
using OpenSim.Region.Environment.Interfaces; using OpenSim.Region.Environment.Interfaces;
using OpenSim.Region.Environment.Scenes; using OpenSim.Region.Environment.Scenes;
// using log4net;
// using System.Reflection;
/***************************************************** /*****************************************************
* *
* WorldCommModule * WorldCommModule
@ -83,6 +87,9 @@ namespace OpenSim.Region.Environment.Modules.Scripting.WorldComm
{ {
public class WorldCommModule : IRegionModule, IWorldComm public class WorldCommModule : IRegionModule, IWorldComm
{ {
// private static readonly ILog m_log =
// LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private ListenerManager m_listenerManager; private ListenerManager m_listenerManager;
private Queue m_pending; private Queue m_pending;
private Queue m_pendingQ; private Queue m_pendingQ;
@ -117,6 +124,7 @@ namespace OpenSim.Region.Environment.Modules.Scripting.WorldComm
m_scene.RegisterModuleInterface<IWorldComm>(this); m_scene.RegisterModuleInterface<IWorldComm>(this);
m_listenerManager = new ListenerManager(maxlisteners, maxhandles); m_listenerManager = new ListenerManager(maxlisteners, maxhandles);
m_scene.EventManager.OnChatFromClient += DeliverClientMessage; m_scene.EventManager.OnChatFromClient += DeliverClientMessage;
m_scene.EventManager.OnChatBroadcast += DeliverClientMessage;
m_pendingQ = new Queue(); m_pendingQ = new Queue();
m_pending = Queue.Synchronized(m_pendingQ); m_pending = Queue.Synchronized(m_pendingQ);
} }
@ -197,6 +205,22 @@ namespace OpenSim.Region.Environment.Modules.Scripting.WorldComm
m_listenerManager.DeleteListener(itemID); 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);
}
/// <summary> /// <summary>
/// This method scans over the objects which registered an interest in listen callbacks. /// 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 /// 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
/// <param name="name">name of sender (object or avatar)</param> /// <param name="name">name of sender (object or avatar)</param>
/// <param name="id">key of sender (object or avatar)</param> /// <param name="id">key of sender (object or avatar)</param>
/// <param name="msg">msg to sent</param> /// <param name="msg">msg to sent</param>
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 // 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 // in a limited set of listeners, each belonging a host. If the host is in range, add them
// to the pending queue. // to the pending queue.
@ -326,11 +319,6 @@ namespace OpenSim.Region.Environment.Modules.Scripting.WorldComm
#endregion #endregion
// private void NewClient(IClientAPI client)
// {
// client.OnChatFromViewer += DeliverClientMessage;
// }
/******************************************************************** /********************************************************************
* *
* Listener Stuff * Listener Stuff
@ -339,11 +327,10 @@ namespace OpenSim.Region.Environment.Modules.Scripting.WorldComm
private void DeliverClientMessage(Object sender, OSChatMessage e) private void DeliverClientMessage(Object sender, OSChatMessage e)
{ {
DeliverMessage(e.Type, if (null != e.Sender)
e.Channel, DeliverMessage(e.Type, e.Channel, e.Sender.Name, e.Sender.AgentId, e.Message, e.Position);
e.Sender.Name, else
e.Sender.AgentId, DeliverMessage(e.Type, e.Channel, e.From, UUID.Zero, e.Message, e.Position);
e.Message);
} }
public Object[] GetSerializationData(UUID itemID) public Object[] GetSerializationData(UUID itemID)

View File

@ -7238,7 +7238,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
{ {
m_host.AddScriptLPS(1); 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<IWorldComm>(); // IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
// wComm.DeliverMessage(ChatTypeEnum.Owner, 0, m_host.Name, m_host.UUID, msg); // wComm.DeliverMessage(ChatTypeEnum.Owner, 0, m_host.Name, m_host.UUID, msg);
} }

View File

@ -398,20 +398,21 @@ flush-on-error=true
;nick = OpenSimBotNameProbablyMakeThisShorter ;nick = OpenSimBotNameProbablyMakeThisShorter
;channel = #the_irc_channel_you_want_to_connect_to ;channel = #the_irc_channel_you_want_to_connect_to
;port = 6667 ;port = 6667
;; channel to listen for configuration updates ;; channel to listen for configuration commands
;commandchannel = 2777 ;commands_enabled = false
;; 0 for no control messages, 2 for all control messages ;command_channel = 2777
;verbosity = 1 ;report_clients = true
;; bridging connections ;; relay private chat connections
;; outchannel -- channel to send messages out to the IRC bridge ;; relay_private_channels = true: will relay IRC chat from/to private in-world channels
;; inchannel -- channel to receive message from the IRC bridge ;; 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 ;; access_password -- simple security device
;inchannel = 2226 ;relay_private_channels = false
;outchannel = 2225 ;relay_channel_in = 2226
;relay_channel_out = 2225
;access_password = foobar ;access_password = foobar
;fallback_region = name of "default" region ;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 ;MSGformat fields : 0=botnick, 1=user, 2=region, 3=message
; must start with "PRIVMSG {0} : " or irc server will get upset ; must start with "PRIVMSG {0} : " or irc server will get upset
;for <bot>:<user in region> :<message> ;for <bot>:<user in region> :<message>