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> CTCPMessageReceivedEvent;
  20. public event EventHandler<CTCPMessage> CTCPNoticeReceivedEvent;
  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 = null;
  63. string senderHost = null;
  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.Location = recipient;
  118. ctcpMessage.Sender = new Nick()
  119. {
  120. Nickname = senderNick,
  121. Realname = senderRealname,
  122. Host = senderHost
  123. };
  124. ctcpMessage.Command = ctcpMatch.Groups["Command"].Value;
  125. ctcpMessage.Arguments = ctcpMatch.Groups["Args"].Value;
  126. await Task.Run(() =>
  127. {
  128. if (CTCPMessageReceivedEvent != null)
  129. {
  130. CTCPMessageReceivedEvent(this, ctcpMessage);
  131. }
  132. });
  133. }
  134. else
  135. {
  136. if (Channel.IsChannel(recipient))
  137. {
  138. ChannelMessage msg = new ChannelMessage();
  139. msg.Channel = recipient;
  140. msg.Sender = new Nick()
  141. {
  142. Nickname = senderNick,
  143. Realname = senderRealname,
  144. Host = senderHost
  145. };
  146. msg.Message = args;
  147. await Task.Run(() =>
  148. {
  149. if (ChannelMessageReceivedEvent != null)
  150. {
  151. ChannelMessageReceivedEvent(this, msg);
  152. }
  153. });
  154. }
  155. else
  156. {
  157. PrivateMessage msg = new PrivateMessage();
  158. msg.Sender = new Nick()
  159. {
  160. Nickname = senderNick,
  161. Realname = senderRealname,
  162. Host = senderHost
  163. };
  164. msg.Message = args;
  165. await Task.Run(() =>
  166. {
  167. if (PrivateMessageReceivedEvent != null)
  168. {
  169. PrivateMessageReceivedEvent(this, msg);
  170. }
  171. });
  172. }
  173. }
  174. break;
  175. // The message was a notice to a channel or nick
  176. case "NOTICE":
  177. if (CTCPRegex.IsMatch(args))
  178. {
  179. Match ctcpMatch = CTCPRegex.Match(args);
  180. CTCPMessage ctcpMessage = new CTCPMessage();
  181. ctcpMessage.Sender = new Nick()
  182. {
  183. Nickname = senderNick,
  184. Realname = senderRealname,
  185. Host = senderHost
  186. };
  187. ctcpMessage.Command = ctcpMatch.Groups["Command"].Value;
  188. ctcpMessage.Arguments = ctcpMatch.Groups["Args"].Value;
  189. await Task.Run(() =>
  190. {
  191. if (CTCPNoticeReceivedEvent != null)
  192. {
  193. CTCPNoticeReceivedEvent(this, ctcpMessage);
  194. }
  195. });
  196. }
  197. if (Channel.IsChannel(recipient))
  198. {
  199. ChannelNotice msg = new ChannelNotice();
  200. msg.Channel = recipient;
  201. msg.Sender = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  202. msg.Message = args;
  203. await Task.Run(() =>
  204. {
  205. if (ChannelNoticeReceivedEvent != null)
  206. {
  207. ChannelNoticeReceivedEvent(this, msg);
  208. }
  209. });
  210. }
  211. else
  212. {
  213. PrivateNotice msg = new PrivateNotice();
  214. msg.Sender = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  215. msg.Message = args;
  216. await Task.Run(() =>
  217. {
  218. if (PrivateNoticeReceivedEvent != null)
  219. {
  220. PrivateNoticeReceivedEvent(this, msg);
  221. }
  222. });
  223. }
  224. break;
  225. // The message was a mode change message for a channel or nick
  226. case "MODE":
  227. if (Channel.IsChannel(recipient))
  228. {
  229. ChannelModeChangeInfo modeMsg = new ChannelModeChangeInfo();
  230. modeMsg.Modes = new List<ChannelModeInfo>();
  231. modeMsg.Channel = recipient;
  232. modeMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  233. string[] modeArgs = args.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
  234. List<string> argList = modeArgs.ToList();
  235. argList.RemoveAt(0);
  236. modeMsg.Modes.AddRange(_IRC.ParseChannelModeString(modeArgs[0].TrimStart(':'), string.Join(" ", argList)));
  237. await Task.Run(() =>
  238. {
  239. if (ChannelModeChangeEvent != null)
  240. {
  241. ChannelModeChangeEvent(this, modeMsg);
  242. }
  243. });
  244. }
  245. else
  246. {
  247. UserModeChangeInfo modeMsg = new UserModeChangeInfo();
  248. modeMsg.Modes = new List<UserModeInfo>();
  249. modeMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  250. string[] modeArgs = args.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
  251. char[] modeInfo = modeArgs[0].TrimStart(':').ToCharArray();
  252. bool set = true;
  253. foreach (char mode in modeInfo)
  254. {
  255. if (mode.Equals('-'))
  256. {
  257. set = false;
  258. }
  259. else if (mode.Equals('+'))
  260. {
  261. set = true;
  262. }
  263. else if (mode.ToString() != string.Empty)
  264. {
  265. UserModeInfo newMode = new UserModeInfo();
  266. newMode.Set = set;
  267. newMode.Mode = (UserMode)Enum.Parse(typeof(UserMode), mode.ToString());
  268. modeMsg.Modes.Add(newMode);
  269. }
  270. }
  271. await Task.Run(() =>
  272. {
  273. if (UserModeChangeEvent != null)
  274. {
  275. UserModeChangeEvent(this, modeMsg);
  276. }
  277. });
  278. }
  279. break;
  280. // The message was a topic change for a channel
  281. case "TOPIC":
  282. TopicChangeInfo topicMsg = new TopicChangeInfo();
  283. topicMsg.Channel = recipient;
  284. topicMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  285. topicMsg.Topic = args;
  286. await Task.Run(() =>
  287. {
  288. if (TopicChangeEvent != null)
  289. {
  290. TopicChangeEvent(this, topicMsg);
  291. }
  292. });
  293. break;
  294. // The message was a nick change
  295. case "NICK":
  296. NickChangeInfo nickMsg = new NickChangeInfo();
  297. nickMsg.OldNick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  298. nickMsg.NewNick = new Nick() { Nickname = recipient.TrimStart(':'), Realname = senderRealname, Host = senderHost};
  299. await Task.Run(() =>
  300. {
  301. if (NickChangeEvent != null)
  302. {
  303. NickChangeEvent(this, nickMsg);
  304. }
  305. });
  306. break;
  307. // The message was an invite to a channel
  308. case "INVITE":
  309. InviteChannelInfo inviteMsg = new InviteChannelInfo();
  310. inviteMsg.Requester = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  311. inviteMsg.Recipient = new Nick() { Nickname = recipient };
  312. inviteMsg.Channel = args;
  313. await Task.Run(() =>
  314. {
  315. if (InviteChannelEvent != null)
  316. {
  317. InviteChannelEvent(this, inviteMsg);
  318. }
  319. });
  320. break;
  321. // The message was a nick joining a channel
  322. case "JOIN":
  323. JoinChannelInfo joinMsg = new JoinChannelInfo();
  324. joinMsg.Channel = recipient.TrimStart(':');
  325. joinMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  326. await Task.Run(() =>
  327. {
  328. if (JoinChannelEvent != null)
  329. {
  330. JoinChannelEvent(this, joinMsg);
  331. }
  332. });
  333. break;
  334. // The message was a nick parting a channel
  335. case "PART":
  336. PartChannelInfo partMsg = new PartChannelInfo();
  337. partMsg.Channel = recipient;
  338. partMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  339. await Task.Run(() =>
  340. {
  341. if (PartChannelEvent != null)
  342. {
  343. PartChannelEvent(this, partMsg);
  344. }
  345. });
  346. break;
  347. // The message was a nick being kicked from a channel
  348. case "KICK":
  349. KickInfo kickMsg = new KickInfo();
  350. kickMsg.Channel = recipient;
  351. kickMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  352. string[] argSplit = args.Split(new char[] { ' ' }, StringSplitOptions.None);
  353. kickMsg.KickedNick = new Nick() { Nickname = argSplit[0] };
  354. List<string> reasonArgs = argSplit.ToList<string>();
  355. reasonArgs.RemoveAt(0);
  356. kickMsg.Reason = string.Join(" ", reasonArgs.ToArray()).Remove(0, 1);
  357. await Task.Run(() =>
  358. {
  359. if (KickEvent != null)
  360. {
  361. KickEvent(this, kickMsg);
  362. }
  363. });
  364. break;
  365. // The message was a nick quiting the irc network
  366. case "QUIT":
  367. QuitInfo quitMsg = new QuitInfo();
  368. quitMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  369. quitMsg.Message = string.Join(" ", recipient.Remove(0, 1), args);
  370. await Task.Run(() =>
  371. {
  372. if (QuitEvent != null)
  373. {
  374. QuitEvent(this, quitMsg);
  375. }
  376. });
  377. break;
  378. default:
  379. break;
  380. }
  381. }
  382. }
  383. else if (pingRegex.IsMatch(message)) // The message was a PING
  384. {
  385. Match match = pingRegex.Match(message);
  386. PingInfo ping = new PingInfo();
  387. ping.Message = match.Groups["Message"].Value;
  388. await Task.Run(() =>
  389. {
  390. if (PingEvent != null)
  391. {
  392. PingEvent(this, ping);
  393. }
  394. });
  395. }
  396. else if (pongRegex.IsMatch(message)) // The message was a PONG
  397. {
  398. Match match = pongRegex.Match(message);
  399. PongInfo pong = new PongInfo();
  400. pong.Message = match.Groups["Message"].Value;
  401. await Task.Run(() =>
  402. {
  403. if (PongEvent != null)
  404. {
  405. PongEvent(this, pong);
  406. }
  407. });
  408. }
  409. else if (errorRegex.IsMatch(message)) // The message was a server error
  410. {
  411. Match match = errorRegex.Match(message);
  412. ErrorMessage error = new ErrorMessage();
  413. error.Message = match.Groups["Message"].Value;
  414. if (ErrorMessageEvent != null)
  415. {
  416. ErrorMessageEvent(this, error);
  417. }
  418. }
  419. string rawMessage = message;
  420. await Task.Run(() =>
  421. {
  422. if (RawMessageEvent != null)
  423. {
  424. RawMessageEvent(this, rawMessage);
  425. }
  426. });
  427. }
  428. }
  429. public ServerReplyMessage GetServerReply(IRCReplyCode replyCode, string match)
  430. {
  431. GetReply reply = new GetReply();
  432. reply.Reply = replyCode;
  433. reply.Match = match;
  434. ServerReplyEvent += (sender, e) => HandleReply(sender, e, reply);
  435. reply.Ready.Wait(TimeSpan.FromMilliseconds(5000));
  436. return reply.Result;
  437. }
  438. public ServerErrorMessage GetServerError(IRCErrorCode errorCode, string match)
  439. {
  440. GetError error = new GetError();
  441. error.Error = errorCode;
  442. error.Match = match;
  443. ServerReplyEvent += (sender, e) => HandleError(sender, e, error);
  444. error.Ready.Wait(TimeSpan.FromMilliseconds(5000));
  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. }