aboutsummaryrefslogtreecommitdiff
path: root/plugins/degesch/slack.lua
blob: dcddb3c3da2fb66d95c1b6cc8032c88201a68dc7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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

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)