diff options
Diffstat (limited to 'OpenSim/Region/Environment/Modules/Avatar/Chat/ChatModule.cs')
-rw-r--r-- | OpenSim/Region/Environment/Modules/Avatar/Chat/ChatModule.cs | 831 |
1 files changed, 831 insertions, 0 deletions
diff --git a/OpenSim/Region/Environment/Modules/Avatar/Chat/ChatModule.cs b/OpenSim/Region/Environment/Modules/Avatar/Chat/ChatModule.cs new file mode 100644 index 0000000..1281873 --- /dev/null +++ b/OpenSim/Region/Environment/Modules/Avatar/Chat/ChatModule.cs | |||
@@ -0,0 +1,831 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.IO; | ||
31 | using System.Net.Sockets; | ||
32 | using System.Reflection; | ||
33 | using System.Text.RegularExpressions; | ||
34 | using System.Threading; | ||
35 | using libsecondlife; | ||
36 | using log4net; | ||
37 | using Nini.Config; | ||
38 | using OpenSim.Framework; | ||
39 | using OpenSim.Region.Environment.Interfaces; | ||
40 | using OpenSim.Region.Environment.Scenes; | ||
41 | |||
42 | namespace OpenSim.Region.Environment.Modules.Avatar.Chat | ||
43 | { | ||
44 | public class ChatModule : IRegionModule, ISimChat | ||
45 | { | ||
46 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
47 | |||
48 | private List<Scene> m_scenes = new List<Scene>(); | ||
49 | |||
50 | private int m_whisperdistance = 10; | ||
51 | private int m_saydistance = 30; | ||
52 | private int m_shoutdistance = 100; | ||
53 | |||
54 | private IRCChatModule m_irc = null; | ||
55 | |||
56 | private string m_last_new_user = null; | ||
57 | private string m_last_leaving_user = null; | ||
58 | private string m_defaultzone = null; | ||
59 | internal object m_syncInit = new object(); | ||
60 | internal object m_syncLogout = new object(); | ||
61 | private Thread m_irc_connector=null; | ||
62 | |||
63 | public void Initialise(Scene scene, IConfigSource config) | ||
64 | { | ||
65 | lock (m_syncInit) | ||
66 | { | ||
67 | if (!m_scenes.Contains(scene)) | ||
68 | { | ||
69 | m_scenes.Add(scene); | ||
70 | scene.EventManager.OnNewClient += NewClient; | ||
71 | scene.RegisterModuleInterface<ISimChat>(this); | ||
72 | } | ||
73 | |||
74 | // wrap this in a try block so that defaults will work if | ||
75 | // the config file doesn't specify otherwise. | ||
76 | try | ||
77 | { | ||
78 | m_whisperdistance = config.Configs["Chat"].GetInt("whisper_distance", m_whisperdistance); | ||
79 | m_saydistance = config.Configs["Chat"].GetInt("say_distance", m_saydistance); | ||
80 | m_shoutdistance = config.Configs["Chat"].GetInt("shout_distance", m_shoutdistance); | ||
81 | } | ||
82 | catch (Exception) | ||
83 | { | ||
84 | } | ||
85 | |||
86 | try | ||
87 | { | ||
88 | m_defaultzone = config.Configs["IRC"].GetString("nick","Sim"); | ||
89 | } | ||
90 | catch (Exception) | ||
91 | { | ||
92 | } | ||
93 | |||
94 | // setup IRC Relay | ||
95 | if (m_irc == null) { m_irc = new IRCChatModule(config); } | ||
96 | if (m_irc_connector == null) | ||
97 | { | ||
98 | m_irc_connector = new Thread(IRCConnectRun); | ||
99 | m_irc_connector.Name = "IRCConnectorThread"; | ||
100 | m_irc_connector.IsBackground = true; | ||
101 | } | ||
102 | } | ||
103 | } | ||
104 | |||
105 | public void PostInitialise() | ||
106 | { | ||
107 | if (m_irc.Enabled) | ||
108 | { | ||
109 | try | ||
110 | { | ||
111 | //m_irc.Connect(m_scenes); | ||
112 | if (m_irc_connector == null) | ||
113 | { | ||
114 | m_irc_connector = new Thread(IRCConnectRun); | ||
115 | m_irc_connector.Name = "IRCConnectorThread"; | ||
116 | m_irc_connector.IsBackground = true; | ||
117 | } | ||
118 | if (!m_irc_connector.IsAlive) | ||
119 | { | ||
120 | m_irc_connector.Start(); | ||
121 | ThreadTracker.Add(m_irc_connector); | ||
122 | } | ||
123 | } | ||
124 | catch (Exception) | ||
125 | { | ||
126 | } | ||
127 | } | ||
128 | } | ||
129 | |||
130 | public void Close() | ||
131 | { | ||
132 | m_irc.Close(); | ||
133 | } | ||
134 | |||
135 | public string Name | ||
136 | { | ||
137 | get { return "ChatModule"; } | ||
138 | } | ||
139 | |||
140 | public bool IsSharedModule | ||
141 | { | ||
142 | get { return true; } | ||
143 | } | ||
144 | |||
145 | public void NewClient(IClientAPI client) | ||
146 | { | ||
147 | try | ||
148 | { | ||
149 | client.OnChatFromViewer += SimChat; | ||
150 | |||
151 | if ((m_irc.Enabled) && (m_irc.Connected)) | ||
152 | { | ||
153 | string clientName = client.FirstName + " " + client.LastName; | ||
154 | // handles simple case. May not work for hundred connecting in per second. | ||
155 | // and the NewClients calles getting interleved | ||
156 | // but filters out multiple reports | ||
157 | if (clientName != m_last_new_user) | ||
158 | { | ||
159 | m_last_new_user = clientName; | ||
160 | string clientRegion = FindClientRegion(client.FirstName, client.LastName); | ||
161 | m_irc.PrivMsg(m_irc.Nick, "Sim", "notices " + clientName + " in "+clientRegion); | ||
162 | } | ||
163 | } | ||
164 | client.OnLogout += ClientLoggedOut; | ||
165 | client.OnConnectionClosed += ClientLoggedOut; | ||
166 | client.OnLogout += ClientLoggedOut; | ||
167 | } | ||
168 | catch (Exception ex) | ||
169 | { | ||
170 | m_log.Error("[IRC]: NewClient exception trap:" + ex.ToString()); | ||
171 | } | ||
172 | } | ||
173 | |||
174 | public void ClientLoggedOut(IClientAPI client) | ||
175 | { | ||
176 | lock (m_syncLogout) | ||
177 | { | ||
178 | try | ||
179 | { | ||
180 | if ((m_irc.Enabled) && (m_irc.Connected)) | ||
181 | { | ||
182 | string clientName = client.FirstName + " " + client.LastName; | ||
183 | string clientRegion = FindClientRegion(client.FirstName, client.LastName); | ||
184 | // handles simple case. May not work for hundred connecting in per second. | ||
185 | // and the NewClients calles getting interleved | ||
186 | // but filters out multiple reports | ||
187 | if (clientName != m_last_leaving_user) | ||
188 | { | ||
189 | m_last_leaving_user = clientName; | ||
190 | m_irc.PrivMsg(m_irc.Nick, "Sim", "notices " + clientName + " left " + clientRegion); | ||
191 | m_log.Info("[IRC]: IRC watcher notices " + clientName + " left " + clientRegion); | ||
192 | } | ||
193 | } | ||
194 | } | ||
195 | catch (Exception ex) | ||
196 | { | ||
197 | m_log.Error("[IRC]: ClientLoggedOut exception trap:" + ex.ToString()); | ||
198 | } | ||
199 | } | ||
200 | } | ||
201 | |||
202 | private void TrySendChatMessage(ScenePresence presence, LLVector3 fromPos, LLVector3 regionPos, | ||
203 | LLUUID fromAgentID, string fromName, ChatTypeEnum type, string message) | ||
204 | { | ||
205 | if (!presence.IsChildAgent) | ||
206 | { | ||
207 | LLVector3 fromRegionPos = fromPos + regionPos; | ||
208 | LLVector3 toRegionPos = presence.AbsolutePosition + regionPos; | ||
209 | int dis = Math.Abs((int) Util.GetDistanceTo(toRegionPos, fromRegionPos)); | ||
210 | |||
211 | if (type == ChatTypeEnum.Whisper && dis > m_whisperdistance || | ||
212 | type == ChatTypeEnum.Say && dis > m_saydistance || | ||
213 | type == ChatTypeEnum.Shout && dis > m_shoutdistance) | ||
214 | { | ||
215 | return; | ||
216 | } | ||
217 | |||
218 | // TODO: should change so the message is sent through the avatar rather than direct to the ClientView | ||
219 | presence.ControllingClient.SendChatMessage(message, (byte) type, fromPos, fromName, fromAgentID); | ||
220 | } | ||
221 | } | ||
222 | |||
223 | public void SimChat(Object sender, ChatFromViewerArgs e) | ||
224 | { | ||
225 | // FROM: Sim TO: IRC | ||
226 | |||
227 | ScenePresence avatar = null; | ||
228 | |||
229 | //TODO: Move ForEachScenePresence and others into IScene. | ||
230 | Scene scene = (Scene) e.Scene; | ||
231 | |||
232 | //TODO: Remove the need for this check | ||
233 | if (scene == null) | ||
234 | scene = m_scenes[0]; | ||
235 | |||
236 | // Filled in since it's easier than rewriting right now. | ||
237 | LLVector3 fromPos = e.Position; | ||
238 | LLVector3 regionPos = new LLVector3(scene.RegionInfo.RegionLocX * Constants.RegionSize, scene.RegionInfo.RegionLocY * Constants.RegionSize, 0); | ||
239 | |||
240 | string fromName = e.From; | ||
241 | string message = e.Message; | ||
242 | LLUUID fromAgentID = LLUUID.Zero; | ||
243 | |||
244 | if (e.Sender != null) | ||
245 | { | ||
246 | avatar = scene.GetScenePresence(e.Sender.AgentId); | ||
247 | } | ||
248 | |||
249 | if (avatar != null) | ||
250 | { | ||
251 | fromPos = avatar.AbsolutePosition; | ||
252 | regionPos = new LLVector3(scene.RegionInfo.RegionLocX * Constants.RegionSize, scene.RegionInfo.RegionLocY * Constants.RegionSize, 0); | ||
253 | fromName = avatar.Firstname + " " + avatar.Lastname; | ||
254 | fromAgentID = e.Sender.AgentId; | ||
255 | } | ||
256 | |||
257 | // Try to reconnect to server if not connected | ||
258 | if (m_irc.Enabled && !m_irc.Connected) | ||
259 | { | ||
260 | // In a non-blocking way. Eventually the connector will get it started | ||
261 | try | ||
262 | { | ||
263 | if (m_irc_connector == null) | ||
264 | { | ||
265 | m_irc_connector = new Thread(IRCConnectRun); | ||
266 | m_irc_connector.Name = "IRCConnectorThread"; | ||
267 | m_irc_connector.IsBackground = true; | ||
268 | } | ||
269 | if (!m_irc_connector.IsAlive) | ||
270 | { | ||
271 | m_irc_connector.Start(); | ||
272 | ThreadTracker.Add(m_irc_connector); | ||
273 | } | ||
274 | } | ||
275 | catch (Exception) | ||
276 | { | ||
277 | } | ||
278 | } | ||
279 | |||
280 | |||
281 | // We only want to relay stuff on channel 0 | ||
282 | if (e.Channel == 0) | ||
283 | { | ||
284 | // IRC stuff | ||
285 | if (e.Message.Length > 0) | ||
286 | { | ||
287 | if (m_irc.Connected && (avatar != null)) // this is to keep objects from talking to IRC | ||
288 | { | ||
289 | m_irc.PrivMsg(fromName, scene.RegionInfo.RegionName, e.Message); | ||
290 | } | ||
291 | } | ||
292 | |||
293 | foreach (Scene s in m_scenes) | ||
294 | { | ||
295 | s.ForEachScenePresence(delegate(ScenePresence presence) | ||
296 | { | ||
297 | TrySendChatMessage(presence, fromPos, regionPos, | ||
298 | fromAgentID, fromName, e.Type, message); | ||
299 | }); | ||
300 | } | ||
301 | } | ||
302 | } | ||
303 | |||
304 | // if IRC is enabled then just keep trying using a monitor thread | ||
305 | public void IRCConnectRun() | ||
306 | { | ||
307 | while(true) | ||
308 | { | ||
309 | if ((m_irc.Enabled)&&(!m_irc.Connected)) | ||
310 | { | ||
311 | m_irc.Connect(m_scenes); | ||
312 | } | ||
313 | Thread.Sleep(15000); | ||
314 | } | ||
315 | } | ||
316 | |||
317 | public string FindClientRegion(string client_FirstName,string client_LastName) | ||
318 | { | ||
319 | string sourceRegion = null; | ||
320 | foreach (Scene s in m_scenes) | ||
321 | { | ||
322 | s.ForEachScenePresence(delegate(ScenePresence presence) | ||
323 | { | ||
324 | if ((presence.IsChildAgent==false) | ||
325 | &&(presence.Firstname==client_FirstName) | ||
326 | &&(presence.Lastname==client_LastName)) | ||
327 | { | ||
328 | sourceRegion = presence.Scene.RegionInfo.RegionName; | ||
329 | //sourceRegion= s.RegionInfo.RegionName; | ||
330 | } | ||
331 | }); | ||
332 | if (sourceRegion != null) return sourceRegion; | ||
333 | } | ||
334 | if (m_defaultzone == null) { m_defaultzone = "Sim"; } | ||
335 | return m_defaultzone; | ||
336 | } | ||
337 | } | ||
338 | |||
339 | internal class IRCChatModule | ||
340 | { | ||
341 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
342 | |||
343 | private string m_server = null; | ||
344 | private uint m_port = 6668; | ||
345 | private string m_user = "USER OpenSimBot 8 * :I'm a OpenSim to irc bot"; | ||
346 | private string m_nick = null; | ||
347 | private string m_basenick = null; | ||
348 | private string m_channel = null; | ||
349 | private string m_privmsgformat = "PRIVMSG {0} :<{1} in {2}>: {3}"; | ||
350 | |||
351 | private NetworkStream m_stream; | ||
352 | private TcpClient m_tcp; | ||
353 | private StreamWriter m_writer; | ||
354 | private StreamReader m_reader; | ||
355 | |||
356 | private Thread pingSender; | ||
357 | private Thread listener; | ||
358 | internal object m_syncConnect = new object(); | ||
359 | |||
360 | private bool m_enabled = false; | ||
361 | private bool m_connected = false; | ||
362 | |||
363 | private List<Scene> m_scenes = null; | ||
364 | private List<Scene> m_last_scenes = null; | ||
365 | |||
366 | public IRCChatModule(IConfigSource config) | ||
367 | { | ||
368 | m_nick = "OSimBot" + Util.RandomClass.Next(1, 99); | ||
369 | m_tcp = null; | ||
370 | m_writer = null; | ||
371 | m_reader = null; | ||
372 | |||
373 | // configuration in OpenSim.ini | ||
374 | // [IRC] | ||
375 | // server = chat.freenode.net | ||
376 | // nick = OSimBot_mysim | ||
377 | // ;username = USER OpenSimBot 8 * :I'm a OpenSim to irc bot | ||
378 | // ; username is the IRC command line sent | ||
379 | // ; USER <irc_user> <visible=8,invisible=0> * : <IRC_realname> | ||
380 | // channel = #opensim-regions | ||
381 | // port = 6667 | ||
382 | // ;MSGformat fields : 0=botnick, 1=user, 2=region, 3=message | ||
383 | // ;for <bot>:<user in region> :<message> | ||
384 | // ;msgformat = "PRIVMSG {0} :<{1} in {2}>: {3}" | ||
385 | // ;for <bot>:<message> - <user of region> : | ||
386 | // ;msgformat = "PRIVMSG {0} : {3} - {1} of {2}" | ||
387 | // ;for <bot>:<message> - from <user> : | ||
388 | // ;msgformat = "PRIVMSG {0} : {3} - from {1}" | ||
389 | // Traps I/O disconnects so it does not crash the sim | ||
390 | // Trys to reconnect if disconnected and someone says something | ||
391 | // Tells IRC server "QUIT" when doing a close (just to be nice) | ||
392 | // Default port back to 6667 | ||
393 | |||
394 | try | ||
395 | { | ||
396 | m_server = config.Configs["IRC"].GetString("server"); | ||
397 | m_nick = config.Configs["IRC"].GetString("nick"); | ||
398 | m_basenick = m_nick; | ||
399 | m_channel = config.Configs["IRC"].GetString("channel"); | ||
400 | m_port = (uint) config.Configs["IRC"].GetInt("port", (int) m_port); | ||
401 | m_user = config.Configs["IRC"].GetString("username", m_user); | ||
402 | m_privmsgformat = config.Configs["IRC"].GetString("msgformat", m_privmsgformat); | ||
403 | if (m_server != null && m_nick != null && m_channel != null) | ||
404 | { | ||
405 | m_nick = m_nick + Util.RandomClass.Next(1, 99); | ||
406 | m_enabled = true; | ||
407 | } | ||
408 | } | ||
409 | catch (Exception) | ||
410 | { | ||
411 | m_log.Info("[CHAT]: No IRC config information, skipping IRC bridge configuration"); | ||
412 | } | ||
413 | } | ||
414 | |||
415 | public bool Connect(List<Scene> scenes) | ||
416 | { | ||
417 | lock (m_syncConnect) | ||
418 | { | ||
419 | try | ||
420 | { | ||
421 | if (m_connected) return true; | ||
422 | m_scenes = scenes; | ||
423 | if (m_last_scenes == null) { m_last_scenes = scenes; } | ||
424 | |||
425 | m_tcp = new TcpClient(m_server, (int)m_port); | ||
426 | m_log.Info("[IRC]: Connecting..."); | ||
427 | m_stream = m_tcp.GetStream(); | ||
428 | m_log.Info("[IRC]: Connected to " + m_server); | ||
429 | m_reader = new StreamReader(m_stream); | ||
430 | m_writer = new StreamWriter(m_stream); | ||
431 | |||
432 | pingSender = new Thread(new ThreadStart(PingRun)); | ||
433 | pingSender.Name = "PingSenderThread"; | ||
434 | pingSender.IsBackground = true; | ||
435 | pingSender.Start(); | ||
436 | ThreadTracker.Add(pingSender); | ||
437 | |||
438 | listener = new Thread(new ThreadStart(ListenerRun)); | ||
439 | listener.Name = "IRCChatModuleListenerThread"; | ||
440 | listener.IsBackground = true; | ||
441 | listener.Start(); | ||
442 | ThreadTracker.Add(listener); | ||
443 | |||
444 | m_writer.WriteLine(m_user); | ||
445 | m_writer.Flush(); | ||
446 | m_writer.WriteLine("NICK " + m_nick); | ||
447 | m_writer.Flush(); | ||
448 | m_writer.WriteLine("JOIN " + m_channel); | ||
449 | m_writer.Flush(); | ||
450 | m_log.Info("[IRC]: Connection fully established"); | ||
451 | m_connected = true; | ||
452 | } | ||
453 | catch (Exception e) | ||
454 | { | ||
455 | Console.WriteLine(e.ToString()); | ||
456 | } | ||
457 | return m_connected; | ||
458 | } | ||
459 | } | ||
460 | |||
461 | public bool Enabled | ||
462 | { | ||
463 | get { return m_enabled; } | ||
464 | } | ||
465 | |||
466 | public bool Connected | ||
467 | { | ||
468 | get { return m_connected; } | ||
469 | } | ||
470 | |||
471 | public string Nick | ||
472 | { | ||
473 | get { return m_nick; } | ||
474 | } | ||
475 | |||
476 | public void Reconnect() | ||
477 | { | ||
478 | m_connected = false; | ||
479 | listener.Abort(); | ||
480 | pingSender.Abort(); | ||
481 | m_writer.Close(); | ||
482 | m_reader.Close(); | ||
483 | m_tcp.Close(); | ||
484 | if (m_enabled) { Connect(m_last_scenes); } | ||
485 | } | ||
486 | |||
487 | public void PrivMsg(string from, string region, string msg) | ||
488 | { | ||
489 | // One message to the IRC server | ||
490 | |||
491 | try | ||
492 | { | ||
493 | if (m_privmsgformat == null) | ||
494 | { | ||
495 | m_writer.WriteLine("PRIVMSG {0} :<{1} in {2}>: {3}", m_channel, from, region, msg); | ||
496 | } | ||
497 | else | ||
498 | { | ||
499 | m_writer.WriteLine(m_privmsgformat, m_channel, from, region, msg); | ||
500 | } | ||
501 | m_writer.Flush(); | ||
502 | m_log.Info("[IRC]: PrivMsg " + from + " in " + region + " :" + msg); | ||
503 | } | ||
504 | catch (IOException) | ||
505 | { | ||
506 | m_log.Error("[IRC]: Disconnected from IRC server.(PrivMsg)"); | ||
507 | Reconnect(); | ||
508 | } | ||
509 | catch (Exception ex) | ||
510 | { | ||
511 | m_log.Error("[IRC]: PrivMsg exception trap:" + ex.ToString()); | ||
512 | } | ||
513 | } | ||
514 | |||
515 | private Dictionary<string, string> ExtractMsg(string input) | ||
516 | { | ||
517 | //examines IRC commands and extracts any private messages | ||
518 | // which will then be reboadcast in the Sim | ||
519 | |||
520 | m_log.Info("[IRC]: ExtractMsg: " + input); | ||
521 | Dictionary<string, string> result = null; | ||
522 | //string regex = @":(?<nick>\w*)!~(?<user>\S*) PRIVMSG (?<channel>\S+) :(?<msg>.*)"; | ||
523 | string regex = @":(?<nick>\w*)!(?<user>\S*) PRIVMSG (?<channel>\S+) :(?<msg>.*)"; | ||
524 | Regex RE = new Regex(regex, RegexOptions.Multiline); | ||
525 | MatchCollection matches = RE.Matches(input); | ||
526 | // Get some direct matches $1 $4 is a | ||
527 | if ((matches.Count == 1) && (matches[0].Groups.Count == 5)) | ||
528 | { | ||
529 | result = new Dictionary<string, string>(); | ||
530 | result.Add("nick", matches[0].Groups[1].Value); | ||
531 | result.Add("user", matches[0].Groups[2].Value); | ||
532 | result.Add("channel", matches[0].Groups[3].Value); | ||
533 | result.Add("msg", matches[0].Groups[4].Value); | ||
534 | } | ||
535 | else | ||
536 | { | ||
537 | m_log.Info("[IRC]: Number of matches: " + matches.Count); | ||
538 | if (matches.Count > 0) | ||
539 | { | ||
540 | m_log.Info("[IRC]: Number of groups: " + matches[0].Groups.Count); | ||
541 | } | ||
542 | } | ||
543 | return result; | ||
544 | } | ||
545 | |||
546 | public void PingRun() | ||
547 | { | ||
548 | // IRC keep alive thread | ||
549 | // send PING ever 15 seconds | ||
550 | while (true) | ||
551 | { | ||
552 | try | ||
553 | { | ||
554 | if (m_connected == true) | ||
555 | { | ||
556 | m_writer.WriteLine("PING :" + m_server); | ||
557 | m_writer.Flush(); | ||
558 | Thread.Sleep(15000); | ||
559 | } | ||
560 | } | ||
561 | catch (IOException) | ||
562 | { | ||
563 | m_log.Error("[IRC]: Disconnected from IRC server.(PingRun)"); | ||
564 | Reconnect(); | ||
565 | } | ||
566 | catch (Exception ex) | ||
567 | { | ||
568 | m_log.Error("[IRC]: PingRun exception trap:" + ex.ToString() + "\n" + ex.StackTrace); | ||
569 | } | ||
570 | } | ||
571 | } | ||
572 | |||
573 | public void ListenerRun() | ||
574 | { | ||
575 | string inputLine; | ||
576 | LLVector3 pos = new LLVector3(128, 128, 20); | ||
577 | while (true) | ||
578 | { | ||
579 | try | ||
580 | { | ||
581 | while ((m_connected == true) && ((inputLine = m_reader.ReadLine()) != null)) | ||
582 | { | ||
583 | // Console.WriteLine(inputLine); | ||
584 | if (inputLine.Contains(m_channel)) | ||
585 | { | ||
586 | Dictionary<string, string> data = ExtractMsg(inputLine); | ||
587 | // Any chat ??? | ||
588 | if (data != null) | ||
589 | { | ||
590 | foreach (Scene m_scene in m_scenes) | ||
591 | { | ||
592 | m_scene.ForEachScenePresence(delegate(ScenePresence avatar) | ||
593 | { | ||
594 | if (!avatar.IsChildAgent) | ||
595 | { | ||
596 | avatar.ControllingClient.SendChatMessage( | ||
597 | Helpers.StringToField(data["msg"]), 255, | ||
598 | pos, data["nick"], | ||
599 | LLUUID.Zero); | ||
600 | } | ||
601 | }); | ||
602 | } | ||
603 | } | ||
604 | else | ||
605 | { | ||
606 | // Was an command from the IRC server | ||
607 | ProcessIRCCommand(inputLine); | ||
608 | } | ||
609 | } | ||
610 | else | ||
611 | { | ||
612 | // Was an command from the IRC server | ||
613 | ProcessIRCCommand(inputLine); | ||
614 | } | ||
615 | Thread.Sleep(150); | ||
616 | } | ||
617 | } | ||
618 | catch (IOException) | ||
619 | { | ||
620 | m_log.Error("[IRC]: ListenerRun IOException. Disconnected from IRC server ??? (ListenerRun)"); | ||
621 | Reconnect(); | ||
622 | } | ||
623 | catch (Exception ex) | ||
624 | { | ||
625 | m_log.Error("[IRC]: ListenerRun exception trap:" + ex.ToString() + "\n" + ex.StackTrace); | ||
626 | } | ||
627 | } | ||
628 | } | ||
629 | |||
630 | public void BroadcastSim(string message,string sender) | ||
631 | { | ||
632 | LLVector3 pos = new LLVector3(128, 128, 20); | ||
633 | try | ||
634 | { | ||
635 | foreach (Scene m_scene in m_scenes) | ||
636 | { | ||
637 | m_scene.ForEachScenePresence(delegate(ScenePresence avatar) | ||
638 | { | ||
639 | if (!avatar.IsChildAgent) | ||
640 | { | ||
641 | avatar.ControllingClient.SendChatMessage( | ||
642 | Helpers.StringToField(message), 255, | ||
643 | pos, sender, | ||
644 | LLUUID.Zero); | ||
645 | } | ||
646 | }); | ||
647 | } | ||
648 | } | ||
649 | catch (Exception ex) // IRC gate should not crash Sim | ||
650 | { | ||
651 | m_log.Error("[IRC]: BroadcastSim Exception Trap:" + ex.ToString() + "\n" + ex.StackTrace); | ||
652 | } | ||
653 | } | ||
654 | |||
655 | public enum ErrorReplies | ||
656 | { | ||
657 | NotRegistered = 451, // ":You have not registered" | ||
658 | NicknameInUse = 433 // "<nick> :Nickname is already in use" | ||
659 | } | ||
660 | |||
661 | public enum Replies | ||
662 | { | ||
663 | MotdStart = 375, // ":- <server> Message of the day - " | ||
664 | Motd = 372, // ":- <text>" | ||
665 | EndOfMotd = 376 // ":End of /MOTD command" | ||
666 | } | ||
667 | |||
668 | public void ProcessIRCCommand(string command) | ||
669 | { | ||
670 | //m_log.Info("[IRC]: ProcessIRCCommand:" + command); | ||
671 | |||
672 | string[] commArgs = new string[command.Split(' ').Length]; | ||
673 | string c_server = m_server; | ||
674 | |||
675 | commArgs = command.Split(' '); | ||
676 | if (commArgs[0].Substring(0, 1) == ":") | ||
677 | { | ||
678 | commArgs[0] = commArgs[0].Remove(0, 1); | ||
679 | } | ||
680 | |||
681 | if (commArgs[1] == "002") | ||
682 | { | ||
683 | // fetch the correct servername | ||
684 | // ex: irc.freenode.net -> brown.freenode.net/kornbluth.freenode.net/... | ||
685 | // irc.bluewin.ch -> irc1.bluewin.ch/irc2.bluewin.ch | ||
686 | |||
687 | c_server = (commArgs[6].Split('['))[0]; | ||
688 | m_server = c_server; | ||
689 | } | ||
690 | |||
691 | if (commArgs[0] == "ERROR") | ||
692 | { | ||
693 | m_log.Error("[IRC]: IRC SERVER ERROR:" + command); | ||
694 | } | ||
695 | |||
696 | if (commArgs[0] == "PING") | ||
697 | { | ||
698 | string p_reply = ""; | ||
699 | |||
700 | for (int i = 1; i < commArgs.Length; i++) | ||
701 | { | ||
702 | p_reply += commArgs[i] + " "; | ||
703 | } | ||
704 | |||
705 | m_writer.WriteLine("PONG " + p_reply); | ||
706 | m_writer.Flush(); | ||
707 | } | ||
708 | else if (commArgs[0] == c_server) | ||
709 | { | ||
710 | // server message | ||
711 | try | ||
712 | { | ||
713 | Int32 commandCode = Int32.Parse(commArgs[1]); | ||
714 | switch (commandCode) | ||
715 | { | ||
716 | case (int)ErrorReplies.NicknameInUse: | ||
717 | // Gen a new name | ||
718 | m_nick = m_basenick + Util.RandomClass.Next(1, 99); | ||
719 | m_log.Error("[IRC]: IRC SERVER reports NicknameInUse, trying " + m_nick); | ||
720 | // Retry | ||
721 | m_writer.WriteLine("NICK " + m_nick); | ||
722 | m_writer.Flush(); | ||
723 | m_writer.WriteLine("JOIN " + m_channel); | ||
724 | m_writer.Flush(); | ||
725 | break; | ||
726 | case (int)ErrorReplies.NotRegistered: | ||
727 | break; | ||
728 | case (int)Replies.EndOfMotd: | ||
729 | break; | ||
730 | } | ||
731 | } | ||
732 | catch (Exception) | ||
733 | { | ||
734 | } | ||
735 | } | ||
736 | else | ||
737 | { | ||
738 | // Normal message | ||
739 | string commAct = commArgs[1]; | ||
740 | switch (commAct) | ||
741 | { | ||
742 | case "JOIN": eventIrcJoin(commArgs); break; | ||
743 | case "PART": eventIrcPart(commArgs); break; | ||
744 | case "MODE": eventIrcMode(commArgs); break; | ||
745 | case "NICK": eventIrcNickChange(commArgs); break; | ||
746 | case "KICK": eventIrcKick(commArgs); break; | ||
747 | case "QUIT": eventIrcQuit(commArgs); break; | ||
748 | case "PONG": break; // that's nice | ||
749 | } | ||
750 | } | ||
751 | } | ||
752 | |||
753 | public void eventIrcJoin(string[] commArgs) | ||
754 | { | ||
755 | string IrcChannel = commArgs[2]; | ||
756 | string IrcUser = commArgs[0].Split('!')[0]; | ||
757 | BroadcastSim(IrcUser + " is joining " + IrcChannel, m_nick); | ||
758 | } | ||
759 | |||
760 | public void eventIrcPart(string[] commArgs) | ||
761 | { | ||
762 | string IrcChannel = commArgs[2]; | ||
763 | string IrcUser = commArgs[0].Split('!')[0]; | ||
764 | BroadcastSim(IrcUser + " is parting " + IrcChannel, m_nick); | ||
765 | } | ||
766 | |||
767 | public void eventIrcMode(string[] commArgs) | ||
768 | { | ||
769 | string IrcChannel = commArgs[2]; | ||
770 | string IrcUser = commArgs[0].Split('!')[0]; | ||
771 | string UserMode = ""; | ||
772 | for (int i = 3; i < commArgs.Length; i++) | ||
773 | { | ||
774 | UserMode += commArgs[i] + " "; | ||
775 | } | ||
776 | |||
777 | if (UserMode.Substring(0, 1) == ":") | ||
778 | { | ||
779 | UserMode = UserMode.Remove(0, 1); | ||
780 | } | ||
781 | } | ||
782 | |||
783 | public void eventIrcNickChange(string[] commArgs) | ||
784 | { | ||
785 | string UserOldNick = commArgs[0].Split('!')[0]; | ||
786 | string UserNewNick = commArgs[2].Remove(0, 1); | ||
787 | BroadcastSim(UserOldNick + " changed their nick to " + UserNewNick, m_nick); | ||
788 | } | ||
789 | |||
790 | public void eventIrcKick(string[] commArgs) | ||
791 | { | ||
792 | string UserKicker = commArgs[0].Split('!')[0]; | ||
793 | string UserKicked = commArgs[3]; | ||
794 | string IrcChannel = commArgs[2]; | ||
795 | string KickMessage = ""; | ||
796 | for (int i = 4; i < commArgs.Length; i++) | ||
797 | { | ||
798 | KickMessage += commArgs[i] + " "; | ||
799 | } | ||
800 | BroadcastSim(UserKicker + " kicked " + UserKicked +" on "+IrcChannel+" saying "+KickMessage, m_nick); | ||
801 | if (UserKicked == m_nick) | ||
802 | { | ||
803 | BroadcastSim("Hey, that was me!!!", m_nick); | ||
804 | } | ||
805 | } | ||
806 | |||
807 | public void eventIrcQuit(string[] commArgs) | ||
808 | { | ||
809 | string IrcUser = commArgs[0].Split('!')[0]; | ||
810 | string QuitMessage = ""; | ||
811 | |||
812 | for (int i = 2; i < commArgs.Length; i++) | ||
813 | { | ||
814 | QuitMessage += commArgs[i] + " "; | ||
815 | } | ||
816 | BroadcastSim(IrcUser + " quits saying " + QuitMessage, m_nick); | ||
817 | } | ||
818 | |||
819 | public void Close() | ||
820 | { | ||
821 | m_connected = false; | ||
822 | m_writer.WriteLine("QUIT :" + m_nick + " to " + m_channel + " wormhole with " + m_server + " closing"); | ||
823 | m_writer.Flush(); | ||
824 | listener.Abort(); | ||
825 | pingSender.Abort(); | ||
826 | m_writer.Close(); | ||
827 | m_reader.Close(); | ||
828 | m_tcp.Close(); | ||
829 | } | ||
830 | } | ||
831 | } \ No newline at end of file | ||