The easy to use and full featured Irc Bot everyone is talking about!
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

Messages.cs 26KB

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