The easy to use and full featured Irc Bot everyone is talking about!
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

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