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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  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<TopicChangeInfo> TopicChangeEvent;
  20. public event EventHandler<ChannelModeChangeInfo> ChannelModeChangeEvent;
  21. public event EventHandler<UserModeChangeInfo> UserModeChangeEvent;
  22. public event EventHandler<NickChangeInfo> NickChangeEvent;
  23. public event EventHandler<InviteChannelInfo> InviteChannelEvent;
  24. public event EventHandler<JoinChannelInfo> JoinChannelEvent;
  25. public event EventHandler<PartChannelInfo> PartChannelEvent;
  26. public event EventHandler<KickInfo> KickEvent;
  27. public event EventHandler<QuitInfo> QuitEvent;
  28. public event EventHandler<PingInfo> PingEvent;
  29. public event EventHandler<PongInfo> PongEvent;
  30. private IRC _IRC;
  31. internal Messages(IRC irc)
  32. {
  33. _IRC = irc;
  34. }
  35. /// <summary>
  36. /// Parses the raw messages coming from the server and triggers an event based on the type of message.
  37. /// </summary>
  38. /// <param name="tcpMessage">The raw string read from the TCP stream.</param>
  39. internal void ParseTCPMessage(string tcpMessage)
  40. {
  41. DateTime messageTime = DateTime.Now;
  42. Regex messageRegex = new Regex(@"^:(?<Sender>[^\s]+)\s(?<Type>[^\s]+)\s(?<Recipient>[^\s]+)\s?:?(?<Args>.*)", RegexOptions.None);
  43. Regex senderRegex = new Regex(@"^(?<Nick>[^\s]+)!(?<Realname>[^\s]+)@(?<Host>[^\s]+)", RegexOptions.None);
  44. Regex pingRegex = new Regex(@"^PING :(?<Message>.+)", RegexOptions.None);
  45. Regex pongRegex = new Regex(@"^PONG :(?<Message>.+)", RegexOptions.None);
  46. Regex errorRegex = new Regex(@"^ERROR :(?<Message>.+)", RegexOptions.None);
  47. string[] messages = tcpMessage.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
  48. foreach (string message in messages)
  49. {
  50. if (messageRegex.IsMatch(message))
  51. {
  52. Match match = messageRegex.Match(message);
  53. string type = match.Groups["Type"].Value;
  54. string sender = match.Groups["Sender"].Value;
  55. string recipient = match.Groups["Recipient"].Value;
  56. string args = match.Groups["Args"].Value;
  57. Match senderMatch = senderRegex.Match(sender);
  58. string senderNick = sender;
  59. string senderRealname = sender;
  60. string senderHost = sender;
  61. if (senderMatch.Success)
  62. {
  63. senderNick = senderMatch.Groups["Nick"].Value;
  64. senderRealname = senderMatch.Groups["Realname"].Value;
  65. senderHost = senderMatch.Groups["Host"].Value;
  66. }
  67. int replyCode;
  68. if (int.TryParse(type, out replyCode))
  69. {
  70. // The message was a reply to a command sent
  71. if (Enum.IsDefined(typeof(IRCReplyCode), replyCode))
  72. {
  73. if (ServerReplyEvent != null)
  74. {
  75. ServerReplyEvent(this, new ServerReplyMessage() { TimeStamp = messageTime, ReplyCode = (IRCReplyCode)replyCode, Message = args });
  76. }
  77. }
  78. else if (Enum.IsDefined(typeof(IRCErrorCode), replyCode))
  79. {
  80. if (ServerReplyEvent != null)
  81. {
  82. ServerReplyEvent(this, new ServerErrorMessage() { TimeStamp = messageTime, ErrorCode = (IRCErrorCode)replyCode, Message = args });
  83. }
  84. }
  85. }
  86. else
  87. {
  88. switch (type)
  89. {
  90. // The message was a private message to a channel or nick
  91. case "PRIVMSG":
  92. if (recipient.StartsWith("&") || recipient.StartsWith("#"))
  93. {
  94. ChannelMessage msg = new ChannelMessage();
  95. msg.Channel = recipient;
  96. msg.Sender = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  97. msg.Message = args;
  98. if (ChannelMessageReceivedEvent != null)
  99. {
  100. ChannelMessageReceivedEvent(this, msg);
  101. }
  102. }
  103. else
  104. {
  105. PrivateMessage msg = new PrivateMessage();
  106. msg.Sender = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  107. msg.Message = args;
  108. if (PrivateMessageReceivedEvent != null)
  109. {
  110. PrivateMessageReceivedEvent(this, msg);
  111. }
  112. }
  113. break;
  114. // The message was a notice to a channel or nick
  115. case "NOTICE":
  116. if (recipient.StartsWith("&") || recipient.StartsWith("#"))
  117. {
  118. ChannelNotice msg = new ChannelNotice();
  119. msg.Channel = recipient;
  120. msg.Sender = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  121. msg.Message = args;
  122. if (ChannelNoticeReceivedEvent != null)
  123. {
  124. ChannelNoticeReceivedEvent(this, msg);
  125. }
  126. }
  127. else
  128. {
  129. PrivateNotice msg = new PrivateNotice();
  130. msg.Sender = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  131. msg.Message = args;
  132. if (PrivateNoticeReceivedEvent != null)
  133. {
  134. PrivateNoticeReceivedEvent(this, msg);
  135. }
  136. }
  137. break;
  138. // The message was a mode change message for a channel or nick
  139. case "MODE":
  140. if (recipient.StartsWith("&") || recipient.StartsWith("#"))
  141. {
  142. ChannelModeChangeInfo modeMsg = new ChannelModeChangeInfo();
  143. modeMsg.Modes = new List<ChannelModeInfo>();
  144. modeMsg.Channel = recipient;
  145. modeMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  146. string[] modeArgs = args.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
  147. char[] modeInfo = modeArgs[0].TrimStart(':').ToCharArray();
  148. bool set = true;
  149. int argIndex = 1;
  150. foreach (char mode in modeInfo)
  151. {
  152. if (mode.Equals('-'))
  153. {
  154. set = false;
  155. }
  156. else if (mode.Equals('+'))
  157. {
  158. set = true;
  159. }
  160. else
  161. {
  162. ChannelModeInfo newMode = new ChannelModeInfo();
  163. newMode.Set = set;
  164. newMode.Mode = (ChannelMode)Enum.Parse(typeof(ChannelMode), mode.ToString());
  165. if (modeArgs.GetUpperBound(0) >= argIndex)
  166. {
  167. switch (newMode.Mode)
  168. {
  169. case ChannelMode.k:
  170. case ChannelMode.l:
  171. case ChannelMode.v:
  172. case ChannelMode.h:
  173. case ChannelMode.o:
  174. case ChannelMode.a:
  175. case ChannelMode.q:
  176. case ChannelMode.b:
  177. case ChannelMode.e:
  178. case ChannelMode.I:
  179. newMode.Parameter = modeArgs[argIndex];
  180. argIndex++;
  181. break;
  182. default:
  183. newMode.Parameter = string.Empty;
  184. break;
  185. }
  186. }
  187. else
  188. {
  189. newMode.Parameter = string.Empty;
  190. }
  191. modeMsg.Modes.Add(newMode);
  192. }
  193. }
  194. if (ChannelModeChangeEvent != null)
  195. {
  196. ChannelModeChangeEvent(this, modeMsg);
  197. }
  198. }
  199. else
  200. {
  201. UserModeChangeInfo modeMsg = new UserModeChangeInfo();
  202. modeMsg.Modes = new List<UserModeInfo>();
  203. modeMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  204. string[] modeArgs = args.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
  205. char[] modeInfo = modeArgs[0].TrimStart(':').ToCharArray();
  206. bool set = true;
  207. foreach (char mode in modeInfo)
  208. {
  209. if (mode.Equals('-'))
  210. {
  211. set = false;
  212. }
  213. else if (mode.Equals('+'))
  214. {
  215. set = true;
  216. }
  217. else
  218. {
  219. UserModeInfo newMode = new UserModeInfo();
  220. newMode.Set = set;
  221. newMode.Mode = (UserMode)Enum.Parse(typeof(UserMode), mode.ToString());
  222. modeMsg.Modes.Add(newMode);
  223. }
  224. }
  225. if (UserModeChangeEvent != null)
  226. {
  227. UserModeChangeEvent(this, modeMsg);
  228. }
  229. }
  230. break;
  231. // The message was a topic change for a channel
  232. case "TOPIC":
  233. TopicChangeInfo topicMsg = new TopicChangeInfo();
  234. topicMsg.Channel = recipient;
  235. topicMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  236. topicMsg.Topic = args;
  237. if (TopicChangeEvent != null)
  238. {
  239. TopicChangeEvent(this, topicMsg);
  240. }
  241. break;
  242. // The message was a nick change
  243. case "NICK":
  244. NickChangeInfo nickMsg = new NickChangeInfo();
  245. nickMsg.OldNick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  246. nickMsg.NewNick = new Nick() { Nickname = recipient.Remove(0, 1) };
  247. if (NickChangeEvent != null)
  248. {
  249. NickChangeEvent(this, nickMsg);
  250. }
  251. break;
  252. // The message was an invite to a channel
  253. case "INVITE":
  254. InviteChannelInfo inviteMsg = new InviteChannelInfo();
  255. inviteMsg.Requester = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  256. inviteMsg.Recipient = new Nick() { Nickname = recipient };
  257. inviteMsg.Channel = args;
  258. if (InviteChannelEvent != null)
  259. {
  260. InviteChannelEvent(this, inviteMsg);
  261. }
  262. break;
  263. // The message was a nick joining a channel
  264. case "JOIN":
  265. JoinChannelInfo joinMsg = new JoinChannelInfo();
  266. joinMsg.Channel = recipient.TrimStart(':');
  267. joinMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  268. if (JoinChannelEvent != null)
  269. {
  270. JoinChannelEvent(this, joinMsg);
  271. }
  272. break;
  273. // The message was a nick parting a channel
  274. case "PART":
  275. PartChannelInfo partMsg = new PartChannelInfo();
  276. partMsg.Channel = recipient;
  277. partMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  278. if (PartChannelEvent != null)
  279. {
  280. PartChannelEvent(this, partMsg);
  281. }
  282. break;
  283. // The message was a nick being kicked from a channel
  284. case "KICK":
  285. KickInfo kickMsg = new KickInfo();
  286. kickMsg.Channel = recipient;
  287. kickMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  288. string[] argSplit = args.Split(new char[] { ' ' }, StringSplitOptions.None);
  289. kickMsg.KickedNick = new Nick() { Nickname = argSplit[0], Realname = argSplit[0], Host = argSplit[0] };
  290. List<string> reasonArgs = argSplit.ToList<string>();
  291. reasonArgs.RemoveAt(0);
  292. kickMsg.Reason = string.Join(" ", reasonArgs.ToArray()).Remove(0, 1);
  293. if (KickEvent != null)
  294. {
  295. KickEvent(this, kickMsg);
  296. }
  297. break;
  298. // The message was a nick quiting the irc network
  299. case "QUIT":
  300. QuitInfo quitMsg = new QuitInfo();
  301. quitMsg.Nick = new Nick() { Nickname = senderNick, Realname = senderRealname, Host = senderHost };
  302. quitMsg.Message = recipient.Remove(0, 1);
  303. if (QuitEvent != null)
  304. {
  305. QuitEvent(this, quitMsg);
  306. }
  307. break;
  308. default:
  309. break;
  310. }
  311. }
  312. }
  313. else if (pingRegex.IsMatch(message)) // The message was a PING
  314. {
  315. Match match = pingRegex.Match(message);
  316. PingInfo ping = new PingInfo();
  317. ping.Message = match.Groups["Message"].Value;
  318. if (PingEvent != null)
  319. {
  320. PingEvent(this, ping);
  321. }
  322. }
  323. else if (pongRegex.IsMatch(message)) // The message was a PONG
  324. {
  325. Match match = pongRegex.Match(message);
  326. PongInfo pong = new PongInfo();
  327. pong.Message = match.Groups["Message"].Value;
  328. if (PongEvent != null)
  329. {
  330. PongEvent(this, pong);
  331. }
  332. }
  333. else if (errorRegex.IsMatch(message)) // The message was a server error
  334. {
  335. Match match = errorRegex.Match(message);
  336. ErrorMessage error = new ErrorMessage();
  337. error.Message = match.Groups["Message"].Value;
  338. if (ErrorMessageEvent != null)
  339. {
  340. ErrorMessageEvent(this, error);
  341. }
  342. }
  343. if (RawMessageEvent != null)
  344. {
  345. RawMessageEvent(this, message);
  346. }
  347. }
  348. }
  349. internal bool GetReply(List<IRCReplyCode> ReplyCodes, List<IRCErrorCode> ErrorCodes)
  350. {
  351. GetReply reply = new GetReply();
  352. reply.Replies = ReplyCodes;
  353. reply.Errors = ErrorCodes;
  354. ServerReplyEvent += (sender, e) => HandleReply(sender, e, reply);
  355. reply.Ready.Wait(TimeSpan.FromMilliseconds(5000));
  356. reply.Reattach = false;
  357. return reply.Result;
  358. }
  359. private void HandleReply(object sender, IReply message, GetReply reply)
  360. {
  361. bool replyFound = false;
  362. if (message.GetType() == typeof(ServerReplyMessage))
  363. {
  364. ServerReplyMessage msg = (ServerReplyMessage)message;
  365. replyFound = reply.Replies.Contains(msg.ReplyCode);
  366. }
  367. else
  368. {
  369. ServerErrorMessage msg = (ServerErrorMessage)message;
  370. replyFound = reply.Errors.Contains(msg.ErrorCode);
  371. }
  372. if (replyFound)
  373. {
  374. reply.Result = replyFound;
  375. reply.Ready.Set();
  376. }
  377. else
  378. {
  379. if (reply.Reattach)
  380. {
  381. ServerReplyEvent += (obj, e) => HandleReply(obj, e, reply);
  382. }
  383. }
  384. }
  385. }
  386. }