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 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  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. List<string> argList = modeArgs.ToList();
  234. argList.RemoveAt(0);
  235. modeMsg.Modes.AddRange(_IRC.ParseChannelModeString(modeArgs[0].TrimStart(':'), string.Join(" ", argList)));
  236. await Task.Run(() =>
  237. {
  238. if (ChannelModeChangeEvent != null)
  239. {
  240. ChannelModeChangeEvent(this, modeMsg);
  241. }
  242. });
  243. }
  244. else
  245. {
  246. UserModeChangeInfo modeMsg = new UserModeChangeInfo();
  247. modeMsg.Modes = new List<UserModeInfo>();
  248. modeMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  249. string[] modeArgs = args.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
  250. char[] modeInfo = modeArgs[0].TrimStart(':').ToCharArray();
  251. bool set = true;
  252. foreach (char mode in modeInfo)
  253. {
  254. if (mode.Equals('-'))
  255. {
  256. set = false;
  257. }
  258. else if (mode.Equals('+'))
  259. {
  260. set = true;
  261. }
  262. else
  263. {
  264. UserModeInfo newMode = new UserModeInfo();
  265. newMode.Set = set;
  266. newMode.Mode = (UserMode)Enum.Parse(typeof(UserMode), mode.ToString());
  267. modeMsg.Modes.Add(newMode);
  268. }
  269. }
  270. await Task.Run(() =>
  271. {
  272. if (UserModeChangeEvent != null)
  273. {
  274. UserModeChangeEvent(this, modeMsg);
  275. }
  276. });
  277. }
  278. break;
  279. // The message was a topic change for a channel
  280. case "TOPIC":
  281. TopicChangeInfo topicMsg = new TopicChangeInfo();
  282. topicMsg.Channel = recipient;
  283. topicMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  284. topicMsg.Topic = args;
  285. await Task.Run(() =>
  286. {
  287. if (TopicChangeEvent != null)
  288. {
  289. TopicChangeEvent(this, topicMsg);
  290. }
  291. });
  292. break;
  293. // The message was a nick change
  294. case "NICK":
  295. NickChangeInfo nickMsg = new NickChangeInfo();
  296. nickMsg.OldNick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  297. nickMsg.NewNick = new Nick() { Nickname = recipient.Remove(0, 1) };
  298. await Task.Run(() =>
  299. {
  300. if (NickChangeEvent != null)
  301. {
  302. NickChangeEvent(this, nickMsg);
  303. }
  304. });
  305. break;
  306. // The message was an invite to a channel
  307. case "INVITE":
  308. InviteChannelInfo inviteMsg = new InviteChannelInfo();
  309. inviteMsg.Requester = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  310. inviteMsg.Recipient = new Nick() { Nickname = recipient };
  311. inviteMsg.Channel = args;
  312. await Task.Run(() =>
  313. {
  314. if (InviteChannelEvent != null)
  315. {
  316. InviteChannelEvent(this, inviteMsg);
  317. }
  318. });
  319. break;
  320. // The message was a nick joining a channel
  321. case "JOIN":
  322. JoinChannelInfo joinMsg = new JoinChannelInfo();
  323. joinMsg.Channel = recipient.TrimStart(':');
  324. joinMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  325. await Task.Run(() =>
  326. {
  327. if (JoinChannelEvent != null)
  328. {
  329. JoinChannelEvent(this, joinMsg);
  330. }
  331. });
  332. break;
  333. // The message was a nick parting a channel
  334. case "PART":
  335. PartChannelInfo partMsg = new PartChannelInfo();
  336. partMsg.Channel = recipient;
  337. partMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  338. await Task.Run(() =>
  339. {
  340. if (PartChannelEvent != null)
  341. {
  342. PartChannelEvent(this, partMsg);
  343. }
  344. });
  345. break;
  346. // The message was a nick being kicked from a channel
  347. case "KICK":
  348. KickInfo kickMsg = new KickInfo();
  349. kickMsg.Channel = recipient;
  350. kickMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  351. string[] argSplit = args.Split(new char[] { ' ' }, StringSplitOptions.None);
  352. kickMsg.KickedNick = new Nick() { Nickname = argSplit[0], Realname = argSplit[0], Host = argSplit[0] };
  353. List<string> reasonArgs = argSplit.ToList<string>();
  354. reasonArgs.RemoveAt(0);
  355. kickMsg.Reason = string.Join(" ", reasonArgs.ToArray()).Remove(0, 1);
  356. await Task.Run(() =>
  357. {
  358. if (KickEvent != null)
  359. {
  360. KickEvent(this, kickMsg);
  361. }
  362. });
  363. break;
  364. // The message was a nick quiting the irc network
  365. case "QUIT":
  366. QuitInfo quitMsg = new QuitInfo();
  367. quitMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  368. quitMsg.Message = recipient.Remove(0, 1);
  369. await Task.Run(() =>
  370. {
  371. if (QuitEvent != null)
  372. {
  373. QuitEvent(this, quitMsg);
  374. }
  375. });
  376. break;
  377. default:
  378. break;
  379. }
  380. }
  381. }
  382. else if (pingRegex.IsMatch(message)) // The message was a PING
  383. {
  384. Match match = pingRegex.Match(message);
  385. PingInfo ping = new PingInfo();
  386. ping.Message = match.Groups["Message"].Value;
  387. await Task.Run(() =>
  388. {
  389. if (PingEvent != null)
  390. {
  391. PingEvent(this, ping);
  392. }
  393. });
  394. }
  395. else if (pongRegex.IsMatch(message)) // The message was a PONG
  396. {
  397. Match match = pongRegex.Match(message);
  398. PongInfo pong = new PongInfo();
  399. pong.Message = match.Groups["Message"].Value;
  400. await Task.Run(() =>
  401. {
  402. if (PongEvent != null)
  403. {
  404. PongEvent(this, pong);
  405. }
  406. });
  407. }
  408. else if (errorRegex.IsMatch(message)) // The message was a server error
  409. {
  410. Match match = errorRegex.Match(message);
  411. ErrorMessage error = new ErrorMessage();
  412. error.Message = match.Groups["Message"].Value;
  413. if (ErrorMessageEvent != null)
  414. {
  415. ErrorMessageEvent(this, error);
  416. }
  417. }
  418. await Task.Run(() =>
  419. {
  420. if (RawMessageEvent != null)
  421. {
  422. RawMessageEvent(this, message);
  423. }
  424. });
  425. }
  426. }
  427. public ServerReplyMessage GetServerReply(IRCReplyCode replyCode, string match)
  428. {
  429. GetReply reply = new GetReply();
  430. reply.Reply = replyCode;
  431. reply.Match = match;
  432. ServerReplyEvent += (sender, e) => HandleReply(sender, e, reply);
  433. reply.Ready.Wait(TimeSpan.FromMilliseconds(5000));
  434. ServerReplyEvent -= (obj, e) => HandleReply(obj, e, reply);
  435. return reply.Result;
  436. }
  437. public ServerErrorMessage GetServerError(IRCErrorCode errorCode, string match)
  438. {
  439. GetError error = new GetError();
  440. error.Error = errorCode;
  441. error.Match = match;
  442. ServerReplyEvent += (sender, e) => HandleError(sender, e, error);
  443. error.Ready.Wait(TimeSpan.FromMilliseconds(5000));
  444. ServerReplyEvent -= (sender, e) => HandleError(sender, e, error);
  445. return error.Result;
  446. }
  447. private void HandleReply(object sender, IReply message, GetReply reply)
  448. {
  449. bool replyFound = false;
  450. Regex replyRegex = new Regex(reply.Match);
  451. if (message.GetType() == typeof(ServerReplyMessage))
  452. {
  453. ServerReplyMessage msg = (ServerReplyMessage)message;
  454. replyFound = reply.Reply.Equals(msg.ReplyCode);
  455. if (replyFound && replyRegex.IsMatch(msg.Message))
  456. {
  457. reply.Result = msg;
  458. reply.Ready.Set();
  459. }
  460. }
  461. }
  462. private void HandleError(object sender, IReply message, GetError error)
  463. {
  464. bool errorFound = false;
  465. Regex errorRegex = new Regex(error.Match);
  466. if (message.GetType() == typeof(ServerErrorMessage))
  467. {
  468. ServerErrorMessage msg = (ServerErrorMessage)message;
  469. errorFound = error.Error.Equals(msg.ErrorCode);
  470. if (errorFound && errorRegex.IsMatch(msg.Message))
  471. {
  472. error.Result = msg;
  473. error.Ready.Set();
  474. }
  475. }
  476. }
  477. }
  478. }