Přemysl Janouch
a0e9ede3e3
I've come to the conclusion that copyright mostly just stands in the way of software development. In my jurisdiction I cannot give up my own copyright and 0BSD seems to be the closest thing to public domain. The updated mail address, also used in my author/committer lines, is shorter and looks nicer. People rarely interact anyway.
148 lines
5.1 KiB
Lua
148 lines
5.1 KiB
Lua
--
|
|
-- slack.lua: try to fix up UX when using the Slack IRC gateway
|
|
--
|
|
-- Copyright (c) 2017, Přemysl 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
|
|
|
|
degesch.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
|
|
degesch.hook_irc (function (hook, server, line)
|
|
local msg, us = degesch.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
|
|
degesch.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
|
|
degesch.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)
|
|
|
|
degesch.hook_completion (function (hook, data, word)
|
|
local chan = degesch.current_buffer.channel
|
|
local server = degesch.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)
|