using System; using System.Collections.Generic; using System.Linq; namespace IrcTokens { /// /// Tools to represent, parse, and format IRC lines /// public class Line { public Dictionary Tags { get; set; } public string Source { get; set; } public string Command { get; set; } public List Params { get; set; } private Hostmask _hostmask; private string _rawLine; public override string ToString() => $"Line(tags={string.Join(";", Tags.Select(kvp => $"{kvp.Key}={kvp.Value}"))}, params={string.Join(",", Params)})"; public Hostmask Hostmask => _hostmask ??= new Hostmask(Source); public Line() { } /// /// Build new object parsed from a string. Analogous to irctokens.tokenise() /// /// irc line to parse public Line(string line) { _rawLine = line; string[] split; if (line.StartsWith('@')) { Tags = new Dictionary(); split = line.Split(" "); var messageTags = split[0]; line = string.Join(" ", split.Skip(1)); foreach (var part in messageTags.Substring(1).Split(';')) { if (part.Contains('=')) { split = part.Split('='); Tags[split[0]] = Protocol.UnescapeTag(split[1]); } else { Tags[part] = string.Empty; } } } string trailing; if (line.Contains(" :")) { split = line.Split(" :"); line = split[0]; trailing = string.Join(" :", split.Skip(1)); } else { trailing = null; } Params = line.Contains(' ') ? line.Split(' ').Where(p => !string.IsNullOrWhiteSpace(p)).ToList() : new List {line}; if (Params[0].StartsWith(':')) { Source = Params[0].Substring(1); Params.RemoveAt(0); } if (Params.Count > 0) { Command = Params[0].ToUpper(); Params.RemoveAt(0); } if (trailing != null) { Params.Add(trailing); } } /// /// Format a as a standards-compliant IRC line /// /// formatted irc line public string Format() { var outs = new List(); if (Tags != null && Tags.Any()) { var tags = Tags.Keys .Select(key => string.IsNullOrWhiteSpace(Tags[key]) ? key : $"{key}={Protocol.EscapeTag(Tags[key])}") .ToList(); outs.Add($"@{string.Join(";", tags)}"); } if (Source != null) { outs.Add($":{Source}"); } outs.Add(Command); if (Params != null && Params.Any()) { var last = Params[^1]; Params.RemoveAt(Params.Count - 1); foreach (var p in Params) { if (p.Contains(' ')) throw new ArgumentException("non-last parameters cannot have spaces"); if (p.StartsWith(':')) throw new ArgumentException("non-last parameters cannot start with colon"); } outs.AddRange(Params); if (last == null || string.IsNullOrWhiteSpace(last) || last.Contains(' ') || last.StartsWith(':')) last = $":{last}"; outs.Add(last); } return string.Join(" ", outs); } } }