aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs
diff options
context:
space:
mode:
authorDr Scofield2008-10-20 17:31:54 +0000
committerDr Scofield2008-10-20 17:31:54 +0000
commit72a388a7b6dfba8f93ffc5c5c45db4c44bb46480 (patch)
treedb9ab06f6820806d8c2b3fc32029383971e955b7 /OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs
parentMantis #2438 (diff)
downloadopensim-SC_OLD-72a388a7b6dfba8f93ffc5c5c45db4c44bb46480.zip
opensim-SC_OLD-72a388a7b6dfba8f93ffc5c5c45db4c44bb46480.tar.gz
opensim-SC_OLD-72a388a7b6dfba8f93ffc5c5c45db4c44bb46480.tar.bz2
opensim-SC_OLD-72a388a7b6dfba8f93ffc5c5c45db4c44bb46480.tar.xz
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.
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs841
1 files changed, 104 insertions, 737 deletions
diff --git a/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs b/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs
index e992862..05718d8 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
48 48
49 private const int DEBUG_CHANNEL = 2147483647; 49 private const int DEBUG_CHANNEL = 2147483647;
50 50
51 private string m_defaultzone = null;
52 51
53 private IRCChatModule m_irc = null; 52 private IRCConnector m_irc = null;
54 private Thread m_irc_connector = null;
55 53
56 private string m_last_leaving_user = null; 54 private string m_last_leaving_user = null;
57 private string m_last_new_user = null; 55 private string m_last_new_user = null;
@@ -61,6 +59,12 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
61 internal object m_syncLogout = new object(); 59 internal object m_syncLogout = new object();
62 60
63 private IConfig m_config; 61 private IConfig m_config;
62 private string m_defaultzone = null;
63 private bool m_commandsEnabled = false;
64 private int m_commandChannel = -1;
65 private bool m_relayPrivateChannels = false;
66 private int m_relayChannelOut = -1;
67 private bool m_clientReporting = true;
64 68
65 #region IRegionModule Members 69 #region IRegionModule Members
66 70
@@ -86,6 +90,15 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
86 return; 90 return;
87 } 91 }
88 92
93 m_commandsEnabled = m_config.GetBoolean("commands_enabled", m_commandsEnabled);
94 m_commandChannel = m_config.GetInt("commandchannel", m_commandChannel); // compat
95 m_commandChannel = m_config.GetInt("command_channel", m_commandChannel);
96
97 m_relayPrivateChannels = m_config.GetBoolean("relay_private_channels", m_relayPrivateChannels);
98 m_relayChannelOut = m_config.GetInt("relay_private_channel_out", m_relayChannelOut);
99
100 m_clientReporting = m_config.GetBoolean("report_clients", m_clientReporting);
101
89 lock (m_syncInit) 102 lock (m_syncInit)
90 { 103 {
91 104
@@ -110,43 +123,21 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
110 // setup IRC Relay 123 // setup IRC Relay
111 if (m_irc == null) 124 if (m_irc == null)
112 { 125 {
113 m_irc = new IRCChatModule(config); 126 m_irc = new IRCConnector(config);
114 } 127 }
115 128 m_irc.AddScene(scene);
116 if (m_irc_connector == null) 129
117 { 130 m_log.InfoFormat("[IRC] initialized for {0}, nick: {1}, commands {2}, private channels {3}",
118 m_irc_connector = new Thread(IRCConnectRun); 131 scene.RegionInfo.RegionName, m_defaultzone,
119 m_irc_connector.Name = "IRCConnectorThread"; 132 m_commandsEnabled ? "enabled" : "not enabled",
120 m_irc_connector.IsBackground = true; 133 m_relayPrivateChannels ? "relayed" : "not relayed");
121 }
122 m_log.InfoFormat("[IRC] initialized for {0}, nick: {1} ", scene.RegionInfo.RegionName,
123 m_defaultzone);
124 } 134 }
125 } 135 }
126 136
127 public void PostInitialise() 137 public void PostInitialise()
128 { 138 {
129 if (null == m_irc || !m_irc.Enabled) return; 139 if (null == m_irc || !m_irc.Enabled) return;
130 140 m_irc.Start();
131 try
132 {
133 //m_irc.Connect(m_scenes);
134 if (m_irc_connector == null)
135 {
136 m_irc_connector = new Thread(IRCConnectRun);
137 m_irc_connector.Name = "IRCConnectorThread";
138 m_irc_connector.IsBackground = true;
139 }
140
141 if (!m_irc_connector.IsAlive)
142 {
143 m_irc_connector.Start();
144 ThreadTracker.Add(m_irc_connector);
145 }
146 }
147 catch (Exception)
148 {
149 }
150 } 141 }
151 142
152 public void Close() 143 public void Close()
@@ -172,14 +163,21 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
172 163
173 #region ISimChat Members 164 #region ISimChat Members
174 165
175 public void OnSimChat(Object sender, OSChatMessage e) 166 public void OnSimChat(Object sender, OSChatMessage c)
176 { 167 {
177 m_log.DebugFormat("[IRC] heard on channel {0}: {1}", e.Channel.ToString(), e.Message); 168 // early return if nothing to forward
169 if (c.Message.Length == 0) return;
170
171 // early return if this comes from the IRC forwarder
172 if (m_irc.Equals(sender)) return;
173
174 m_log.DebugFormat("[IRC] heard on channel {0}: {1}", c.Channel, c.Message);
178 175
179 // We only want to relay stuff on channel 0 176 // check for commands coming from avatars or in-world
180 if (e.Channel == m_irc.m_commandChannel) 177 // object (if commands are enabled)
178 if (m_commandsEnabled && c.Channel == m_commandChannel)
181 { 179 {
182 string[] messages = e.Message.Split(' '); 180 string[] messages = c.Message.Split(' ');
183 string command = messages[0].ToLower(); 181 string command = messages[0].ToLower();
184 182
185 try 183 try
@@ -187,146 +185,135 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
187 switch (command) 185 switch (command)
188 { 186 {
189 case "channel": 187 case "channel":
190 m_irc.m_channel = messages[1]; 188 m_irc.IrcChannel = messages[1];
191 break; 189 break;
192 case "close": 190 case "close":
193 m_irc.Close(); 191 m_irc.Close();
194 break; 192 break;
195 case "connect": 193 case "connect":
196 m_irc.Connect(m_scenes); 194 m_irc.Connect();
197 break; 195 break;
198 case "nick": 196 case "nick":
199 m_irc.m_nick = messages[1]; 197 m_irc.Nick = messages[1];
200 break; 198 break;
201 case "port": 199 case "port":
202 m_irc.m_port = Convert.ToUInt32(messages[1]); 200 m_irc.Port = Convert.ToUInt32(messages[1]);
203 break; 201 break;
204 case "reconnect": 202 case "reconnect":
205 m_irc.Reconnect(); 203 m_irc.Reconnect();
206 break; 204 break;
207 case "server": 205 case "server":
208 m_irc.m_server = messages[1]; 206 m_irc.Server = messages[1];
209 break; 207 break;
210 case "verbosity": 208 case "client-reporting":
211 m_irc.m_verbosity = Convert.ToInt32(messages[1]); 209 m_irc.ClientReporting = Convert.ToBoolean(messages[1]);
210
212 break; 211 break;
213 case "in-channel": 212 case "in-channel":
214 m_irc.m_messageOutChannel = Convert.ToInt32(messages[1]); 213 m_irc.RelayChannel = Convert.ToInt32(messages[1]);
215 break; 214 break;
216 case "out-channel": 215 case "out-channel":
217 m_irc.m_messageInChannel = Convert.ToInt32(messages[1]); 216 m_relayChannelOut = Convert.ToInt32(messages[1]);
218 break; 217 break;
219 218
220 default: 219 default:
221 m_irc.Send(e.Message); 220 m_irc.Send(c.Message);
222 break; 221 break;
223 } 222 }
224 } 223 }
225 catch 224 catch(Exception ex)
226 { } 225 {
226 m_log.DebugFormat("[IRC] error processing in-world command channel input: {0}", ex);
227 }
227 } 228 }
228 229
229 if (e.Channel != m_irc.m_messageOutChannel) return; 230 // drop all messages coming in on a private channel,
230 if (e.Message.Length == 0) return; 231 // except if we are relaying private channels, in which
231 232 // case we drop if the private channel is not the
232 // not interested in our own babblings 233 // configured m_relayChannelOut
233 if (m_irc.Equals(sender)) return; 234 if (m_relayPrivateChannels)
235 {
236 if (c.Channel != 0 && c.Channel != DEBUG_CHANNEL && c.Channel != m_relayChannelOut)
237 {
238 m_log.DebugFormat("[IRC] dropping message {0} on channel {1}", c, c.Channel);
239 return;
240 }
241 }
242 else if (c.Channel != 0 && c.Channel != DEBUG_CHANNEL)
243 {
244 m_log.DebugFormat("[IRC] dropping message {0} on channel {1}", c, c.Channel);
245 return;
246 }
234 247
235 ScenePresence avatar = null; 248 ScenePresence avatar = null;
236 Scene scene = (Scene)e.Scene; 249 Scene scene = (Scene)c.Scene;
237 250
238 if (scene == null) 251 if (scene == null)
239 scene = m_scenes[0]; 252 scene = m_scenes[0];
240 253
241 // Filled in since it's easier than rewriting right now. 254 string fromName = c.From;
242 string fromName = e.From;
243 255
244 if (e.Sender != null) 256 if (c.Sender != null)
245 { 257 {
246 avatar = scene.GetScenePresence(e.Sender.AgentId); 258 avatar = scene.GetScenePresence(c.Sender.AgentId);
259 if (avatar != null) fromName = avatar.Name;
247 } 260 }
248 261
249 if (avatar != null) 262 if (!m_irc.Connected)
250 { 263 {
251 fromName = avatar.Name; 264 m_log.WarnFormat("[IRC] IRCConnector not connected: dropping message from {0}", fromName);
265 return;
252 } 266 }
253 267
254 // Try to reconnect to server if not connected 268 if (null != avatar)
255 if (m_irc.Enabled && !m_irc.Connected)
256 { 269 {
257 // In a non-blocking way. Eventually the connector will get it started 270 string msg = c.Message;
258 try 271 if (msg.StartsWith("/me "))
272 msg = String.Format("{0} {1}", fromName, c.Message.Substring(4));
273
274 m_irc.PrivMsg(fromName, scene.RegionInfo.RegionName, msg);
275 }
276 else
277 {
278 //Message came from an object
279 char[] splits = { ',' };
280 string[] tokens = c.Message.Split(splits,3); // This is certainly wrong
281 if (tokens.Length == 3)
259 { 282 {
260 if (m_irc_connector == null) 283 if (tokens[0] == m_irc.m_accessPassword) // This is my really simple check
261 { 284 {
262 m_irc_connector = new Thread(IRCConnectRun); 285 m_log.DebugFormat("[IRC] message from object {0}, {1}", tokens[0], tokens[1]);
263 m_irc_connector.Name = "IRCConnectorThread"; 286 m_irc.PrivMsg(tokens[1], scene.RegionInfo.RegionName, tokens[2]);
264 m_irc_connector.IsBackground = true;
265 } 287 }
266 288 else
267 if (!m_irc_connector.IsAlive)
268 { 289 {
269 m_irc_connector.Start(); 290 m_log.WarnFormat("[IRC] prim security key mismatch <{0}> not <{1}>", tokens[0], m_irc.m_accessPassword);
270 ThreadTracker.Add(m_irc_connector);
271 } 291 }
272 } 292 }
273 catch (Exception)
274 {
275 }
276 } 293 }
277
278 if (e.Message.StartsWith("/me ") && (null != avatar))
279 e.Message = String.Format("{0} {1}", fromName, e.Message.Substring(4));
280
281 // this is to keep objects from talking to IRC
282 if (m_irc.Connected && (avatar != null))
283 m_irc.PrivMsg(fromName, scene.RegionInfo.RegionName, e.Message);
284
285 // Handle messages from objects
286 if (m_irc.Connected && (null == avatar))
287 {
288 //Message came from an object
289 char[] splits = { ',' };
290 string[] tokens = e.Message.Split(splits,3); // This is certainly wrong
291 if (tokens.Length == 3)
292 {
293 if (tokens[0] == m_irc.m_accessPassword) // This is my really simple check
294 {
295 m_log.DebugFormat("[IRC] message from object {0}, {1}", tokens[0], tokens[1]);
296 m_irc.PrivMsg(tokens[1], scene.RegionInfo.RegionName, tokens[2]);
297 }
298 else
299 {
300 m_log.WarnFormat("[IRC] prim security key mismatch <{0}> not <{1}>", tokens[0], m_irc.m_accessPassword);
301 }
302 }
303 }
304 } 294 }
305
306 #endregion 295 #endregion
307 296
308 public void OnNewClient(IClientAPI client) 297 public void OnNewClient(IClientAPI client)
309 { 298 {
310 try 299 try
311 { 300 {
312 // client.OnChatFromViewer += OnSimChat;
313 client.OnLogout += OnClientLoggedOut; 301 client.OnLogout += OnClientLoggedOut;
314 client.OnConnectionClosed += OnClientLoggedOut; 302 client.OnConnectionClosed += OnClientLoggedOut;
315 303
316 if (client.Name != m_last_new_user) 304 if (client.Name != m_last_new_user)
317 { 305 {
318 if ((m_irc.Enabled) && (m_irc.Connected) && (m_irc.m_verbosity >= 1)) 306 if ((m_irc.Enabled) && (m_irc.Connected) && (m_clientReporting))
319 { 307 {
320 m_log.DebugFormat("[IRC] {0} logging on", client.Name); 308 m_log.DebugFormat("[IRC] {0} logging on", client.Name);
321 m_irc.PrivMsg(m_irc.Nick, "Sim", 309 m_irc.PrivMsg(m_irc.Nick, "Sim", String.Format("notices {0} logging on", client.Name));
322 String.Format("notices {0} logging on", client.Name));
323 } 310 }
324 m_last_new_user = client.Name; 311 m_last_new_user = client.Name;
325 } 312 }
326 } 313 }
327 catch (Exception ex) 314 catch (Exception ex)
328 { 315 {
329 m_log.Error("[IRC]: NewClient exception trap:" + ex.ToString()); 316 m_log.Error("[IRC]: OnNewClient exception trap:" + ex.ToString());
330 } 317 }
331 } 318 }
332 319
@@ -334,7 +321,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
334 { 321 {
335 try 322 try
336 { 323 {
337 if ((m_irc.Enabled) && (m_irc.Connected) && (m_irc.m_verbosity >= 2)) 324 if ((m_irc.Enabled) && (m_irc.Connected) && (m_clientReporting))
338 { 325 {
339 string regionName = presence.Scene.RegionInfo.RegionName; 326 string regionName = presence.Scene.RegionInfo.RegionName;
340 string clientName = String.Format("{0} {1}", presence.Firstname, presence.Lastname); 327 string clientName = String.Format("{0} {1}", presence.Firstname, presence.Lastname);
@@ -351,7 +338,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
351 { 338 {
352 try 339 try
353 { 340 {
354 if ((m_irc.Enabled) && (m_irc.Connected) && (m_irc.m_verbosity >= 2)) 341 if ((m_irc.Enabled) && (m_irc.Connected) && (m_clientReporting))
355 { 342 {
356 string regionName = presence.Scene.RegionInfo.RegionName; 343 string regionName = presence.Scene.RegionInfo.RegionName;
357 string clientName = String.Format("{0} {1}", presence.Firstname, presence.Lastname); 344 string clientName = String.Format("{0} {1}", presence.Firstname, presence.Lastname);
@@ -371,16 +358,14 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
371 { 358 {
372 try 359 try
373 { 360 {
374 if ((m_irc.Enabled) && (m_irc.Connected) && (m_irc.m_verbosity >= 1)) 361 if ((m_irc.Enabled) && (m_irc.Connected) && (m_clientReporting))
375 { 362 {
376 // handles simple case. May not work for hundred connecting in per second. 363 // handles simple case. May not work for
377 // and the NewClients calles getting interleved 364 // hundred connecting in per second. and
378 // but filters out multiple reports 365 // OnNewClients calle getting interleaved but
366 // filters out multiple reports
379 if (client.Name != m_last_leaving_user) 367 if (client.Name != m_last_leaving_user)
380 { 368 {
381 // Console.WriteLine("Avatar was seen logging out.");
382 // Console.ReadLine();
383 // Console.WriteLine();
384 m_last_leaving_user = client.Name; 369 m_last_leaving_user = client.Name;
385 m_irc.PrivMsg(m_irc.Nick, "Sim", String.Format("notices {0} logging out", client.Name)); 370 m_irc.PrivMsg(m_irc.Nick, "Sim", String.Format("notices {0} logging out", client.Name));
386 m_log.InfoFormat("[IRC]: {0} logging out", client.Name); 371 m_log.InfoFormat("[IRC]: {0} logging out", client.Name);
@@ -396,623 +381,5 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
396 } 381 }
397 } 382 }
398 } 383 }
399
400 // if IRC is enabled then just keep trying using a monitor thread
401 public void IRCConnectRun()
402 {
403 while (m_irc.Enabled)
404 {
405 if (!m_irc.Connected)
406 {
407 m_irc.Connect(m_scenes);
408 }
409 Thread.Sleep(15000);
410 }
411 }
412 }
413
414 internal class IRCChatModule
415 {
416 #region ErrorReplies enum
417
418 public enum ErrorReplies
419 {
420 NotRegistered = 451, // ":You have not registered"
421 NicknameInUse = 433 // "<nick> :Nickname is already in use"
422 }
423
424 #endregion
425
426 #region Replies enum
427
428 public enum Replies
429 {
430 MotdStart = 375, // ":- <server> Message of the day - "
431 Motd = 372, // ":- <text>"
432 EndOfMotd = 376 // ":End of /MOTD command"
433 }
434
435 #endregion
436
437 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
438 private Thread listener;
439
440 private string m_basenick = null;
441 public string m_channel = null;
442 private bool m_nrnick = false;
443 private bool m_connected = false;
444 private bool m_enabled = false;
445 public int m_commandChannel = -1;
446
447 public int m_verbosity = 1;
448 public int m_messageOutChannel = 0; // Will be the message channel we listen to
449 public int m_messageInChannel = 0; // Will be the message channel where we forward msgs
450 public string m_accessPassword = "badkitty";
451 public bool m_useWorldComm = false; // true if we want chat to be localized, false if we want to broadcast to the entire region
452
453 public List<Scene> m_last_scenes = null;
454 public string m_nick = null;
455 public uint m_port = 6668;
456 private string m_privmsgformat = "PRIVMSG {0} :<{1} in {2}>: {3}";
457 private StreamReader m_reader;
458 private List<Scene> m_scenes = null;
459 public string m_server = null;
460
461 private NetworkStream m_stream = null;
462 internal object m_syncConnect = new object();
463 private TcpClient m_tcp;
464 private string m_user = "USER OpenSimBot 8 * :I'm an OpenSim to IRC bot";
465 private StreamWriter m_writer;
466
467 private Thread pingSender;
468
469 public IRCChatModule(IConfigSource config)
470 {
471 m_nick = "OSimBot" + Util.RandomClass.Next(1, 99);
472 m_tcp = null;
473 m_writer = null;
474 m_reader = null;
475
476 // configuration in OpenSim.ini
477 // [IRC]
478 // server = chat.freenode.net
479 // nick = OSimBot_mysim
480 // nicknum = true
481 // ;nicknum set to true appends a 2 digit random number to the nick
482 // ;username = USER OpenSimBot 8 * :I'm a OpenSim to irc bot
483 // ; username is the IRC command line sent
484 // ; USER <irc_user> <visible=8,invisible=0> * : <IRC_realname>
485 // channel = #opensim-regions
486 // port = 6667
487 // ;MSGformat fields : 0=botnick, 1=user, 2=region, 3=message
488 // ;for <bot>:<user in region> :<message>
489 // ;msgformat = "PRIVMSG {0} :<{1} in {2}>: {3}"
490 // ;for <bot>:<message> - <user of region> :
491 // ;msgformat = "PRIVMSG {0} : {3} - {1} of {2}"
492 // ;for <bot>:<message> - from <user> :
493 // ;msgformat = "PRIVMSG {0} : {3} - from {1}"
494 // Traps I/O disconnects so it does not crash the sim
495 // Trys to reconnect if disconnected and someone says something
496 // Tells IRC server "QUIT" when doing a close (just to be nice)
497 // Default port back to 6667
498
499 try
500 {
501 m_server = config.Configs["IRC"].GetString("server");
502 m_nick = config.Configs["IRC"].GetString("nick");
503 m_basenick = m_nick;
504 m_nrnick = config.Configs["IRC"].GetBoolean("nicknum", true);
505 m_channel = config.Configs["IRC"].GetString("channel");
506 m_port = (uint)config.Configs["IRC"].GetInt("port", (int)m_port);
507 m_user = config.Configs["IRC"].GetString("username", m_user);
508 m_privmsgformat = config.Configs["IRC"].GetString("msgformat", m_privmsgformat);
509 m_commandChannel = config.Configs["IRC"].GetInt("commandchannel", m_commandChannel);
510
511 m_verbosity = config.Configs["IRC"].GetInt("verbosity", m_verbosity);
512 m_messageOutChannel = config.Configs["IRC"].GetInt("outchannel", m_messageOutChannel);
513 m_messageInChannel = config.Configs["IRC"].GetInt("inchannel", m_messageInChannel);
514 m_accessPassword = config.Configs["IRC"].GetString("access_password",m_accessPassword);
515 m_useWorldComm = config.Configs["IRC"].GetBoolean("useworldcomm", m_useWorldComm);
516
517 if (m_server != null && m_nick != null && m_channel != null)
518 {
519 if (m_nrnick == true)
520 {
521 m_nick = m_nick + Util.RandomClass.Next(1, 99);
522 }
523 m_enabled = true;
524 }
525 }
526 catch (Exception ex)
527 {
528 m_log.Error("[IRC]: Incomplete IRC configuration, skipping IRC bridge configuration");
529 m_log.DebugFormat("[IRC] Incomplete IRC configuration: {0}", ex.ToString());
530 }
531 }
532
533 public bool Enabled
534 {
535 get { return m_enabled; }
536 }
537
538 public bool Connected
539 {
540 get { return m_connected; }
541 }
542
543 public string Nick
544 {
545 get { return m_nick; }
546 }
547
548 public bool Connect(List<Scene> scenes)
549 {
550 lock (m_syncConnect)
551 {
552 try
553 {
554 if (m_connected) return true;
555
556 m_scenes = scenes;
557 if (m_last_scenes == null)
558 {
559 m_last_scenes = scenes;
560 }
561
562 m_tcp = new TcpClient(m_server, (int)m_port);
563 m_stream = m_tcp.GetStream();
564 m_reader = new StreamReader(m_stream);
565 m_writer = new StreamWriter(m_stream);
566
567 m_log.DebugFormat("[IRC]: Connected to {0}:{1}", m_server, m_port);
568
569 pingSender = new Thread(new ThreadStart(PingRun));
570 pingSender.Name = "PingSenderThread";
571 pingSender.IsBackground = true;
572 pingSender.Start();
573 ThreadTracker.Add(pingSender);
574
575 listener = new Thread(new ThreadStart(ListenerRun));
576 listener.Name = "IRCChatModuleListenerThread";
577 listener.IsBackground = true;
578 listener.Start();
579 ThreadTracker.Add(listener);
580
581 m_writer.WriteLine(m_user);
582 m_writer.Flush();
583 m_writer.WriteLine(String.Format("NICK {0}", m_nick));
584 m_writer.Flush();
585 m_writer.WriteLine(String.Format("JOIN {0}", m_channel));
586 m_writer.Flush();
587 m_log.Info("[IRC]: Connection fully established");
588 m_connected = true;
589 }
590 catch (Exception e)
591 {
592 m_log.ErrorFormat("[IRC] cannot connect to {0}:{1}: {2}",
593 m_server, m_port, e.Message);
594 }
595 m_log.Debug("[IRC] Connected");
596 return m_connected;
597 }
598 }
599
600 public void Reconnect()
601 {
602 m_connected = false;
603 try
604 {
605 listener.Abort();
606 pingSender.Abort();
607 m_writer.Close();
608 m_reader.Close();
609 m_tcp.Close();
610 }
611 catch (Exception)
612 {
613 }
614
615 if (m_enabled)
616 {
617 Connect(m_last_scenes);
618 }
619 }
620
621 public void PrivMsg(string from, string region, string msg)
622 {
623 m_log.DebugFormat("[IRC] Sending message to IRC from {0}: {1}", from, msg);
624
625 // One message to the IRC server
626 try
627 {
628 m_writer.WriteLine(m_privmsgformat, m_channel, from, region, msg);
629 m_writer.Flush();
630 m_log.InfoFormat("[IRC]: PrivMsg {0} in {1}: {2}", from, region, msg);
631 }
632 catch (IOException)
633 {
634 m_log.Error("[IRC]: Disconnected from IRC server.(PrivMsg)");
635 Reconnect();
636 }
637 catch (Exception ex)
638 {
639 m_log.ErrorFormat("[IRC]: PrivMsg exception trap: {0}", ex.ToString());
640 }
641 }
642
643 public void Send(string msg)
644 {
645 try
646 {
647 m_writer.WriteLine(msg);
648 m_writer.Flush();
649 m_log.Info("IRC: Sent command string: " + msg);
650 }
651 catch (IOException)
652 {
653 m_log.Error("[IRC]: Disconnected from IRC server.(PrivMsg)");
654 Reconnect();
655 }
656 catch (Exception ex)
657 {
658 m_log.ErrorFormat("[IRC]: PrivMsg exception trap: {0}", ex.ToString());
659 }
660
661 }
662
663
664 private Dictionary<string, string> ExtractMsg(string input)
665 {
666 //examines IRC commands and extracts any private messages
667 // which will then be reboadcast in the Sim
668
669 m_log.Info("[IRC]: ExtractMsg: " + input);
670 Dictionary<string, string> result = null;
671 //string regex = @":(?<nick>\w*)!~(?<user>\S*) PRIVMSG (?<channel>\S+) :(?<msg>.*)";
672 string regex = @":(?<nick>[\w-]*)!(?<user>\S*) PRIVMSG (?<channel>\S+) :(?<msg>.*)";
673 Regex RE = new Regex(regex, RegexOptions.Multiline);
674 MatchCollection matches = RE.Matches(input);
675
676 // Get some direct matches $1 $4 is a
677 if ((matches.Count == 0) || (matches.Count != 1) || (matches[0].Groups.Count != 5))
678 {
679 m_log.Info("[IRC]: Number of matches: " + matches.Count);
680 if (matches.Count > 0)
681 {
682 m_log.Info("[IRC]: Number of groups: " + matches[0].Groups.Count);
683 }
684 return null;
685 }
686
687 result = new Dictionary<string, string>();
688 result.Add("nick", matches[0].Groups[1].Value);
689 result.Add("user", matches[0].Groups[2].Value);
690 result.Add("channel", matches[0].Groups[3].Value);
691 result.Add("msg", matches[0].Groups[4].Value);
692
693 return result;
694 }
695
696 public void PingRun()
697 {
698 // IRC keep alive thread
699 // send PING ever 15 seconds
700 while (m_enabled)
701 {
702 try
703 {
704 if (m_connected == true)
705 {
706 m_writer.WriteLine(String.Format("PING :{0}", m_server));
707 m_writer.Flush();
708 Thread.Sleep(15000);
709 }
710 }
711 catch (IOException)
712 {
713 if (m_enabled)
714 {
715 m_log.Error("[IRC]: Disconnected from IRC server.(PingRun)");
716 Reconnect();
717 }
718 }
719 catch (Exception ex)
720 {
721 m_log.ErrorFormat("[IRC]: PingRun exception trap: {0}\n{1}", ex.ToString(), ex.StackTrace);
722 }
723 }
724 }
725
726 static private Vector3 pos = new Vector3(128, 128, 20);
727 public void ListenerRun()
728 {
729 string inputLine;
730
731 while (m_enabled)
732 {
733 try
734 {
735 while ((m_connected == true) && ((inputLine = m_reader.ReadLine()) != null))
736 {
737 m_log.Info("[IRC]: " + inputLine);
738 if (inputLine.Contains(m_channel))
739 {
740 Dictionary<string, string> data = ExtractMsg(inputLine);
741 // Any chat ???
742 if (data != null)
743 {
744 OSChatMessage c = new OSChatMessage();
745 c.Message = data["msg"];
746 c.Type = ChatTypeEnum.Say;
747 c.Channel = m_messageInChannel;
748 c.Position = pos;
749 c.From = data["nick"];
750 c.Sender = null;
751 c.SenderUUID = UUID.Zero;
752
753 // is message "\001ACTION foo
754 // bar\001"? -> "/me foo bar"
755 if ((1 == c.Message[0]) && c.Message.Substring(1).StartsWith("ACTION"))
756 c.Message = String.Format("/me {0}", c.Message.Substring(8, c.Message.Length - 9));
757
758 m_log.DebugFormat("[IRC] ListenerRun from: {0}, {1}", c.From, c.Message);
759
760 foreach (Scene scene in m_scenes)
761 {
762 if (m_useWorldComm)
763 {
764 IWorldComm wComm = scene.RequestModuleInterface<IWorldComm>();
765 wComm.DeliverMessage(ChatTypeEnum.Region, m_messageInChannel, c.From, UUID.Zero, c.Message);
766 }
767 else
768 {
769 c.Scene = scene;
770 scene.EventManager.TriggerOnChatBroadcast(this, c);
771 }
772 }
773 }
774
775 Thread.Sleep(150);
776 continue;
777 }
778
779 ProcessIRCCommand(inputLine);
780 Thread.Sleep(150);
781 }
782 }
783 catch (IOException)
784 {
785 if (m_enabled)
786 {
787 m_log.Error("[IRC]: ListenerRun IOException. Disconnected from IRC server ??? (ListenerRun)");
788 Reconnect();
789 }
790 }
791 catch (Exception ex)
792 {
793 m_log.ErrorFormat("[IRC]: ListenerRun exception trap: {0}\n{1}", ex.ToString(), ex.StackTrace);
794 }
795 }
796 }
797
798 public void BroadcastSim(string sender, string format, params string[] args)
799 {
800 try
801 {
802 OSChatMessage c = new OSChatMessage();
803 c.From = sender;
804 c.Message = String.Format(format, args);
805 c.Type = ChatTypeEnum.Region; // ChatTypeEnum.Say;
806 c.Channel = m_messageInChannel;
807 c.Position = new Vector3(128, 128, 20);
808 c.Sender = null;
809 c.SenderUUID = UUID.Zero;
810
811 m_log.DebugFormat("[IRC] BroadcastSim from {0}: {1}", c.From, c.Message);
812
813 foreach (Scene m_scene in m_scenes)
814 {
815 c.Scene = m_scene;
816 // m_scene.EventManager.TriggerOnChatBroadcast(this, c);
817 // m_scene.EventManager.TriggerOnChatFromWorld(this, c);
818 IWorldComm wComm = m_scene.RequestModuleInterface<IWorldComm>();
819 wComm.DeliverMessage(ChatTypeEnum.Region, m_messageInChannel, sender, UUID.Zero, c.Message);
820 //IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
821 //wComm.DeliverMessage(ChatTypeEnum.Region, channelID, m_host.Name, m_host.UUID, text);
822
823 }
824 }
825 catch (Exception ex) // IRC gate should not crash Sim
826 {
827 m_log.ErrorFormat("[IRC]: BroadcastSim Exception Trap: {0}\n{1}", ex.ToString(), ex.StackTrace);
828 }
829 }
830
831 public void ProcessIRCCommand(string command)
832 {
833 m_log.Debug("[IRC]: ProcessIRCCommand:" + command);
834
835 string[] commArgs = new string[command.Split(' ').Length];
836 string c_server = m_server;
837
838 commArgs = command.Split(' ');
839 if (commArgs[0].Substring(0, 1) == ":")
840 {
841 commArgs[0] = commArgs[0].Remove(0, 1);
842 }
843
844 if (commArgs[1] == "002")
845 {
846 // fetch the correct servername
847 // ex: irc.freenode.net -> brown.freenode.net/kornbluth.freenode.net/...
848 // irc.bluewin.ch -> irc1.bluewin.ch/irc2.bluewin.ch
849
850 c_server = (commArgs[6].Split('['))[0];
851 m_server = c_server;
852 }
853
854 if (commArgs[0] == "ERROR")
855 {
856 m_log.ErrorFormat("[IRC]: IRC SERVER ERROR: {0}", command);
857 }
858
859 if (commArgs[0] == "PING")
860 {
861 string p_reply = "";
862
863 for (int i = 1; i < commArgs.Length; i++)
864 {
865 p_reply += commArgs[i] + " ";
866 }
867
868 m_writer.WriteLine(String.Format("PONG {0}", p_reply));
869 m_writer.Flush();
870 }
871 else if (commArgs[0] == c_server)
872 {
873 // server message
874 try
875 {
876 Int32 commandCode = Int32.Parse(commArgs[1]);
877 switch (commandCode)
878 {
879 case (int)ErrorReplies.NicknameInUse:
880 // Gen a new name
881 m_nick = m_basenick + Util.RandomClass.Next(1, 99);
882 m_log.ErrorFormat("[IRC]: IRC SERVER reports NicknameInUse, trying {0}", m_nick);
883 // Retry
884 m_writer.WriteLine(String.Format("NICK {0}", m_nick));
885 m_writer.Flush();
886 m_writer.WriteLine(String.Format("JOIN {0}", m_channel));
887 m_writer.Flush();
888 break;
889 case (int)ErrorReplies.NotRegistered:
890 break;
891 case (int)Replies.EndOfMotd:
892 break;
893 }
894 }
895 catch (Exception)
896 {
897 }
898 }
899 else
900 {
901 // Normal message
902 string commAct = commArgs[1];
903 switch (commAct)
904 {
905 case "JOIN":
906 eventIrcJoin(commArgs);
907 break;
908 case "PART":
909 eventIrcPart(commArgs);
910 break;
911 case "MODE":
912 eventIrcMode(commArgs);
913 break;
914 case "NICK":
915 eventIrcNickChange(commArgs);
916 break;
917 case "KICK":
918 eventIrcKick(commArgs);
919 break;
920 case "QUIT":
921 eventIrcQuit(commArgs);
922 break;
923 case "PONG":
924 break; // that's nice
925 }
926 }
927 }
928
929 public void eventIrcJoin(string[] commArgs)
930 {
931 string IrcChannel = commArgs[2];
932 if (IrcChannel.StartsWith(":"))
933 IrcChannel = IrcChannel.Substring(1);
934 string IrcUser = commArgs[0].Split('!')[0];
935 if (m_verbosity >= 1)
936 BroadcastSim(IrcUser, "/me joins {0}", IrcChannel);
937 }
938
939 public void eventIrcPart(string[] commArgs)
940 {
941 string IrcChannel = commArgs[2];
942 string IrcUser = commArgs[0].Split('!')[0];
943 if (m_verbosity >= 2)
944 BroadcastSim(IrcUser, "/me parts {0}", IrcChannel);
945 }
946
947 public void eventIrcMode(string[] commArgs)
948 {
949 string UserMode = "";
950 for (int i = 3; i < commArgs.Length; i++)
951 {
952 UserMode += commArgs[i] + " ";
953 }
954
955 if (UserMode.Substring(0, 1) == ":")
956 {
957 UserMode = UserMode.Remove(0, 1);
958 }
959 }
960
961 public void eventIrcNickChange(string[] commArgs)
962 {
963 string UserOldNick = commArgs[0].Split('!')[0];
964 string UserNewNick = commArgs[2].Remove(0, 1);
965 if (m_verbosity >= 2)
966 BroadcastSim(UserOldNick, "/me is now known as {0}", UserNewNick);
967 }
968
969 public void eventIrcKick(string[] commArgs)
970 {
971 string UserKicker = commArgs[0].Split('!')[0];
972 string UserKicked = commArgs[3];
973 string IrcChannel = commArgs[2];
974 string KickMessage = "";
975 for (int i = 4; i < commArgs.Length; i++)
976 {
977 KickMessage += commArgs[i] + " ";
978 }
979 if (m_verbosity >= 1)
980 BroadcastSim(UserKicker, "/me kicks kicks {0} off {1} saying \"{2}\"", UserKicked, IrcChannel, KickMessage);
981 if (UserKicked == m_nick)
982 {
983 BroadcastSim(m_nick, "Hey, that was me!!!");
984 }
985 }
986
987 public void eventIrcQuit(string[] commArgs)
988 {
989 string IrcUser = commArgs[0].Split('!')[0];
990 string QuitMessage = "";
991
992 for (int i = 2; i < commArgs.Length; i++)
993 {
994 QuitMessage += commArgs[i] + " ";
995 }
996 if (m_verbosity >= 1)
997 BroadcastSim(IrcUser, "/me quits saying \"{0}\"", QuitMessage);
998 }
999
1000 public void Close()
1001 {
1002 m_writer.WriteLine(String.Format("QUIT :{0} to {1} wormhole to {2} closing",
1003 m_nick, m_channel, m_server));
1004 m_writer.Flush();
1005
1006 m_connected = false;
1007 m_enabled = false;
1008
1009 //listener.Abort();
1010 //pingSender.Abort();
1011
1012 m_writer.Close();
1013 m_reader.Close();
1014 m_stream.Close();
1015 m_tcp.Close();
1016 }
1017 } 384 }
1018} 385}