ChatModule.cs 30 KB

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