▾ modified src/frq/irc.jolt +26 -3 Viewed
@@ -73,14 +73,37 @@ 73 73 :command (str/upper-case (or (first parts) "")) 74 74 :params (cond-> (vec (rest parts)) trailing (conj trailing))})) 75 75 76 + (defn unescape-tag 77 + "An IRCv3 tag value with its escapes undone. 78 + 79 + `\\:` is a semicolon, `\\s` a space, and `\\\\`, `\\r` and `\\n` themselves — 80 + the escaping exists because `;` separates tags and a space ends them. It 81 + matters for any value that can contain either: a reaction tally is 82 + `emoji:nick;emoji:nick` on the wire and arrives with every one of those 83 + semicolons written `\\:`, so a reader that skips this step sees one tally 84 + where there were three, and counts to match." 85 + [v] 86 + (when v 87 + (loop [in (seq v) out []] 88 + (if-let [c (first in)] 89 + (if (and (= \\ c) (second in)) 90 + (recur (drop 2 in) 91 + (conj out (case (second in) 92 + \: \; 93 + \s \space 94 + \r \return 95 + \n \newline 96 + (second in)))) 97 + (recur (rest in) (conj out c))) 98 + (apply str out))))) 99 + 76 100 (defn tag-value 77 - "One IRCv3 tag's value, or nil. Tag values escape `;` and space; the ids and 78 - timestamps read here contain neither, so they are taken as they come." 101 + "One IRCv3 tag's value, unescaped, or nil." 79 102 [tags key] 80 103 (when tags 81 104 (some (fn [pair] 82 105 (let [[k v] (str/split pair #"=" 2)] 83 - (when (= k key) v))) 106 + (when (= k key) (unescape-tag v)))) 84 107 (str/split tags #";")))) 85 108 86 109 (defn nick-of
@@ -73,14 +73,37 @@ 73 :command (str/upper-case (or (first parts) "")) 73 :command (str/upper-case (or (first parts) "")) 74 :params (cond-> (vec (rest parts)) trailing (conj trailing))})) 74 :params (cond-> (vec (rest parts)) trailing (conj trailing))})) 75 75 76 + (defn unescape-tag 77 + "An IRCv3 tag value with its escapes undone. 78 + 79 + `\\:` is a semicolon, `\\s` a space, and `\\\\`, `\\r` and `\\n` themselves — 80 + the escaping exists because `;` separates tags and a space ends them. It 81 + matters for any value that can contain either: a reaction tally is 82 + `emoji:nick;emoji:nick` on the wire and arrives with every one of those 83 + semicolons written `\\:`, so a reader that skips this step sees one tally 84 + where there were three, and counts to match." 85 + [v] 86 + (when v 87 + (loop [in (seq v) out []] 88 + (if-let [c (first in)] 89 + (if (and (= \\ c) (second in)) 90 + (recur (drop 2 in) 91 + (conj out (case (second in) 92 + \: \; 93 + \s \space 94 + \r \return 95 + \n \newline 96 + (second in)))) 97 + (recur (rest in) (conj out c))) 98 + (apply str out))))) 99 + 76 (defn tag-value 100 (defn tag-value 77 - "One IRCv3 tag's value, or nil. Tag values escape `;` and space; the ids and 101 + "One IRCv3 tag's value, unescaped, or nil." 78 - timestamps read here contain neither, so they are taken as they come." 79 [tags key] 102 [tags key] 80 (when tags 103 (when tags 81 (some (fn [pair] 104 (some (fn [pair] 82 (let [[k v] (str/split pair #"=" 2)] 105 (let [[k v] (str/split pair #"=" 2)] 83 - (when (= k key) v))) 106 + (when (= k key) (unescape-tag v)))) 84 (str/split tags #";")))) 107 (str/split tags #";")))) 85 108 86 (defn nick-of 109 (defn nick-of