ChatModule.cs 31 KB

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