The easy to use and full featured Irc Bot everyone is talking about!
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Linq.Expressions;
  5. using System.Text;
  6. using System.Threading;
  7. using System.Threading.Tasks;
  8. using System.Net;
  9. using System.Net.NetworkInformation;
  10. using System.Net.Sockets;
  11. using Combot.IRCServices.Messaging;
  12. using Combot.IRCServices.Commanding;
  13. using Combot.IRCServices.TCP;
  14. namespace Combot.IRCServices
  15. {
  16. public partial class IRC
  17. {
  18. public List<Channel> Channels = new List<Channel>();
  19. public Messages Message;
  20. public Commands Command;
  21. public event Action ConnectEvent;
  22. public event Action DisconnectEvent;
  23. public event Action<TCPError> TCPErrorEvent;
  24. public string Nickname;
  25. public Dictionary<string, PrivilegeMode> PrivilegeMapping = new Dictionary<string, PrivilegeMode>() { { "+", PrivilegeMode.v }, { "%", PrivilegeMode.h }, { "@", PrivilegeMode.o }, { "&", PrivilegeMode.a }, { "~", PrivilegeMode.q } };
  26. private int ReadTimeout;
  27. private int AllowedFailedReads;
  28. private Thread TCPReader;
  29. private Thread KeepAlive;
  30. private event Action<string> TCPMessageEvent;
  31. private readonly TCPInterface _TCP;
  32. private readonly ReaderWriterLockSlim ChannelRWLock;
  33. public IRC(int maxMessageLength, int messageSendDelay = 0, int readTimeout = 5000, int allowedFailedReads = 0)
  34. {
  35. _TCP = new TCPInterface();
  36. Message = new Messages(this);
  37. Command = new Commands(this, maxMessageLength, messageSendDelay);
  38. Nickname = string.Empty;
  39. ChannelRWLock = new ReaderWriterLockSlim();
  40. ReadTimeout = readTimeout;
  41. AllowedFailedReads = allowedFailedReads;
  42. TCPMessageEvent += Message.ParseTCPMessage;
  43. _TCP.TCPConnectionEvent += HandleTCPConnection;
  44. _TCP.TCPErrorEvent += HandleTCPError;
  45. Message.ErrorMessageEvent += HandleErrorMessage;
  46. Message.PingEvent += HandlePing;
  47. Message.ServerReplyEvent += HandleReply;
  48. Message.ChannelModeChangeEvent += HandleChannelModeChange;
  49. Message.UserModeChangeEvent += HandleUserModeChange;
  50. Message.NickChangeEvent += HandleNickChange;
  51. Message.JoinChannelEvent += HandleJoin;
  52. Message.PartChannelEvent += HandlePart;
  53. Message.KickEvent += HandleKick;
  54. Message.QuitEvent += HandleQuit;
  55. }
  56. /// <summary>
  57. /// Starts a TCP connection to the specified host.
  58. /// </summary>
  59. /// <param name="IP">The IP address of the host.</param>
  60. /// <param name="port">The port for the tcp connection.</param>
  61. /// <param name="readTimeout">The timeout for read operations in milliseconds.</param>
  62. /// <param name="allowedFailedCount">Number of times a read can fail before disconnecting.</param>
  63. /// <returns></returns>
  64. public bool Connect(IPAddress IP, int port)
  65. {
  66. bool result = false;
  67. if (!_TCP.Connected)
  68. {
  69. result = _TCP.Connect(IP, port, ReadTimeout, AllowedFailedReads);
  70. if (result)
  71. {
  72. TCPReader = new Thread(ReadTCPMessages);
  73. TCPReader.IsBackground = true;
  74. TCPReader.Start();
  75. KeepAlive = new Thread(() => CheckConnection(IP, port));
  76. KeepAlive.IsBackground = true;
  77. KeepAlive.Start();
  78. if (ConnectEvent != null)
  79. {
  80. ConnectEvent();
  81. }
  82. }
  83. }
  84. return result;
  85. }
  86. /// <summary>
  87. /// Disconencts from the active TCP connection.
  88. /// </summary>
  89. /// <returns></returns>
  90. public void Disconnect()
  91. {
  92. if (_TCP.Connected)
  93. {
  94. _TCP.Disconnect();
  95. }
  96. if (KeepAlive.IsAlive)
  97. {
  98. KeepAlive.Join();
  99. }
  100. if (TCPReader.IsAlive)
  101. {
  102. TCPReader.Join();
  103. }
  104. ChannelRWLock.EnterWriteLock();
  105. Channels = new List<Channel>();
  106. ChannelRWLock.ExitWriteLock();
  107. if (DisconnectEvent != null)
  108. {
  109. DisconnectEvent();
  110. }
  111. }
  112. /// <summary>
  113. /// Logs in the specified nick using their Username and Realname.
  114. /// </summary>
  115. /// <param name="serverName">The server's name.</param>
  116. /// <param name="nick">The nick information for the login.</param>
  117. public void Login(string serverName, Nick nick)
  118. {
  119. Nickname = nick.Nickname;
  120. Command.SendNick(nick.Nickname);
  121. Command.SendUser(nick.Username, nick.Host, serverName, nick.Realname);
  122. }
  123. /// <summary>
  124. /// Parses a given mode and parameter string to generate a channel mode list.
  125. /// </summary>
  126. /// <param name="modeString">The mode string that contains the mode info.</param>
  127. /// <param name="parameterString">The parameter string that is associated with the mode info.</param>
  128. /// <returns></returns>
  129. public List<ChannelModeInfo> ParseChannelModeString(string modeString, string parameterString)
  130. {
  131. string[] modeArgs = parameterString.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
  132. char[] modeInfo = modeString.ToCharArray();
  133. bool set = true;
  134. int argIndex = 0;
  135. List<ChannelModeInfo> modeInfos = new List<ChannelModeInfo>();
  136. foreach (char mode in modeInfo)
  137. {
  138. if (mode.Equals('-'))
  139. {
  140. set = false;
  141. }
  142. else if (mode.Equals('+'))
  143. {
  144. set = true;
  145. }
  146. else
  147. {
  148. ChannelModeInfo newMode = new ChannelModeInfo();
  149. newMode.Set = set;
  150. ChannelMode foundMode;
  151. bool validMode = Enum.TryParse(mode.ToString(), false, out foundMode);
  152. if (validMode)
  153. {
  154. newMode.Mode = foundMode;
  155. if (modeArgs.GetUpperBound(0) >= argIndex)
  156. {
  157. switch (newMode.Mode)
  158. {
  159. case ChannelMode.k:
  160. case ChannelMode.l:
  161. case ChannelMode.b:
  162. case ChannelMode.e:
  163. case ChannelMode.I:
  164. case ChannelMode.v:
  165. case ChannelMode.h:
  166. case ChannelMode.o:
  167. case ChannelMode.a:
  168. case ChannelMode.q:
  169. newMode.Parameter = modeArgs[argIndex];
  170. argIndex++;
  171. break;
  172. default:
  173. newMode.Parameter = string.Empty;
  174. break;
  175. }
  176. }
  177. else
  178. {
  179. newMode.Parameter = string.Empty;
  180. }
  181. modeInfos.Add(newMode);
  182. }
  183. }
  184. }
  185. return modeInfos;
  186. }
  187. public List<UserModeInfo> ParseUserModeString(string modeString)
  188. {
  189. List<UserModeInfo> userModes = new List<UserModeInfo>();
  190. bool set = true;
  191. char[] modeArr = modeString.ToCharArray();
  192. for (int i = 1; i <= modeArr.GetUpperBound(0); i++)
  193. {
  194. UserModeInfo newMode = new UserModeInfo();
  195. if (modeArr[i].Equals('-'))
  196. {
  197. set = false;
  198. }
  199. else if (modeArr[i].Equals('+'))
  200. {
  201. set = true;
  202. }
  203. else if (modeArr[i].Equals('*'))
  204. {
  205. newMode.Mode = UserMode.o;
  206. newMode.Set = set;
  207. userModes.Add(newMode);
  208. }
  209. else
  210. {
  211. UserMode foundMode;
  212. bool validMode = Enum.TryParse(modeArr[i].ToString(), false, out foundMode);
  213. if (validMode)
  214. {
  215. newMode.Mode = foundMode;
  216. newMode.Set = set;
  217. userModes.Add(newMode);
  218. }
  219. }
  220. }
  221. return userModes;
  222. }
  223. private void ReadTCPMessages()
  224. {
  225. while (_TCP.Connected)
  226. {
  227. string response = ReadTCPMessage();
  228. if (TCPMessageEvent != null && response != null && response != string.Empty)
  229. {
  230. TCPMessageEvent(response);
  231. }
  232. Thread.Sleep(10);
  233. }
  234. }
  235. private string ReadTCPMessage()
  236. {
  237. if (_TCP.Connected)
  238. {
  239. return _TCP.Read();
  240. }
  241. return null;
  242. }
  243. internal void SendTCPMessage(string message)
  244. {
  245. if (_TCP.Connected)
  246. {
  247. string replaceWith = string.Empty;
  248. string parsedMessage = message.Replace("\r\n", replaceWith).Replace("\n", replaceWith).Replace("\r", replaceWith);
  249. _TCP.Write(parsedMessage);
  250. }
  251. }
  252. private void CheckConnection(IPAddress IP, int port)
  253. {
  254. int diconnectCount = 0;
  255. bool disconnectActivated = false;
  256. while (_TCP.Connected)
  257. {
  258. Thread.Sleep(5000);
  259. bool stillConnected = NetworkInterface.GetIsNetworkAvailable();
  260. if (stillConnected)
  261. {
  262. Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  263. try
  264. {
  265. s.Connect(IP, port);
  266. }
  267. catch
  268. {
  269. stillConnected = false;
  270. }
  271. }
  272. if (!stillConnected)
  273. {
  274. diconnectCount++;
  275. }
  276. else
  277. {
  278. diconnectCount = 0;
  279. }
  280. if (diconnectCount >= 5 && !disconnectActivated)
  281. {
  282. disconnectActivated = true;
  283. Task.Run(() =>
  284. {
  285. Disconnect();
  286. });
  287. }
  288. }
  289. }
  290. /// <summary>
  291. /// Responds with PONG on a PING with the specified arguments.
  292. /// </summary>
  293. /// <param name="sender"></param>
  294. /// <param name="e"></param>
  295. private void HandlePing(object sender, PingInfo e)
  296. {
  297. Command.SendPong(e.Message);
  298. }
  299. private void HandleTCPConnection(int e)
  300. {
  301. if (DisconnectEvent != null)
  302. {
  303. DisconnectEvent();
  304. }
  305. }
  306. private void HandleTCPError(TCPError e)
  307. {
  308. if (TCPErrorEvent != null)
  309. {
  310. TCPErrorEvent(e);
  311. }
  312. }
  313. private void HandleErrorMessage(object sender, ErrorMessage e)
  314. {
  315. Disconnect();
  316. }
  317. private void HandleReply(object sender, IReply e)
  318. {
  319. if (e.GetType() == typeof(ServerReplyMessage))
  320. {
  321. ServerReplyMessage msg = (ServerReplyMessage)e;
  322. switch (msg.ReplyCode)
  323. {
  324. // If we get a WHO response, we parse and add the nicks to the specified channel if they are not there already.
  325. case IRCReplyCode.RPL_WHOREPLY:
  326. ChannelRWLock.EnterWriteLock();
  327. string[] msgSplit = msg.Message.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
  328. if (msgSplit.GetUpperBound(0) > 0)
  329. {
  330. string target = msgSplit[0];
  331. if (target.StartsWith("&") || target.StartsWith("#"))
  332. {
  333. if (msgSplit.GetUpperBound(0) >= 7)
  334. {
  335. string nickname = msgSplit[4];
  336. string realname = msgSplit[7];
  337. string username = msgSplit[1];
  338. string host = msgSplit[2];
  339. string modeString = msgSplit[5];
  340. Channel channel = Channels.Find(chan => chan.Name == target);
  341. if (channel != null)
  342. {
  343. Nick nick = channel.GetNick(nickname);
  344. bool nickFound = true;
  345. if (nick == null)
  346. {
  347. nickFound = false;
  348. nick = new Nick();
  349. }
  350. nick.Nickname = nickname;
  351. nick.Host = host;
  352. nick.Realname = realname;
  353. nick.Username = username;
  354. nick.Modes = new List<UserMode>();
  355. nick.Privileges = new List<PrivilegeMode>();
  356. char[] modeArr = modeString.ToCharArray();
  357. for (int i = 1; i <= modeArr.GetUpperBound(0); i++)
  358. {
  359. if (PrivilegeMapping.ContainsKey(modeArr[i].ToString()))
  360. {
  361. nick.Privileges.Add(PrivilegeMapping[modeArr[i].ToString()]);
  362. }
  363. else if (modeArr[i].ToString() == "*")
  364. {
  365. nick.Modes.Add(UserMode.o);
  366. }
  367. else
  368. {
  369. UserMode foundMode;
  370. bool valid = Enum.TryParse(modeArr[i].ToString(), false, out foundMode);
  371. if (valid)
  372. {
  373. nick.Modes.Add(foundMode);
  374. }
  375. }
  376. }
  377. if (!nickFound)
  378. {
  379. channel.AddNick(nick);
  380. }
  381. }
  382. }
  383. }
  384. }
  385. ChannelRWLock.ExitWriteLock();
  386. break;
  387. // On a topic reply, update the channel's topic
  388. case IRCReplyCode.RPL_TOPIC:
  389. ChannelRWLock.EnterWriteLock();
  390. string[] topicSplit = msg.Message.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
  391. if (topicSplit.GetUpperBound(0) > 0)
  392. {
  393. string topicChan = topicSplit[0];
  394. Channel topicChannel = Channels.Find(chan => chan.Name == topicChan);
  395. if (topicChannel != null)
  396. {
  397. topicChannel.Topic = topicSplit[1].Remove(0, 1);
  398. }
  399. }
  400. ChannelRWLock.ExitWriteLock();
  401. break;
  402. default:
  403. break;
  404. }
  405. }
  406. else
  407. {
  408. ServerErrorMessage msg = (ServerErrorMessage)e;
  409. }
  410. }
  411. /// <summary>
  412. /// Update a channel's mode.
  413. /// </summary>
  414. /// <param name="sender"></param>
  415. /// <param name="e"></param>
  416. private void HandleChannelModeChange(object sender, ChannelModeChangeInfo e)
  417. {
  418. ChannelRWLock.EnterWriteLock();
  419. Channel channel = Channels.Find(chan => chan.Name == e.Channel);
  420. if (channel != null)
  421. {
  422. foreach (ChannelModeInfo mode in e.Modes)
  423. {
  424. switch (mode.Mode)
  425. {
  426. case ChannelMode.v:
  427. case ChannelMode.h:
  428. case ChannelMode.o:
  429. case ChannelMode.a:
  430. case ChannelMode.q:
  431. Nick changedNick = channel.GetNick(mode.Parameter);
  432. if (changedNick != null)
  433. {
  434. if (mode.Set)
  435. {
  436. changedNick.AddPrivilege((PrivilegeMode) Enum.Parse(typeof (PrivilegeMode), mode.Mode.ToString()));
  437. }
  438. else
  439. {
  440. changedNick.RemovePrivilege((PrivilegeMode) Enum.Parse(typeof (PrivilegeMode), mode.Mode.ToString()));
  441. }
  442. }
  443. break;
  444. case ChannelMode.b:
  445. if (mode.Set)
  446. {
  447. channel.AddBan(mode.Parameter);
  448. }
  449. else
  450. {
  451. channel.RemoveBan(mode.Parameter);
  452. }
  453. break;
  454. case ChannelMode.k:
  455. if (mode.Set)
  456. {
  457. channel.AddMode(mode.Mode);
  458. channel.Key = mode.Parameter;
  459. }
  460. else
  461. {
  462. channel.RemoveMode(mode.Mode);
  463. channel.Key = string.Empty;
  464. }
  465. break;
  466. default:
  467. if (mode.Set)
  468. {
  469. channel.AddMode(mode.Mode);
  470. }
  471. else
  472. {
  473. channel.RemoveMode(mode.Mode);
  474. }
  475. break;
  476. }
  477. }
  478. Command.SendWho(channel.Name);
  479. }
  480. ChannelRWLock.ExitWriteLock();
  481. }
  482. /// <summary>
  483. /// Update a nick's mode.
  484. /// </summary>
  485. /// <param name="sender"></param>
  486. /// <param name="e"></param>
  487. private void HandleUserModeChange(object sender, UserModeChangeInfo e)
  488. {
  489. ChannelRWLock.EnterWriteLock();
  490. for (int i = 0; i < Channels.Count; i++)
  491. {
  492. Nick changedNick = Channels[i].GetNick(e.Nick.Nickname);
  493. if (changedNick != null)
  494. {
  495. foreach (UserModeInfo mode in e.Modes)
  496. {
  497. if (mode.Set)
  498. {
  499. changedNick.AddMode(mode.Mode);
  500. }
  501. else
  502. {
  503. changedNick.RemoveMode(mode.Mode);
  504. }
  505. }
  506. }
  507. }
  508. ChannelRWLock.ExitWriteLock();
  509. }
  510. /// <summary>
  511. /// Update a nick to use their new nickname.
  512. /// </summary>
  513. /// <param name="sender"></param>
  514. /// <param name="e"></param>
  515. private void HandleNickChange(object sender, NickChangeInfo e)
  516. {
  517. ChannelRWLock.EnterWriteLock();
  518. for (int i = 0; i < Channels.Count; i++)
  519. {
  520. Nick newNick = Channels[i].GetNick(e.OldNick.Nickname);
  521. if (newNick != null)
  522. {
  523. if (e.OldNick.Nickname == Nickname)
  524. {
  525. Nickname = e.NewNick.Nickname;
  526. }
  527. newNick.Nickname = e.NewNick.Nickname;
  528. }
  529. }
  530. ChannelRWLock.ExitWriteLock();
  531. }
  532. /// <summary>
  533. /// Add a nick to a channel on join.
  534. /// </summary>
  535. /// <param name="sender"></param>
  536. /// <param name="e"></param>
  537. private void HandleJoin(object sender, JoinChannelInfo e)
  538. {
  539. ChannelRWLock.EnterWriteLock();
  540. Channel channel = Channels.Find(chan => chan.Name == e.Channel);
  541. if (channel != null)
  542. {
  543. channel.AddNick(e.Nick);
  544. }
  545. else
  546. {
  547. Channel newChannel = new Channel();
  548. newChannel.Name = e.Channel;
  549. newChannel.Nicks.Add(e.Nick);
  550. Channels.Add(newChannel);
  551. Command.SendWho(newChannel.Name);
  552. }
  553. ChannelRWLock.ExitWriteLock();
  554. }
  555. /// <summary>
  556. /// Remove a nick from a channel on part.
  557. /// </summary>
  558. /// <param name="sender"></param>
  559. /// <param name="e"></param>
  560. private void HandlePart(object sender, PartChannelInfo e)
  561. {
  562. ChannelRWLock.EnterWriteLock();
  563. Channel channel = Channels.Find(chan => chan.Name == e.Channel);
  564. if (channel != null)
  565. {
  566. if (e.Nick.Nickname == Nickname)
  567. {
  568. Channels.Remove(channel);
  569. }
  570. else
  571. {
  572. channel.RemoveNick(e.Nick.Nickname);
  573. }
  574. }
  575. ChannelRWLock.ExitWriteLock();
  576. }
  577. /// <summary>
  578. /// Remove a nick from a channel on kick.
  579. /// </summary>
  580. /// <param name="sender"></param>
  581. /// <param name="e"></param>
  582. private void HandleKick(object sender, KickInfo e)
  583. {
  584. ChannelRWLock.EnterWriteLock();
  585. Channel channel = Channels.Find(chan => chan.Name == e.Channel);
  586. if (channel != null)
  587. {
  588. if (e.KickedNick.Nickname == Nickname)
  589. {
  590. Channels.Remove(channel);
  591. }
  592. else
  593. {
  594. channel.RemoveNick(e.KickedNick.Nickname);
  595. }
  596. }
  597. ChannelRWLock.ExitWriteLock();
  598. }
  599. /// <summary>
  600. /// Remove a nick from all channels on quit.
  601. /// </summary>
  602. /// <param name="sender"></param>
  603. /// <param name="e"></param>
  604. private void HandleQuit(object sender, QuitInfo e)
  605. {
  606. ChannelRWLock.EnterWriteLock();
  607. for (int i = 0; i < Channels.Count; i++)
  608. {
  609. Channels[i].RemoveNick(e.Nick.Nickname);
  610. }
  611. ChannelRWLock.ExitWriteLock();
  612. }
  613. }
  614. }