local hexchat = hexchat hexchat.register("rot13", "3.0.1", "rot13") local rot13 = {} for i = string.byte('a'), string.byte('m') do local a, b = string.char(i), string.char(i+13) local A, B = a:upper(), b:upper() rot13[a] = b rot13[b] = a rot13[A] = B rot13[B] = A end local function do_rot13(arg, arg_eol) if not arg[3] then hexchat.print("Usage: /" .. arg[1] .. " ") return hexchat.EAT_ALL end hexchat.command("say " .. arg[2] .. "\x01CW1 " .. arg_eol[3]:gsub("[A-Za-z]", rot13) .. "\x01") return hexchat.EAT_ALL end hexchat.hook_command("cw-rot13", do_rot13) local function startswith(str1, str2) return str1:sub(1, #str2) == str2 end local function parsecw(msg) local hidden = {} msg = msg:gsub("()(\1([^\1]*)\1)()", function(start, ctcp, contents, finish) if startswith(contents, "CW1 ") then local cw = contents:sub(5) hidden[#hidden + 1] = cw:gsub("[A-Za-z]", rot13) return " [Hidden Text " .. #hidden .. "] " end return ctcp end) return msg, hidden end local invert_notice = false local last_notice = false local tunpack = unpack or table.unpack local skip = false local function mkparse(event, pos) return function(word, attributes) if skip then return end if event:find("Notice$") then if invert_notice then word[pos] = '\1'.. word[pos] if last_notice then word[pos] = word[pos] .. '\1' end end end invert_notice = false local old_msg = word[pos] local hidden word[pos], hidden = parsecw(word[pos]) if word[pos] ~= old_msg then skip = true hexchat.emit_print_attrs(attributes, event, tunpack(word)) for i, v in ipairs(hidden) do hexchat.print("\00326*\tHidden Text " .. i .. "\3 > \8"..v.."\8 < (Copy and paste to expand)") end skip = false return hexchat.EAT_ALL end end end local function hookparse(event, pos) return hexchat.hook_print_attrs(event, mkparse(event, pos)) end do (function(f,...)return f(f,...) end)(function(f, a, b, ...) if a then hookparse(a, b) return f(f, ...) end end, "Channel Message", 2, "Channel Msg Hilight", 2, "Channel Notice", 3, "Private Message", 2, "Private Message to Dialog", 2, "Notice", 2, "Your Message", 2, "Notice Send", 2, "Message Send", 2, nil) end -- from here on: 3.0.1 bugfix -- TODO maybe move these to a separate plugin. (or integrate them in hexchat) local function tooct(n) return string.format("%o", n) end local function on_privmsg(word, word_eol, attrs) -- fix up some PRIVMSGs, pass them on to our handlers above local i_at = 0 for i,v in ipairs(word) do if v == "PRIVMSG" then i_at = i + 2 break end end if i_at == 0 then return end if word_eol[i_at]:sub(1, 2):find(":[-+]") and tooct(hexchat.props["flags"]):find("[4-7].$") then -- bleh word_eol[i_at] = ":" .. word_eol[i_at]:sub(3) end if word_eol[i_at]:sub(1, 6) == ":\1CW1 " then if not word_eol[i_at]:find("\1", 7, true) == #word_eol[i_at] then return hexchat.EAT_NONE -- let hexchat handle it normally end local target = word[i_at - 1] local source if word[1]:sub(1,1) ~= "@" then source = word[1] else source = word[2] end source = source:match("^:([^!]+)!") -- this seems easy, right? local context = hexchat.find_context(hexchat.get_info("server"), target) if context then local old_ctx = hexchat.get_context() local serv_id = hexchat.props["id"] -- sanity check if hexchat.set_context(context) and hexchat.props["id"] == serv_id then if hexchat.props["type"] == 2 then local prefix = "" for user in hexchat.iterate("users") do if user.nick == source then prefix = user.prefix end end -- TODO fix highlights hexchat.emit_print_attrs(attrs, "Channel Message", source, word_eol[i_at]:sub(2), prefix) return hexchat.EAT_HEXCHAT elseif hexchat.props["type"] == 3 then hexchat.emit_print_attrs(attrs, "Private Message to Dialog", source, word_eol[i_at]:sub(2)) return hexchat.EAT_HEXCHAT end end -- assume it's a query, because usually you only get channel messages if you're in the channel if not hexchat.set_context(old_ctx) then return end -- how did we get this far without segfaulting? end if hexchat.prefs["gui_autoopen_dialog"] then hexchat.command("query -nofocus " .. target) local context = hexchat.find_context(hexchat.get_info("server"), target) if context then local old_ctx = hexchat.get_context() local serv_id = hexchat.props["id"] -- sanity check if hexchat.set_context(context) and hexchat.props["id"] == serv_id then -- meh, go hardmode on this (this shouldn't happen unless find_context returns the server context for some reason) if hexchat.props["type"] ~= 3 then local found = false for chan in hexchat.iterate("channels") do if chan.id == serv_id and chan.type == 3 and chan.channel == source then found = true hexchat.set_context(chan.context) end end -- still not found? ragequit! if not found then return hexchat.EAT_HEXCHAT end end hexchat.emit_print_attrs(attrs, "Private Message to Dialog", source, word_eol[i_at]:sub(2)) end end return hexchat.EAT_HEXCHAT end hexchat.emit_print_attrs(attrs, "Private Message", source, word_eol[i_at]:sub(2)) return hexchat.EAT_HEXCHAT end end local function on_notice(word, word_eol, attrs) -- this one is much simpler local i_at = 0 for i,v in ipairs(word) do if v == "NOTICE" then i_at = i + 2 break end end if i_at == 0 then return end if word_eol[i_at]:sub(1, 2):find(":[-+]") and tooct(hexchat.props["flags"]):find("[4-7].$") then -- bleh word_eol[i_at] = ":" .. word_eol[i_at]:sub(3) end if word_eol[i_at]:sub(1, 6) == ":\1CW1 " then -- workaround invert_notice = true last_notice = word_eol[i_at]:sub(-1,-1) == "\1" -- then just let hexchat handle it end end hexchat.hook_server_attrs("PRIVMSG", on_privmsg) hexchat.hook_server_attrs("NOTICE", on_notice) hexchat.print("rot13 loaded") -- this was in 3.0.0 too