diff options
Diffstat (limited to 'plugins/xC/slack.lua')
-rw-r--r-- | plugins/xC/slack.lua | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/plugins/xC/slack.lua b/plugins/xC/slack.lua new file mode 100644 index 0000000..c1a08de --- /dev/null +++ b/plugins/xC/slack.lua @@ -0,0 +1,147 @@ +-- +-- slack.lua: try to fix up UX when using the Slack IRC gateway +-- +-- Copyright (c) 2017, Přemysl Eric Janouch <p@janouch.name> +-- +-- Permission to use, copy, modify, and/or distribute this software for any +-- purpose with or without fee is hereby granted. +-- +-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +-- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +-- SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +-- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +-- OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +-- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +-- + +local servers = {} +local read_servers = function (v) + servers = {} + for name in v:lower ():gmatch "[^,]+" do + servers[name] = true + end +end + +-- This is a reverse list of Slack's automatic emoji, noseless forms +local unemojify, emoji, emoji_default = false, {}, { + heart = "<3", + broken_heart = "</3", + sunglasses = "8)", + anguished = "D:", + cry = ":'(", + monkey_face = ":o)", + kiss = ":*", + smiley = "=)", + smile = ":D", + wink = ";)", + laughing = ":>", + neutral_face = ":|", + open_mouth = ":o", + angry = ">:(", + slightly_smiling_face = ":)", + disappointed = ":(", + confused = ":/", + stuck_out_tongue = ":p", + stuck_out_tongue_winking_eye = ";p", +} +local load_emoji = function (extra) + emoji = {} + for k, v in pairs (emoji_default) do emoji[k] = v end + for k, v in extra:gmatch "([^,]+) ([^,]+)" do emoji[k] = v end +end + +xC.setup_config { + servers = { + type = "string_array", + default = "\"\"", + comment = "list of server names that are Slack IRC gateways", + on_change = read_servers + }, + unemojify = { + type = "boolean", + default = "true", + comment = "convert emoji to normal ASCII emoticons", + on_change = function (v) unemojify = v end + }, + extra_emoji = { + type = "string_array", + default = "\"grinning :)),joy :'),innocent o:),persevere >_<\"", + comment = "overrides or extra emoji for unemojify", + on_change = function (v) load_emoji (v) end + } +} + +-- We can handle external messages about what we've supposedly sent just fine, +-- so let's get rid of that "[username] some message sent from the web UI" crap +xC.hook_irc (function (hook, server, line) + local msg, us = xC.parse (line), server.user + if not servers[server.name] or msg.command ~= "PRIVMSG" or not us + or msg.params[1]:lower () ~= us.nickname:lower () then return line end + + -- Taking a shortcut to avoid lengthy message reassembly + local quoted_nick = us.nickname:gsub ("[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0") + local text = line:match ("^.- PRIVMSG .- :%[" .. quoted_nick .. "%] (.*)$") + if not text then return line end + return ":" .. us.nickname .. "!" .. server.irc_user_host .. " PRIVMSG " + .. msg.prefix:match "^[^!@]*" .. " :" .. text +end) + +-- Unfuck emoji and :nick!nick@irc.tinyspeck.com MODE #channel +v nick : active +xC.hook_irc (function (hook, server, line) + if not servers[server.name] then return line end + if unemojify then + local start, text = line:match ("^(.- PRIVMSG .- :)(.*)$") + if start then return start .. text:gsub (":([a-z_]+):", function (name) + if emoji[name] then return emoji[name] end + return ":" .. name .. ":" + end) end + end + return line:gsub ("^(:%S+ MODE .+) : .*", "%1") +end) + +-- The gateway simply ignores the NAMES command altogether +xC.hook_input (function (hook, buffer, input) + if not buffer.channel or not servers[buffer.server.name] + or not input:match "^/names%s*$" then return input end + + local users = buffer.channel.users + table.sort (users, function (a, b) + if a.prefixes > b.prefixes then return true end + if a.prefixes < b.prefixes then return false end + return a.user.nickname < b.user.nickname + end) + + local names = "Users on " .. buffer.channel.name .. ":" + for i, chan_user in ipairs (users) do + names = names .. " " .. chan_user.prefixes .. chan_user.user.nickname + end + buffer:log (names) +end) + +xC.hook_completion (function (hook, data, word) + local chan = xC.current_buffer.channel + local server = xC.current_buffer.server + if not chan or not servers[server.name] then return end + + -- In /commands there is typically no desire at all to add the at sign + if data.location == 1 and data.words[1]:match "^/" then return end + + -- Handle both when the at sign is already there and when it is not + local needle = word:gsub ("^@", ""):lower () + + local t = {} + local try = function (name) + if data.location == 0 then name = name .. ":" end + if name:sub (1, #needle):lower () == needle then + table.insert (t, "@" .. name) + end + end + for _, chan_user in ipairs (chan.users) do + try (chan_user.user.nickname) + end + for _, special in ipairs { "channel", "here" } do + try (special) + end + return t +end) |