The easy to use and full featured Irc Bot everyone is talking about!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Messages.cs 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Text.RegularExpressions;
  6. using System.Threading.Tasks;
  7. namespace Combot.IRCServices.Messaging
  8. {
  9. public class Messages
  10. {
  11. public event EventHandler<string> RawMessageEvent;
  12. public event EventHandler<IReply> ServerReplyEvent;
  13. public event EventHandler<ErrorMessage> ErrorMessageEvent;
  14. public event EventHandler<ChannelMessage> ChannelMessageReceivedEvent;
  15. public event EventHandler<PrivateMessage> PrivateMessageReceivedEvent;
  16. public event EventHandler<ServerNotice> ServerNoticeReceivedEvent;
  17. public event EventHandler<ChannelNotice> ChannelNoticeReceivedEvent;
  18. public event EventHandler<PrivateNotice> PrivateNoticeReceivedEvent;
  19. public event EventHandler<CTCPMessage> CTCPMessageRecievedEvent;
  20. public event EventHandler<CTCPMessage> CTCPNoticeRecievedEvent;
  21. public event EventHandler<TopicChangeInfo> TopicChangeEvent;
  22. public event EventHandler<ChannelModeChangeInfo> ChannelModeChangeEvent;
  23. public event EventHandler<UserModeChangeInfo> UserModeChangeEvent;
  24. public event EventHandler<NickChangeInfo> NickChangeEvent;
  25. public event EventHandler<InviteChannelInfo> InviteChannelEvent;
  26. public event EventHandler<JoinChannelInfo> JoinChannelEvent;
  27. public event EventHandler<PartChannelInfo> PartChannelEvent;
  28. public event EventHandler<KickInfo> KickEvent;
  29. public event EventHandler<QuitInfo> QuitEvent;
  30. public event EventHandler<PingInfo> PingEvent;
  31. public event EventHandler<PongInfo> PongEvent;
  32. private IRC _IRC;
  33. internal Messages(IRC irc)
  34. {
  35. _IRC = irc;
  36. }
  37. /// <summary>
  38. /// Parses the raw messages coming from the server and triggers an event based on the type of message.
  39. /// </summary>
  40. /// <param name="tcpMessage">The raw string read from the TCP stream.</param>
  41. internal async void ParseTCPMessage(string tcpMessage)
  42. {
  43. DateTime messageTime = DateTime.Now;
  44. Regex messageRegex = new Regex(@"^:(?<Sender>[^\s]+)\s(?<Type>[^\s]+)\s(?<Recipient>[^\s]+)\s?:?(?<Args>.*)", RegexOptions.None);
  45. Regex senderRegex = new Regex(@"^(?<Nick>[^\s]+)!(?<Realname>[^\s]+)@(?<Host>[^\s]+)", RegexOptions.None);
  46. Regex pingRegex = new Regex(@"^PING :(?<Message>.+)", RegexOptions.None);
  47. Regex pongRegex = new Regex(@"^PONG :(?<Message>.+)", RegexOptions.None);
  48. Regex errorRegex = new Regex(@"^ERROR :(?<Message>.+)", RegexOptions.None);
  49. Regex CTCPRegex = new Regex(@"^\u0001(?<Command>[^\s]+)\s?(?<Args>.*)\u0001", RegexOptions.None);
  50. string[] messages = tcpMessage.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
  51. foreach (string message in messages)
  52. {
  53. if (messageRegex.IsMatch(message))
  54. {
  55. Match match = messageRegex.Match(message);
  56. string type = match.Groups["Type"].Value;
  57. string sender = match.Groups["Sender"].Value;
  58. string recipient = match.Groups["Recipient"].Value;
  59. string args = match.Groups["Args"].Value;
  60. Match senderMatch = senderRegex.Match(sender);
  61. string senderNick = sender;
  62. string senderRealname = sender;
  63. string senderHost = sender;
  64. if (senderMatch.Success)
  65. {
  66. senderNick = senderMatch.Groups["Nick"].Value;
  67. senderRealname = senderMatch.Groups["Realname"].Value;
  68. senderHost = senderMatch.Groups["Host"].Value;
  69. }
  70. int replyCode;
  71. if (int.TryParse(type, out replyCode))
  72. {
  73. // The message was a reply to a command sent
  74. if (Enum.IsDefined(typeof(IRCReplyCode), replyCode))
  75. {
  76. await Task.Run(() =>
  77. {
  78. if (ServerReplyEvent != null)
  79. {
  80. ServerReplyEvent(this,
  81. new ServerReplyMessage()
  82. {
  83. TimeStamp = messageTime,
  84. ReplyCode = (IRCReplyCode) replyCode,
  85. Message = args
  86. });
  87. }
  88. });
  89. }
  90. else if (Enum.IsDefined(typeof(IRCErrorCode), replyCode))
  91. {
  92. await Task.Run(() =>
  93. {
  94. if (ServerReplyEvent != null)
  95. {
  96. ServerReplyEvent(this,
  97. new ServerErrorMessage()
  98. {
  99. TimeStamp = messageTime,
  100. ErrorCode = (IRCErrorCode) replyCode,
  101. Message = args
  102. });
  103. }
  104. });
  105. }
  106. }
  107. else
  108. {
  109. switch (type)
  110. {
  111. // The message was a private message to a channel or nick
  112. case "PRIVMSG":
  113. if (CTCPRegex.IsMatch(args))
  114. {
  115. Match ctcpMatch = CTCPRegex.Match(args);
  116. CTCPMessage ctcpMessage = new CTCPMessage();
  117. ctcpMessage.Sender = new Nick()
  118. {
  119. Nickname = senderNick,
  120. Realname = senderRealname,
  121. Host = senderHost
  122. };
  123. ctcpMessage.Command = ctcpMatch.Groups["Command"].Value;
  124. ctcpMessage.Arguments = ctcpMatch.Groups["Args"].Value;
  125. await Task.Run(() =>
  126. {
  127. if (CTCPMessageRecievedEvent != null)
  128. {
  129. CTCPMessageRecievedEvent(this, ctcpMessage);
  130. }
  131. });
  132. }
  133. else
  134. {
  135. if (recipient.StartsWith("&") || recipient.StartsWith("#"))
  136. {
  137. ChannelMessage msg = new ChannelMessage();
  138. msg.Channel = recipient;
  139. msg.Sender = new Nick()
  140. {
  141. Nickname = senderNick,
  142. Realname = senderRealname,
  143. Host = senderHost
  144. };
  145. msg.Message = args;
  146. await Task.Run(() =>
  147. {
  148. if (ChannelMessageReceivedEvent != null)
  149. {
  150. ChannelMessageReceivedEvent(this, msg);
  151. }
  152. });
  153. }
  154. else
  155. {
  156. PrivateMessage msg = new PrivateMessage();
  157. msg.Sender = new Nick()
  158. {
  159. Nickname = senderNick,
  160. Realname = senderRealname,
  161. Host = senderHost
  162. };
  163. msg.Message = args;
  164. await Task.Run(() =>
  165. {
  166. if (PrivateMessageReceivedEvent != null)
  167. {
  168. PrivateMessageReceivedEvent(this, msg);
  169. }
  170. });
  171. }
  172. }
  173. break;
  174. // The message was a notice to a channel or nick
  175. case "NOTICE":
  176. if (CTCPRegex.IsMatch(args))
  177. {
  178. Match ctcpMatch = CTCPRegex.Match(args);
  179. CTCPMessage ctcpMessage = new CTCPMessage();
  180. ctcpMessage.Sender = new Nick()
  181. {
  182. Nickname = senderNick,
  183. Realname = senderRealname,
  184. Host = senderHost
  185. };
  186. ctcpMessage.Command = ctcpMatch.Groups["Command"].Value;
  187. ctcpMessage.Arguments = ctcpMatch.Groups["Args"].Value;
  188. await Task.Run(() =>
  189. {
  190. if (CTCPNoticeRecievedEvent != null)
  191. {
  192. CTCPNoticeRecievedEvent(this, ctcpMessage);
  193. }
  194. });
  195. }
  196. if (recipient.StartsWith("&") || recipient.StartsWith("#"))
  197. {
  198. ChannelNotice msg = new ChannelNotice();
  199. msg.Channel = recipient;
  200. msg.Sender = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  201. msg.Message = args;
  202. await Task.Run(() =>
  203. {
  204. if (ChannelNoticeReceivedEvent != null)
  205. {
  206. ChannelNoticeReceivedEvent(this, msg);
  207. }
  208. });
  209. }
  210. else
  211. {
  212. PrivateNotice msg = new PrivateNotice();
  213. msg.Sender = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  214. msg.Message = args;
  215. await Task.Run(() =>
  216. {
  217. if (PrivateNoticeReceivedEvent != null)
  218. {
  219. PrivateNoticeReceivedEvent(this, msg);
  220. }
  221. });
  222. }
  223. break;
  224. // The message was a mode change message for a channel or nick
  225. case "MODE":
  226. if (recipient.StartsWith("&") || recipient.StartsWith("#"))
  227. {
  228. ChannelModeChangeInfo modeMsg = new ChannelModeChangeInfo();
  229. modeMsg.Modes = new List<ChannelModeInfo>();
  230. modeMsg.Channel = recipient;
  231. modeMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  232. string[] modeArgs = args.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
  233. char[] modeInfo = modeArgs[0].TrimStart(':').ToCharArray();
  234. bool set = true;
  235. int argIndex = 1;
  236. foreach (char mode in modeInfo)
  237. {
  238. if (mode.Equals('-'))
  239. {
  240. set = false;
  241. }
  242. else if (mode.Equals('+'))
  243. {
  244. set = true;
  245. }
  246. else
  247. {
  248. ChannelModeInfo newMode = new ChannelModeInfo();
  249. newMode.Set = set;
  250. newMode.Mode = (ChannelMode)Enum.Parse(typeof(ChannelMode), mode.ToString());
  251. if (modeArgs.GetUpperBound(0) >= argIndex)
  252. {
  253. switch (newMode.Mode)
  254. {
  255. case ChannelMode.k:
  256. case ChannelMode.l:
  257. case ChannelMode.v:
  258. case ChannelMode.h:
  259. case ChannelMode.o:
  260. case ChannelMode.a:
  261. case ChannelMode.q:
  262. case ChannelMode.b:
  263. case ChannelMode.e:
  264. case ChannelMode.I:
  265. newMode.Parameter = modeArgs[argIndex];
  266. argIndex++;
  267. break;
  268. default:
  269. newMode.Parameter = string.Empty;
  270. break;
  271. }
  272. }
  273. else
  274. {
  275. newMode.Parameter = string.Empty;
  276. }
  277. modeMsg.Modes.Add(newMode);
  278. }
  279. }
  280. await Task.Run(() =>
  281. {
  282. if (ChannelModeChangeEvent != null)
  283. {
  284. ChannelModeChangeEvent(this, modeMsg);
  285. }
  286. });
  287. }
  288. else
  289. {
  290. UserModeChangeInfo modeMsg = new UserModeChangeInfo();
  291. modeMsg.Modes = new List<UserModeInfo>();
  292. modeMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  293. string[] modeArgs = args.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
  294. char[] modeInfo = modeArgs[0].TrimStart(':').ToCharArray();
  295. bool set = true;
  296. foreach (char mode in modeInfo)
  297. {
  298. if (mode.Equals('-'))
  299. {
  300. set = false;
  301. }
  302. else if (mode.Equals('+'))
  303. {
  304. set = true;
  305. }
  306. else
  307. {
  308. UserModeInfo newMode = new UserModeInfo();
  309. newMode.Set = set;
  310. newMode.Mode = (UserMode)Enum.Parse(typeof(UserMode), mode.ToString());
  311. modeMsg.Modes.Add(newMode);
  312. }
  313. }
  314. await Task.Run(() =>
  315. {
  316. if (UserModeChangeEvent != null)
  317. {
  318. UserModeChangeEvent(this, modeMsg);
  319. }
  320. });
  321. }
  322. break;
  323. // The message was a topic change for a channel
  324. case "TOPIC":
  325. TopicChangeInfo topicMsg = new TopicChangeInfo();
  326. topicMsg.Channel = recipient;
  327. topicMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  328. topicMsg.Topic = args;
  329. await Task.Run(() =>
  330. {
  331. if (TopicChangeEvent != null)
  332. {
  333. TopicChangeEvent(this, topicMsg);
  334. }
  335. });
  336. break;
  337. // The message was a nick change
  338. case "NICK":
  339. NickChangeInfo nickMsg = new NickChangeInfo();
  340. nickMsg.OldNick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  341. nickMsg.NewNick = new Nick() { Nickname = recipient.Remove(0, 1) };
  342. await Task.Run(() =>
  343. {
  344. if (NickChangeEvent != null)
  345. {
  346. NickChangeEvent(this, nickMsg);
  347. }
  348. });
  349. break;
  350. // The message was an invite to a channel
  351. case "INVITE":
  352. InviteChannelInfo inviteMsg = new InviteChannelInfo();
  353. inviteMsg.Requester = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  354. inviteMsg.Recipient = new Nick() { Nickname = recipient };
  355. inviteMsg.Channel = args;
  356. await Task.Run(() =>
  357. {
  358. if (InviteChannelEvent != null)
  359. {
  360. InviteChannelEvent(this, inviteMsg);
  361. }
  362. });
  363. break;
  364. // The message was a nick joining a channel
  365. case "JOIN":
  366. JoinChannelInfo joinMsg = new JoinChannelInfo();
  367. joinMsg.Channel = recipient.TrimStart(':');
  368. joinMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  369. await Task.Run(() =>
  370. {
  371. if (JoinChannelEvent != null)
  372. {
  373. JoinChannelEvent(this, joinMsg);
  374. }
  375. });
  376. break;
  377. // The message was a nick parting a channel
  378. case "PART":
  379. PartChannelInfo partMsg = new PartChannelInfo();
  380. partMsg.Channel = recipient;
  381. partMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  382. await Task.Run(() =>
  383. {
  384. if (PartChannelEvent != null)
  385. {
  386. PartChannelEvent(this, partMsg);
  387. }
  388. });
  389. break;
  390. // The message was a nick being kicked from a channel
  391. case "KICK":
  392. KickInfo kickMsg = new KickInfo();
  393. kickMsg.Channel = recipient;
  394. kickMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  395. string[] argSplit = args.Split(new char[] { ' ' }, StringSplitOptions.None);
  396. kickMsg.KickedNick = new Nick() { Nickname = argSplit[0], Realname = argSplit[0], Host = argSplit[0] };
  397. List<string> reasonArgs = argSplit.ToList<string>();
  398. reasonArgs.RemoveAt(0);
  399. kickMsg.Reason = string.Join(" ", reasonArgs.ToArray()).Remove(0, 1);
  400. await Task.Run(() =>
  401. {
  402. if (KickEvent != null)
  403. {
  404. KickEvent(this, kickMsg);
  405. }
  406. });
  407. break;
  408. // The message was a nick quiting the irc network
  409. case "QUIT":
  410. QuitInfo quitMsg = new QuitInfo();
  411. quitMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  412. quitMsg.Message = recipient.Remove(0, 1);
  413. await Task.Run(() =>
  414. {
  415. if (QuitEvent != null)
  416. {
  417. QuitEvent(this, quitMsg);
  418. }
  419. });
  420. break;
  421. default:
  422. break;
  423. }
  424. }
  425. }
  426. else if (pingRegex.IsMatch(message)) // The message was a PING
  427. {
  428. Match match = pingRegex.Match(message);
  429. PingInfo ping = new PingInfo();
  430. ping.Message = match.Groups["Message"].Value;
  431. await Task.Run(() =>
  432. {
  433. if (PingEvent != null)
  434. {
  435. PingEvent(this, ping);
  436. }
  437. });
  438. }
  439. else if (pongRegex.IsMatch(message)) // The message was a PONG
  440. {
  441. Match match = pongRegex.Match(message);
  442. PongInfo pong = new PongInfo();
  443. pong.Message = match.Groups["Message"].Value;
  444. await Task.Run(() =>
  445. {
  446. if (PongEvent != null)
  447. {
  448. PongEvent(this, pong);
  449. }
  450. });
  451. }
  452. else if (errorRegex.IsMatch(message)) // The message was a server error
  453. {
  454. Match match = errorRegex.Match(message);
  455. ErrorMessage error = new ErrorMessage();
  456. error.Message = match.Groups["Message"].Value;
  457. if (ErrorMessageEvent != null)
  458. {
  459. ErrorMessageEvent(this, error);
  460. }
  461. }
  462. await Task.Run(() =>
  463. {
  464. if (RawMessageEvent != null)
  465. {
  466. RawMessageEvent(this, message);
  467. }
  468. });
  469. }
  470. }
  471. public bool GetReply(List<IRCReplyCode> ReplyCodes, List<IRCErrorCode> ErrorCodes)
  472. {
  473. GetReply reply = new GetReply();
  474. reply.Replies = ReplyCodes;
  475. reply.Errors = ErrorCodes;
  476. ServerReplyEvent += (sender, e) => HandleReply(sender, e, reply);
  477. reply.Ready.Wait(TimeSpan.FromMilliseconds(5000));
  478. reply.Reattach = false;
  479. return reply.Result;
  480. }
  481. private void HandleReply(object sender, IReply message, GetReply reply)
  482. {
  483. bool replyFound = false;
  484. if (message.GetType() == typeof(ServerReplyMessage))
  485. {
  486. ServerReplyMessage msg = (ServerReplyMessage)message;
  487. replyFound = reply.Replies.Contains(msg.ReplyCode);
  488. }
  489. else
  490. {
  491. ServerErrorMessage msg = (ServerErrorMessage)message;
  492. replyFound = reply.Errors.Contains(msg.ErrorCode);
  493. }
  494. if (replyFound)
  495. {
  496. reply.Result = replyFound;
  497. reply.Ready.Set();
  498. }
  499. else
  500. {
  501. if (reply.Reattach)
  502. {
  503. ServerReplyEvent += (obj, e) => HandleReply(obj, e, reply);
  504. }
  505. }
  506. }
  507. }
  508. }