Come up with sillier names for the binaries
I'm not entirely sure, but it looks like some people might not like jokes about the Holocaust. On a more serious note, the project has become more serious over the 7 or so years of its existence.
This commit is contained in:
48
plugins/xC/auto-rejoin.lua
Normal file
48
plugins/xC/auto-rejoin.lua
Normal file
@@ -0,0 +1,48 @@
|
||||
--
|
||||
-- auto-rejoin.lua: join back automatically when someone kicks you
|
||||
--
|
||||
-- Copyright (c) 2016, 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 timeout
|
||||
xC.setup_config {
|
||||
timeout = {
|
||||
type = "integer",
|
||||
comment = "auto rejoin timeout",
|
||||
default = "0",
|
||||
|
||||
on_change = function (v)
|
||||
timeout = v
|
||||
end,
|
||||
validate = function (v)
|
||||
if v < 0 then error ("timeout must not be negative", 0) end
|
||||
end,
|
||||
},
|
||||
}
|
||||
|
||||
async, await = xC.async, coroutine.yield
|
||||
xC.hook_irc (function (hook, server, line)
|
||||
local msg = xC.parse (line)
|
||||
if msg.command ~= "KICK" then return line end
|
||||
|
||||
local who = msg.prefix:match ("^[^!]*")
|
||||
local channel, whom = table.unpack (msg.params)
|
||||
if who ~= whom and whom == server.user.nickname then
|
||||
async.go (function ()
|
||||
await (async.timer_ms (timeout * 1000))
|
||||
server:send ("JOIN " .. channel)
|
||||
end)
|
||||
end
|
||||
return line
|
||||
end)
|
||||
90
plugins/xC/censor.lua
Normal file
90
plugins/xC/censor.lua
Normal file
@@ -0,0 +1,90 @@
|
||||
--
|
||||
-- censor.lua: black out certain users' messages
|
||||
--
|
||||
-- Copyright (c) 2016 - 2021, 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 to_pattern = function (mask)
|
||||
if not mask:match ("!") then mask = mask .. "!*" end
|
||||
if not mask:match ("@") then mask = mask .. "@*" end
|
||||
|
||||
-- That is, * acts like a wildcard, otherwise everything is escaped
|
||||
return "^" .. mask:gsub ("[%^%$%(%)%%%.%[%]%+%-%?]", "%%%0")
|
||||
:gsub ("%*", ".*") .. "$"
|
||||
end
|
||||
|
||||
local patterns = {}
|
||||
local read_masks = function (v)
|
||||
patterns = {}
|
||||
local add = function (who, where)
|
||||
local channels = patterns[who] or {}
|
||||
table.insert (channels, where)
|
||||
patterns[who] = channels
|
||||
end
|
||||
for item in v:lower ():gmatch ("[^,]+") do
|
||||
local who, where = item:match ("^([^/]+)/*(.*)")
|
||||
if who then add (to_pattern (who), where == "" or where) end
|
||||
end
|
||||
end
|
||||
|
||||
local quote
|
||||
xC.setup_config {
|
||||
masks = {
|
||||
type = "string_array",
|
||||
default = "\"\"",
|
||||
comment = "user masks (optionally \"/#channel\") to censor",
|
||||
on_change = read_masks
|
||||
},
|
||||
quote = {
|
||||
type = "string",
|
||||
default = "\"\\x0301,01\"",
|
||||
comment = "formatting prefix for censored messages",
|
||||
on_change = function (v) quote = v end
|
||||
},
|
||||
}
|
||||
|
||||
local decolor = function (text)
|
||||
local rebuilt, last = {""}, 1
|
||||
for start in text:gmatch ('()\x03') do
|
||||
table.insert (rebuilt, text:sub (last, start - 1))
|
||||
local sub = text:sub (start + 1)
|
||||
last = start + (sub:match ('^%d%d?,%d%d?()') or sub:match ('^%d?%d?()'))
|
||||
end
|
||||
return table.concat (rebuilt) .. text:sub (last)
|
||||
end
|
||||
|
||||
local censor = function (line)
|
||||
-- Taking a shortcut to avoid lengthy message reassembly
|
||||
local start, text = line:match ("^(.- PRIVMSG .- :)(.*)$")
|
||||
local ctcp, rest = text:match ("^(\x01%g+ )(.*)")
|
||||
text = ctcp and ctcp .. quote .. decolor (rest) or quote .. decolor (text)
|
||||
return start .. text
|
||||
end
|
||||
|
||||
xC.hook_irc (function (hook, server, line)
|
||||
local msg = xC.parse (line)
|
||||
if msg.command ~= "PRIVMSG" then return line end
|
||||
|
||||
local channel = msg.params[1]:lower ()
|
||||
for who, where in pairs (patterns) do
|
||||
if msg.prefix:lower ():match (who) then
|
||||
for _, x in pairs (where) do
|
||||
if x == true or x == channel then
|
||||
return censor (line)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return line
|
||||
end)
|
||||
105
plugins/xC/fancy-prompt.lua
Normal file
105
plugins/xC/fancy-prompt.lua
Normal file
@@ -0,0 +1,105 @@
|
||||
--
|
||||
-- fancy-prompt.lua: the fancy multiline prompt you probably want
|
||||
--
|
||||
-- Copyright (c) 2016, 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.
|
||||
--
|
||||
-- Beware that it is a hack and only goes about 90% of the way, which is why
|
||||
-- this functionality is only available as a plugin in the first place
|
||||
-- (well, and also for customizability).
|
||||
--
|
||||
-- The biggest problem is that the way we work with Readline is incompatible
|
||||
-- with multiline prompts, and normal newlines just don't work. This is being
|
||||
-- circumvented by using an overflowing single-line prompt with a specially
|
||||
-- crafted character in the rightmost column that prevents the bar's background
|
||||
-- from spilling all over the last line.
|
||||
--
|
||||
-- There is also a problem with C-r search rendering not clearing out the
|
||||
-- background but to really fix that mode, we'd have to fully reimplement it
|
||||
-- since its alternative prompt very often gets overriden by accident anyway.
|
||||
|
||||
xC.hook_prompt (function (hook)
|
||||
local current = xC.current_buffer
|
||||
local chan = current.channel
|
||||
local s = current.server
|
||||
|
||||
local bg_color = "255"
|
||||
local current_n = 0
|
||||
local active = ""
|
||||
for i, buffer in ipairs (xC.buffers) do
|
||||
if buffer == current then
|
||||
current_n = i
|
||||
elseif buffer.new_messages_count ~= buffer.new_unimportant_count then
|
||||
if active ~= "" then active = active .. "," end
|
||||
if buffer.highlighted then
|
||||
active = active .. "!"
|
||||
bg_color = "224"
|
||||
end
|
||||
active = active .. i
|
||||
end
|
||||
end
|
||||
if active ~= "" then active = "(" .. active .. ")" end
|
||||
local x = current_n .. ":" .. current.name
|
||||
if chan and chan.users_len ~= 0 then
|
||||
local params = ""
|
||||
for mode, param in pairs (chan.param_modes) do
|
||||
params = params .. " +" .. mode .. " " .. param
|
||||
end
|
||||
local modes = chan.no_param_modes .. params:sub (3)
|
||||
if modes ~= "" then x = x .. "(+" .. modes .. ")" end
|
||||
x = x .. "{" .. chan.users_len .. "}"
|
||||
end
|
||||
if current.hide_unimportant then x = x .. "<H>" end
|
||||
|
||||
local lines, cols = xC.get_screen_size ()
|
||||
x = x .. " " .. active .. string.rep (" ", cols)
|
||||
|
||||
-- Readline 7.0.003 seems to be broken and completely corrupts the prompt.
|
||||
-- However 8.0.004 seems to be fine with these, as is libedit 20191231-3.1.
|
||||
--x = x:gsub("[\128-\255]", "?")
|
||||
|
||||
-- Cut off extra characters and apply formatting, including the hack.
|
||||
-- FIXME: this doesn't count with full-width or zero-width characters.
|
||||
-- We might want to export wcwidth() above term_from_utf8 somehow.
|
||||
local overflow = utf8.offset (x, cols - 1)
|
||||
if overflow then x = x:sub (1, overflow) end
|
||||
x = "\x01\x1b[0;4;1;38;5;16m\x1b[48;5;" .. bg_color .. "m\x02" ..
|
||||
x .. "\x01\x1b[0;4;1;7;38;5;" .. bg_color .. "m\x02 \x01\x1b[0;1m\x02"
|
||||
|
||||
local user_prefix = function (chan, user)
|
||||
for i, chan_user in ipairs (chan.users) do
|
||||
if chan_user.user == user then return chan_user.prefixes end
|
||||
end
|
||||
return ""
|
||||
end
|
||||
if s then
|
||||
x = x .. "["
|
||||
local state = s.state
|
||||
if state == "disconnected" or state == "connecting" then
|
||||
x = x .. "(" .. state .. ")"
|
||||
elseif state ~= "registered" then
|
||||
x = x .. "(unregistered)"
|
||||
else
|
||||
local user, modes = s.user, s.user_mode
|
||||
if chan then x = x .. user_prefix (chan, user) end
|
||||
x = x .. user.nickname
|
||||
if modes ~= "" then x = x .. "(" .. modes .. ")" end
|
||||
end
|
||||
x = x .. "] "
|
||||
else
|
||||
-- There needs to be at least one character so that the cursor
|
||||
-- doesn't get damaged by our hack in that last column
|
||||
x = x .. "> "
|
||||
end
|
||||
return x
|
||||
end)
|
||||
178
plugins/xC/last-fm.lua
Normal file
178
plugins/xC/last-fm.lua
Normal file
@@ -0,0 +1,178 @@
|
||||
--
|
||||
-- last-fm.lua: "now playing" feature using the last.fm API
|
||||
--
|
||||
-- Dependencies: lua-cjson (from luarocks e.g.)
|
||||
--
|
||||
-- I call this style closure-oriented programming
|
||||
--
|
||||
-- Copyright (c) 2016, 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 cjson = require "cjson"
|
||||
|
||||
-- Setup configuration to load last.fm API credentials from
|
||||
local user, api_key
|
||||
xC.setup_config {
|
||||
user = {
|
||||
type = "string",
|
||||
comment = "last.fm username",
|
||||
on_change = function (v) user = v end
|
||||
},
|
||||
api_key = {
|
||||
type = "string",
|
||||
comment = "last.fm API key",
|
||||
on_change = function (v) api_key = v end
|
||||
},
|
||||
}
|
||||
|
||||
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
-- Generic error reporting
|
||||
local report_error = function (buffer, error)
|
||||
buffer:log ("last-fm error: " .. error)
|
||||
end
|
||||
|
||||
-- Process data return by the server and extract the now playing song
|
||||
local process = function (buffer, data, action)
|
||||
-- There's no reasonable Lua package to parse HTTP that I could find
|
||||
local s, e, v, status, message = string.find (data, "(%S+) (%S+) .+\r\n")
|
||||
if not s then return "server returned unexpected data" end
|
||||
if status ~= "200" then return status .. " " .. message end
|
||||
|
||||
local s, e = string.find (data, "\r\n\r\n")
|
||||
if not s then return "server returned unexpected data" end
|
||||
|
||||
local parser = cjson.new ()
|
||||
data = parser.decode (string.sub (data, e + 1))
|
||||
if not data.recenttracks or not data.recenttracks.track then
|
||||
return "invalid response" end
|
||||
|
||||
-- Need to make some sense of the XML automatically converted to JSON
|
||||
local text_of = function (node)
|
||||
if type (node) ~= "table" then return node end
|
||||
return node["#text"] ~= "" and node["#text"] or nil
|
||||
end
|
||||
|
||||
local name, artist, album
|
||||
for i, track in ipairs (data.recenttracks.track) do
|
||||
if track["@attr"] and track["@attr"].nowplaying then
|
||||
if track.name then name = text_of (track.name) end
|
||||
if track.artist then artist = text_of (track.artist) end
|
||||
if track.album then album = text_of (track.album) end
|
||||
end
|
||||
end
|
||||
|
||||
if not name then
|
||||
action (false)
|
||||
else
|
||||
local np = "\"" .. name .. "\""
|
||||
if artist then np = np .. " by " .. artist end
|
||||
if album then np = np .. " from " .. album end
|
||||
action (np)
|
||||
end
|
||||
end
|
||||
|
||||
-- Set up the connection and make the request
|
||||
local on_connected = function (buffer, c, host, action)
|
||||
-- Buffer data in the connection object
|
||||
c.data = ""
|
||||
c.on_data = function (data)
|
||||
c.data = c.data .. data
|
||||
end
|
||||
|
||||
-- And process it after we receive everything
|
||||
c.on_eof = function ()
|
||||
error = process (buffer, c.data, action)
|
||||
if error then report_error (buffer, error) end
|
||||
c:close ()
|
||||
end
|
||||
c.on_error = function (e)
|
||||
report_error (buffer, e)
|
||||
end
|
||||
|
||||
-- Make the unencrypted HTTP request
|
||||
local url = "/2.0/?method=user.getrecenttracks&user=" .. user ..
|
||||
"&limit=1&api_key=" .. api_key .. "&format=json"
|
||||
c:send ("GET " .. url .. " HTTP/1.1\r\n")
|
||||
c:send ("User-agent: last-fm.lua\r\n")
|
||||
c:send ("Host: " .. host .. "\r\n")
|
||||
c:send ("Connection: close\r\n")
|
||||
c:send ("\r\n")
|
||||
end
|
||||
|
||||
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
-- Avoid establishing more than one connection at a time
|
||||
local running
|
||||
|
||||
-- Initiate a connection to last.fm servers
|
||||
async, await = xC.async, coroutine.yield
|
||||
local make_request = function (buffer, action)
|
||||
if not user or not api_key then
|
||||
report_error (buffer, "configuration is incomplete")
|
||||
return
|
||||
end
|
||||
|
||||
if running then running:cancel () end
|
||||
running = async.go (function ()
|
||||
local c, host, e = await (async.dial ("ws.audioscrobbler.com", 80))
|
||||
if e then
|
||||
report_error (buffer, e)
|
||||
else
|
||||
on_connected (buffer, c, host, action)
|
||||
end
|
||||
running = nil
|
||||
end)
|
||||
end
|
||||
|
||||
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
local now_playing
|
||||
|
||||
local tell_song = function (buffer)
|
||||
if now_playing == nil then
|
||||
buffer:log ("last-fm: I don't know what you're listening to")
|
||||
elseif not now_playing then
|
||||
buffer:log ("last-fm: not playing anything right now")
|
||||
else
|
||||
buffer:log ("last-fm: now playing: " .. now_playing)
|
||||
end
|
||||
end
|
||||
|
||||
local send_song = function (buffer)
|
||||
if not now_playing then
|
||||
tell_song (buffer)
|
||||
else
|
||||
buffer:execute ("/me is listening to " .. now_playing)
|
||||
end
|
||||
end
|
||||
|
||||
-- Hook input to simulate new commands
|
||||
xC.hook_input (function (hook, buffer, input)
|
||||
if input == "/np" then
|
||||
make_request (buffer, function (np)
|
||||
now_playing = np
|
||||
send_song (buffer)
|
||||
end)
|
||||
elseif input == "/np?" then
|
||||
make_request (buffer, function (np)
|
||||
now_playing = np
|
||||
tell_song (buffer)
|
||||
end)
|
||||
elseif input == "/np!" then
|
||||
send_song (buffer)
|
||||
else
|
||||
return input
|
||||
end
|
||||
end)
|
||||
32
plugins/xC/ping-timeout.lua
Normal file
32
plugins/xC/ping-timeout.lua
Normal file
@@ -0,0 +1,32 @@
|
||||
--
|
||||
-- ping-timeout.lua: ping timeout readability enhancement plugin
|
||||
--
|
||||
-- Copyright (c) 2015 - 2016, 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.
|
||||
--
|
||||
|
||||
xC.hook_irc (function (hook, server, line)
|
||||
local msg = xC.parse (line)
|
||||
local start, timeout = line:match ("^(.* :Ping timeout:) (%d+) seconds$")
|
||||
if msg.command ~= "QUIT" or not start then
|
||||
return line
|
||||
end
|
||||
|
||||
local minutes = timeout // 60
|
||||
if minutes == 0 then
|
||||
return line
|
||||
end
|
||||
|
||||
local seconds = timeout % 60
|
||||
return ("%s %d minutes, %d seconds"):format (start, minutes, seconds)
|
||||
end)
|
||||
68
plugins/xC/prime.lua
Normal file
68
plugins/xC/prime.lua
Normal file
@@ -0,0 +1,68 @@
|
||||
--
|
||||
-- prime.lua: highlight prime numbers in messages
|
||||
--
|
||||
-- Copyright (c) 2020, 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 smallest, highlight = 0, "\x1f"
|
||||
xC.setup_config {
|
||||
smallest = {
|
||||
type = "integer",
|
||||
default = "0",
|
||||
comment = "smallest number to scan for primality",
|
||||
on_change = function (v) smallest = math.max (v, 2) end
|
||||
},
|
||||
highlight = {
|
||||
type = "string",
|
||||
default = "\"\\x1f\"",
|
||||
comment = "the attribute to use for highlights",
|
||||
on_change = function (v) highlight = v end
|
||||
},
|
||||
}
|
||||
|
||||
-- The prime test is actually very fast, so there is no DoS concern
|
||||
local do_intercolour = function (text)
|
||||
return tostring (text:gsub ("%f[%w_]%d+", function (n)
|
||||
if tonumber (n) < smallest then return nil end
|
||||
for i = 2, n ^ (1 / 2) do if (n % i) == 0 then return nil end end
|
||||
return highlight .. n .. highlight
|
||||
end))
|
||||
end
|
||||
|
||||
local do_interlink = function (text)
|
||||
local rebuilt, last = {""}, 1
|
||||
for start in text:gmatch ('()\x03') do
|
||||
table.insert (rebuilt, do_intercolour (text:sub (last, start - 1)))
|
||||
local sub = text:sub (start + 1)
|
||||
last = start + (sub:match ('^%d%d?,%d%d?()') or sub:match ('^%d?%d?()'))
|
||||
table.insert (rebuilt, text:sub (start, last - 1))
|
||||
end
|
||||
return table.concat (rebuilt) .. do_intercolour (text:sub (last))
|
||||
end
|
||||
|
||||
local do_message = function (text)
|
||||
local rebuilt, last = {""}, 1
|
||||
for run, link, endpos in text:gmatch ('(.-)(%f[%g]https?://%g+)()') do
|
||||
last = endpos
|
||||
table.insert (rebuilt, do_interlink (run) .. link)
|
||||
end
|
||||
return table.concat (rebuilt) .. do_interlink (text:sub (last))
|
||||
end
|
||||
|
||||
-- XXX: sadly it won't typically highlight primes in our own messages,
|
||||
-- unless IRCv3 echo-message is on
|
||||
xC.hook_irc (function (hook, server, line)
|
||||
local start, message = line:match ("^(.- PRIVMSG .- :)(.*)$")
|
||||
return message and start .. do_message (message) or line
|
||||
end)
|
||||
147
plugins/xC/slack.lua
Normal file
147
plugins/xC/slack.lua
Normal file
@@ -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)
|
||||
27
plugins/xC/thin-cursor.lua
Normal file
27
plugins/xC/thin-cursor.lua
Normal file
@@ -0,0 +1,27 @@
|
||||
--
|
||||
-- thin-cursor.lua: set a thin cursor
|
||||
--
|
||||
-- Copyright (c) 2016, 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.
|
||||
--
|
||||
-- If tmux doesn't work, add the following to its configuration:
|
||||
-- set -as terminal-overrides ',*:Ss=\E[%p1%d q:Se=\E[2 q'
|
||||
-- Change the "2" as per http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
|
||||
|
||||
local out = io.output ()
|
||||
out:write ("\x1b[6 q"):flush ()
|
||||
|
||||
-- By registering a global variable, we get notified about plugin unload
|
||||
x = setmetatable ({}, { __gc = function ()
|
||||
out:write ("\x1b[2 q"):flush ()
|
||||
end })
|
||||
62
plugins/xC/utm-filter.lua
Normal file
62
plugins/xC/utm-filter.lua
Normal file
@@ -0,0 +1,62 @@
|
||||
--
|
||||
-- utm-filter.lua: filter out Google Analytics bullshit from URLs
|
||||
--
|
||||
-- Copyright (c) 2015, 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.
|
||||
--
|
||||
|
||||
-- A list of useless URL parameters that don't affect page function
|
||||
local banned = {
|
||||
gclid = 1,
|
||||
|
||||
utm_source = 1,
|
||||
utm_medium = 1,
|
||||
utm_term = 1,
|
||||
utm_content = 1,
|
||||
utm_campaign = 1,
|
||||
}
|
||||
|
||||
-- Go through a parameter list and throw out any banned elements
|
||||
local do_args = function (args)
|
||||
local filtered = {}
|
||||
for part in args:gmatch ("[^&]+") do
|
||||
if not banned[part:match ("^[^=]*")] then
|
||||
table.insert (filtered, part)
|
||||
end
|
||||
end
|
||||
return table.concat (filtered, "&")
|
||||
end
|
||||
|
||||
-- Filter parameters in both the query and the fragment part of an URL
|
||||
local do_single_url = function (url)
|
||||
return url:gsub ('^([^?#]*)%?([^#]*)', function (start, query)
|
||||
local clean = do_args (query)
|
||||
return #clean > 0 and start .. "?" .. clean or start
|
||||
end, 1):gsub ('^([^#]*)#(.*)', function (start, fragment)
|
||||
local clean = do_args (fragment)
|
||||
return #clean > 0 and start .. "#" .. clean or start
|
||||
end, 1)
|
||||
end
|
||||
|
||||
local do_text = function (text)
|
||||
return text:gsub ('%f[%g]https?://%g+', do_single_url)
|
||||
end
|
||||
|
||||
xC.hook_irc (function (hook, server, line)
|
||||
local start, message = line:match ("^(.* :)(.*)$")
|
||||
return message and start .. do_text (message) or line
|
||||
end)
|
||||
|
||||
xC.hook_input (function (hook, buffer, input)
|
||||
return do_text (input)
|
||||
end)
|
||||
Reference in New Issue
Block a user