public IRCMessage(byte[] msg) { Time = DateTime.UtcNow; Raw = Encoding.UTF8.GetString(msg); RawTrimmed = Encoding.UTF8.GetString(msg).Trim(); Parsed = false; Prefix = string.Empty; Server = string.Empty; Nick = string.Empty; User = string.Empty; Host = string.Empty; Command = string.Empty; RawParameters = string.Empty; Parameters = new string[0]; #region Actual Parsing if (RawTrimmed.Length > 0) { var subIdx = 0; // If we find a prefix, we shift this value to the end of the prefix // Prefix Section #region Prefix Parsing (Optional in Message Format) if (RawTrimmed.StartsWith(':') || RawTrimmed.Contains('!') || RawTrimmed.Contains('@')) // Contains prefix if the 1st char is : { // (Sometimes twitch replies without the ':' in which case we search for other giveaways that it is a prefix e.g. '!' / '@') // [':' <prefix> <SPACE> ] // < prefix > ::= < servername > | < nick > ['!' < user > ]['@' < host > ] subIdx = RawTrimmed.IndexOf(' '); // searches for the <SPACE> if (RawTrimmed.StartsWith(':')) { Prefix = RawTrimmed.Substring(1, subIdx - 1); // Omit ':' and ' ' } else { Prefix = RawTrimmed.Substring(0, subIdx); // In the event we detect a prefix without the : } var excIdx = Prefix.IndexOf('!'); // Finds '!' var atIdx = Prefix.IndexOf('@'); // Finds '@', use this info to determine if its a nickname or servername (?, unsure about this) if (excIdx == -1 && atIdx == -1) { Server = Prefix; Nick = string.Empty; } else { if (atIdx != -1) { Host = Prefix.Substring(atIdx); } else { Host = string.Empty; } if (excIdx != -1) { if (atIdx != -1) // We need to calculate the length of the User section based of the position of the '@' { User = Prefix.Substring(excIdx, (Prefix.Length - excIdx) - Host.Length); } else { User = Prefix.Substring(excIdx); } } Server = string.Empty; Nick = Prefix.Substring(0, Prefix.Length - User.Length - Host.Length); } } #endregion // Commands Section #region Command Parsing { // < command > ::= < letter > { < letter > } | < number > < number > < number > if (subIdx != 0) { Command = RawTrimmed.Substring(subIdx + 1); } else { Command = RawTrimmed.Substring(subIdx); } var cmdIndex = Command.IndexOf(' '); if (cmdIndex == -1) { return; // Invalid Message, cant be parsed. } // TODO: Log it to see why Command = Command.Substring(0, cmdIndex); // <command> <params> (<params> starts with <SPACE>) IsNumericReply = false; char startChar = (char)Command.ToCharArray().GetValue(0); if (startChar >= 0x30 && startChar <= 0x39) // Between 0 - 9 { // Number Code var num = 0; if (int.TryParse(Command, out num)) { IRCReply = (IRCReply)num; IsNumericReply = true; } } } #endregion // RawParameters Section #region RawParameters Parsing { // <params> ::= <SPACE> [ ':' <trailing> | <middle> <params> ] if (subIdx == 0) { // No prefix detected, subIdx = RawTrimmed.IndexOf(' '); RawParameters = RawTrimmed.Substring(subIdx + 1); } else { // We detected a prefix, so there is already a SPACE, find the next one subIdx = RawTrimmed.IndexOf(' ', subIdx + 1); RawParameters = RawTrimmed.Substring(subIdx + 1); } var paramsList = new List <string>(); var paramsChar = RawParameters.ToCharArray(); for (int i = 0; i < paramsChar.Length; i++) { var ch = paramsChar[i]; if (ch == ':') //[':' < trailing > | ... ] { paramsList.Add(new string(paramsChar.Skip(i).Take(paramsChar.Length - i).ToArray())); break; // Last parameter } else // [ ... | < middle > <params> ] { var nextSpace = RawParameters.IndexOf(' ', i); if (nextSpace == -1) { paramsList.Add(new string(paramsChar.ToArray())); break; // There are no more parameters twitch lied to us (where was the ':' :( ) } else { paramsList.Add(new string(paramsChar.Skip(i).Take(nextSpace - i).ToArray())); i = nextSpace; } } } Parameters = paramsList.ToArray(); } #endregion Parsed = true; } else { // Empty message Prefix = string.Empty; Server = string.Empty; Nick = string.Empty; User = string.Empty; Host = string.Empty; Command = string.Empty; RawParameters = string.Empty; Parameters = new string[0]; } #endregion }