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