summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--discord.lua24
-rw-r--r--irc.lua129
-rw-r--r--main.lua34
-rw-r--r--rirc.lua66
-rw-r--r--xml.lua80
-rw-r--r--xmpp.lua98
6 files changed, 396 insertions, 35 deletions
diff --git a/discord.lua b/discord.lua
new file mode 100644
index 0000000..27e0644
--- /dev/null
+++ b/discord.lua
@@ -0,0 +1,24 @@
+local http_request = require'http.request'
+local dkjson = require'dkjson'
+local url = 'https://discord.com/api/v8/webhooks/1277689699254800436/gzYU3voeunQsEdC797-hgXdTIbJDk09IVuj6l1t2alqbX9xS0d_St7bDWRkagUuQ3sat'
+
+local function exec_webhook(url, payload, debug)
+ local req = http_request.new_from_uri(url)
+ req.headers:upsert('content-type','application/json')
+ req.headers:upsert(':method','POST')
+ req:set_body(dkjson.encode(payload))
+ local resp_head, resp_body = assert(req:go())
+ local status = resp_head:get':status'
+ if debug then
+ resp_head:dump()
+ print()
+ print(resp_body:get_body_as_string())
+ end
+ assert(status:sub(1,1) == '2', 'status was '..status..' not 2xx')
+end
+
+return { grom = function(from,body) print(body) exec_webhook(url, {
+ content=body,
+ username=from,
+ }, true) end }
+
diff --git a/irc.lua b/irc.lua
new file mode 100644
index 0000000..d6f8dae
--- /dev/null
+++ b/irc.lua
@@ -0,0 +1,129 @@
+local cqueues = require'cqueues'
+local socket = require'cqueues.socket'
+local condition = require'cqueues.condition'
+local pprint = require'pprint'
+local rirc = require'rirc'
+
+local Irc = {}
+
+function Irc.makepylon(pylonname, conf)
+ local self = {pylonname=pylonname}
+ local function conf_var(name)
+ assert(conf[name] ~= nil, 'missing conf field '..name)
+ self[name] = conf[name]
+ end
+ conf_var 'host'
+ conf_var 'port'
+ conf_var 'password'
+ conf_var 'nodename'
+
+ self.outqueue = {}
+ self.outcv = condition.new()
+ setmetatable(self, {__index=Irc})
+ return self
+end
+
+function Irc.send(self, args)
+ args.source = args.source or self.nodename
+ rirc.send(self.sock, args)
+end
+
+function Irc.connect(self)
+ self.sock = assert(socket.connect(self.host, self.port))
+ self:send{'PASS', self.password, '0210-IRC', 'wilson|'}
+ self:send{'SERVER', self.nodename, '1', 'i am wilson'}
+end
+
+function Irc.recving(self)
+ for line in self.sock:lines "*l" do
+ print('<', line)
+ local msg = rirc.parse(line)
+ if msg.op == 'PING' then
+ self:send{'PONG', msg.args[1]}
+ elseif msg.op == 'PRIVMSG' then
+ local ch = msg.args[1]
+ local body = msg.args[2]
+ local source = msg.source
+ local neighbors = self.network:neighbors_of(self.pylonname, ch)
+ pprint('gt neighbors', neighbors)
+ for _,other in ipairs(neighbors) do
+ other.pylon:enqueue {
+ channel = other.channel,
+ body = body,
+ source = source..'[i]'
+ }
+ end
+ end
+ end
+end
+
+function Irc.sending(self)
+ local nicks_channels = {}
+ local function ensure_joined(nick, channel)
+ if not nicks_channels[nick] then
+ self:send{'NICK', nick, 1, 'username', 'host', 1, '+', 'realname'}
+ nicks_channels[nick] = {}
+ end
+ if not nicks_channels[nick][channel] then
+ self:send{source=nick, 'JOIN', channel}
+ nicks_channels[nick][channel] = true
+ end
+ end
+ local function say(nick, channel, body)
+ ensure_joined(nick, channel)
+ self:send{source=nick, 'PRIVMSG', channel, body}
+ end
+
+ say('WILSON', '#test', 'i am wilson')
+
+ while true do
+ while #self.outqueue > 0 do
+ local queue = self.outqueue
+ self.outqueue = {}
+ for _, outmsg in pairs(queue) do
+ assert(say(outmsg.source, outmsg.channel, outmsg.body))
+ end
+ end
+ self.outcv:wait()
+ end
+end
+
+function Irc.enqueue(self, message)
+ table.insert(self.outqueue, message)
+ self.outcv:signal()
+end
+
+function Irc.run(self, cq, network)
+ self.network = network
+ self:connect()
+ cq:wrap(self.recving, self)
+ cq:wrap(self.sending, self)
+end
+
+
+local cq = cqueues.new()
+local conf = {
+ host = 'localhost',
+ port = '6667',
+ password = 'mypassword',
+ nodename = 'wilson.ubq323',
+}
+
+local dummy_pylon = {
+ enqueue = function(self, msg)
+ print(string.format(
+ "%s <%s> | %s",
+ msg.channel, msg.source, msg.body))
+ end
+}
+
+local network = {neighbors_of = function(self, pylonname, channel)
+ print('looking for neighbors of '..pylonname..', '..channel)
+ return {
+ {pylon = dummy_pylon, channel='#bridge'}
+ }
+end}
+
+local pylon = Irc.makepylon('test', conf)
+pylon:run(cq, network)
+pprint(cq:loop())
diff --git a/main.lua b/main.lua
new file mode 100644
index 0000000..cbd98d8
--- /dev/null
+++ b/main.lua
@@ -0,0 +1,34 @@
+local irc=require'irc'
+local xmpp=require'xmpp'
+local discord=require'discord'
+
+local pylon_confs = {
+ xmpp_ubq323 = {
+ type='xmpp',
+ jid='wilson@ubq323.website',
+ server='ubq323.website',
+ auth='...',
+ resource='wilson',
+ },
+ irc_apionet = {
+ host='localhost',
+ port=6667
+ password='mypassword',
+ nodename='wilson.ubq323',
+ },
+}
+
+local pylons = {}
+for name, conf in pairs(pylon_confs) do
+ pylons[name] = pylon_types[conf.type].make_pylon(name, conf)
+end
+
+
+local channels = {
+ d = {
+ {xmpp_ubq323, 'd@conference.ubq323.website'},
+ {irc_apionet, '#d'},
+ },
+}
+
+
diff --git a/rirc.lua b/rirc.lua
new file mode 100644
index 0000000..b341bfd
--- /dev/null
+++ b/rirc.lua
@@ -0,0 +1,66 @@
+local irc = {}
+
+function irc.sendmsg(conn, ch, msg)
+ local m = msg:gsub("[\r\n]+"," ")
+ local l = 512-12-#ch
+ m = m:sub(1,l)
+ conn:send("PRIVMSG "..ch.." :"..m.."\r\n")
+end
+
+function irc.send(sock, args)
+ local out = ''
+ if args.source then
+ out = ':' .. args.source .. ' '
+ end
+ for i = 1, #args-1 do
+ local arg = tostring(args[i])
+ out = out .. arg:gsub("[\r\n ]+","") .. ' '
+ end
+ local last = tostring(args[#args]):gsub("[\r\n]+"," ")
+ out = out .. ':' .. last
+ print('>', out)
+ sock:write(out..'\r\n')
+end
+
+function irc.parse(line)
+ local words = {}
+ local idx = 1
+ while true do
+ local word,nidx = line:match("(%g+)%s*()",idx)
+ if not word then
+ break
+ elseif word:sub(1,1) == ":" and idx ~= 1 then
+ -- first word can start with :
+ local rest = line:sub(idx+1)
+ table.insert(words,rest)
+ break
+ else
+ table.insert(words,word)
+ idx = nidx
+ end
+ end
+
+ local from = nil
+ if words[1] and words[1]:sub(1,1) == ":" then
+ -- we have a source
+ from = table.remove(words,1):sub(2)
+ end
+
+ local op = words[1]
+ local args = {}
+ for i=2,#words do
+ args[i-1] = words[i]
+ end
+ return {source=from,op=op:upper(),args=args}
+end
+
+function irc.parse_src(src)
+ local s, _, nick, user, host = src:find("^(.*)!(.*)@(.*)$")
+ if s then
+ return true, nick, user, host
+ else
+ return false
+ end
+end
+
+return irc
diff --git a/xml.lua b/xml.lua
index 7153831..35d2804 100644
--- a/xml.lua
+++ b/xml.lua
@@ -1,6 +1,7 @@
-- originally from http://lua-users.org/wiki/LuaXml
-- modified by me a bit
+
local function parseargs(s)
local arg = {}
string.gsub(s, "([%-%w]+)=([\"'])(.-)%2", function (w, _, a)
@@ -69,6 +70,7 @@ do
local _, cqa = pcall(require, 'cqueues.auxlib')
wrap = cqa and cqa.wrap or coroutine.wrap
end
+
local function stanzae(getmore)
return wrap(function()
local buf = ''
@@ -85,5 +87,81 @@ local function stanzae(getmore)
end)
end
+local entity_escapes = {
+ ["<"]="&lt;",
+ [">"]="&gt;",
+ ["&"]="&amp;",
+ ['"']="&quot;",
+ ["'"]="&apos;"
+}
+
+local function escape(s)
+ return s:gsub("[<>&'\"]",entity_escapes)
+end
+
+local safestr_mt = {name='SAFESTR', __tostring=function(x) return x.s end}
+local function safestr(s) return setmetatable({s=s}, safestr_mt) end
+
+local function _xmlify(x)
+ if getmetatable(x) == safestr_mt then
+ return x
+ elseif type(x) == 'string' then
+ return safestr(escape(x))
+ elseif type(x) == 'table' then -- must be a tag
+ local function argstr(t)
+ local argstring = ''
+ for k,v in pairs(t) do
+ argstring = argstring .. (" %s='%s'"):format(k, _xmlify(v))
+ end
+ return argstring
+ end
+ if x.empty then
+ return safestr(("<%s%s/>"):format(x.label, argstr(x.xarg)))
+ else
+ local open = ("<%s%s>"):format(x.label, argstr(x.xarg))
+ local close = ("</%s>"):format(x.label)
+ local children = {}
+ for _,child in ipairs(x) do
+ table.insert(children, tostring(_xmlify(child)))
+ end
+ return safestr(open .. table.concat(children, '') .. close)
+ end
+ end
+end
+
+local function xmlify(...)
+ local n = select('#',...)
+ if n <= 1 then return _xmlify(...) end
+
+ local out = {}
+ for i = 1, n do
+ out[i] = tostring(_xmlify(select(i, ...)))
+ end
+ return table.concat(out, '\n')
+end
+
+-- dsl for input to xmlify. doesn't do escaping or anything.
+-- THERE IS NO ESCAPE
+local X = setmetatable({}, {__index=function(_,label)
+ return function(tab)
+ local out = {}
+ local xarg = {}
+ local empty = true
+
+ for k, v in pairs(tab) do
+ if type(k) == 'number' then -- child
+ empty = nil
+ out[k] = v
+ elseif type(k) == 'string' then -- attrib
+ xarg[k] = v
+ end
+ end
+
+ out.label = label
+ out.xarg = xarg
+ out.empty = empty
+ return out
+ end
+end})
-return { psingle = psingle, stanzae = stanzae, wrap=wrap }
+return { psingle = psingle, stanzae = stanzae, wrap=wrap, xmlify=xmlify, X=X }
diff --git a/xmpp.lua b/xmpp.lua
index aebbee9..84400bb 100644
--- a/xmpp.lua
+++ b/xmpp.lua
@@ -1,72 +1,102 @@
-local jid = 'wilson@ubq323.website'
-local server = 'ubq323.website'
-local auth = 'AHdpbHNvbgBncmVnb3J5PDM='
-local resource = 'cheese'
-
+local xmpp_conf = {
+ jid='wilson@ubq323.website',
+ server='ubq323.website',
+ auth='AHdpbHNvbgBncmVnb3J5PDM=',
+ resource='cheese',
+ muc='d@conference.ubq323.website',
+}
local cqueues = require'cqueues'
local socket = require'cqueues.socket'
+local xml = require'xml'
+local X = xml.X
+local xmlify = xml.xmlify
+local pprint=require'pprint'
+local discord=require'discord'
-local function connect()
- local sock = assert(socket.connect(server, 5222))
+local function connect(conf)
+ local sock = assert(socket.connect(conf.server, 5222))
sock:setmode('bn','bn')
local start = ([[
-<?xml version='1.0'?><stream:stream from='%s' to='%s' version='1.0' xml:lang='en' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>]]):format(jid, server)
+<?xml version='1.0'?><stream:stream from='%s' to='%s' version='1.0' xml:lang='en' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>]]):format(conf.jid, conf.server)
-- state of the art xml parser
local function check_and_send(test, text)
local x = sock:read('-2048')
- print(x)
+ assert(x:find(test))
if text then sock:write(text) end
end
+ local function ietf_urn(v) return 'urn:ietf:params:xml:ns:xmpp-'..v end
sock:write(start)
- check_and_send('starttls', [[<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>]])
+ check_and_send('starttls', xmlify(X.starttls{xmlns=ietf_urn"tls"}))
check_and_send('proceed', nil)
sock:starttls()
sock:write(start)
check_and_send('PLAIN',
- ([[<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>%s</auth>]]):format(auth))
+ xmlify(X.auth{xmlns=ietf_urn"sasl", mechanism='PLAIN', conf.auth}))
check_and_send('success',start)
- check_and_send('bind', ([[<iq type='set' id='aaa'>
- <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><resource>%s</resource></bind></iq>
- ]]):format(resource))
- check_and_send('jid','<presence/>')
+ check_and_send('bind',
+ xmlify(X.iq{type='set', id='aaaa',
+ X.bind{xmlns=ietf_urn"bind", X.resource{conf.resource}}}))
+ check_and_send('jid',X.presence{X.show{'chat'}})
return sock
end
-local mucs = {
- 'ja@conference.ubq323.website',
- 'a@conference.ubq323.website',
-}
+-- local mucs = {
+-- 'ja@conference.ubq323.website',
+-- 'a@conference.ubq323.website',
+-- }
-local xml = require'xml'
-local pprint=require'pprint'
-local function run_xmpp()
- local sock = connect()
+local function run_xmpp(cq)
+ local sock = connect(xmpp_conf)
+
+ -- sock:write(xmlify(
+ -- X.presence{to=xmpp_conf.muc..'/wilson',
+ -- X.x{xmlns='http://jabber.org/protocol/muc',
+ -- X.history{maxstanzas='0'}}}))
+
+
+ -- cq:wrap(function()
+ -- local n = 1
+ -- while true do
+ -- sock:write(xmlify(
+ -- X.message{to=xmpp_conf.muc, type='groupchat',
+ -- id='wilson_episode_'..n,
+ -- X.body{'i too am in episode '..n}}))
+ -- n=n+1
+ -- cqueues.sleep(60)
+ -- end
+ -- end)
+
+ sock:write(xmlify(
+ X.iq{type='get',to=xmpp_conf.muc, id='info1',
+ X.query{xmlns='http://jabber.org/protocol/disco#info'}}))
- for _, muc in ipairs(mucs) do
- sock:write(([[
- <presence to='%s'>
- <x xmlns='http://jabber.org/protocol/muc'>
- <history maxstanzas='0'/>
- </x>
- </presence>]]):format(muc .. '/wilson'))
- end
for x in xml.stanzae(function() return sock:read('-2048') end) do
- -- pprint(x)
+ pprint(x)
+ print(xmlify(x))
local body = x'body' and x'body'[1]
- if x.label == 'message' then pprint('M', x.xarg.from, body) end
+ if x.label == 'message' then
+ pprint('M', x.xarg.from, body)
+ if body == 'hi wilson' then
+ sock:write(xmlify(
+ X.message{to=xmpp_conf.muc, type='groupchat',
+ X.body{'hi '..x.xarg.from..'.'}}))
+ end
+ -- discord.grom(x.xarg.from, body or '*(there\'s no body here)*')
+ end
end
end
local cq = cqueues.new()
-cq:wrap(run_xmpp)
+cq:wrap(run_xmpp, cq)
+
pprint(cq:loop())