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

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