summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--channel.lua15
-rw-r--r--config.lua45
-rw-r--r--irc.lua82
-rw-r--r--main.lua115
-rw-r--r--terminology.txt17
-rw-r--r--wilson.ini28
-rw-r--r--xml_old.lua48
-rw-r--r--xmpp.lua20
8 files changed, 193 insertions, 177 deletions
diff --git a/channel.lua b/channel.lua
new file mode 100644
index 0000000..e908069
--- /dev/null
+++ b/channel.lua
@@ -0,0 +1,15 @@
+-- channel descriptor
+
+local Channel = {}
+function Channel.make(pylon, descriptor)
+ return setmetatable({pylon=pylon, descriptor=descriptor}, Channel)
+end
+function Channel.__eq(self, other)
+ return self.pylon == other.pylon and self.descriptor == other.descriptor
+end
+function Channel.__call(_, ...) return Channel.make(...) end
+function Channel.__tostring(self)
+ return self.pylon.name .. ':' .. self.descriptor
+end
+setmetatable(Channel, Channel)
+return Channel
diff --git a/config.lua b/config.lua
new file mode 100644
index 0000000..a8838c8
--- /dev/null
+++ b/config.lua
@@ -0,0 +1,45 @@
+local function partial(f,x) return function(...) return f(x,...) end end
+
+local function kv_syntax(block,line)
+ local k,v = line:match"^([a-z0-9_-]+)%s*=%s*(.*)$"
+ assert(k,"syntax error in kv line: "..line)
+ k = k:gsub("-","_")
+ block[k]=v
+end
+
+local function tuple_syntax(pattern) return function(block,line)
+ local matches = {line:match(pattern)}
+ assert(matches[1], "syntax error in tuple line: "..line)
+ table.insert(block, matches)
+end end
+
+local schema = {
+ pylon = kv_syntax,
+ bus = tuple_syntax"^(%S+)%s+(%S+)$",
+}
+
+local function parse_file(file)
+ local blocks = {}
+ for k in pairs(schema) do blocks[k] = {} end
+ local line_handler = function() error("line outside of block") end
+
+ for line in file:lines() do
+ line = line:gsub("#.*$",""):gsub("^%s*",""):gsub("%s*$","")
+ if line == '' then goto next end
+
+ local block_type, block_name = line:match"%[%s*(%S+)%s+(%S+)%s*%]"
+ if block_type then
+ local blocks_of_this_type = assert(blocks[block_type],"no such block type "..block_type)
+ local block = {}
+ blocks_of_this_type[block_name] = block
+ line_handler = partial(schema[block_type], block)
+ else
+ line_handler(line)
+ end
+ ::next::
+ end
+
+ return blocks
+end
+
+return {parse=parse_file}
diff --git a/irc.lua b/irc.lua
index 541c725..fe35753 100644
--- a/irc.lua
+++ b/irc.lua
@@ -12,23 +12,20 @@ function Irc._send(self, args)
print('>', sent)
end
-function Irc.makepylon(pylonname, conf, cq, network)
- local self = {
- pylonname=pylonname,
- cq = cq,
- network = network,
+function Irc.check_config(self, vars)
+ for x in vars:gmatch("%S+") do
+ assert(self[x], "missing conf field "..x) end end
+
+function Irc.make(wilson, conf)
+ local self = setmetatable(conf, {__index=Irc})
+
+ for k,v in pairs {
+ wilson = wilson,
inbox = Queue.make(),
- }
- 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'
+ } do self[k] = v end
+
+ self:check_config "host port password nodename"
- setmetatable(self, {__index=Irc})
return self
end
@@ -39,9 +36,11 @@ function Irc._connect(self)
end
function Irc.run(self)
+ local cq = cqueues.new()
self:_connect()
- self.cq:wrap(self.recving, self)
- self.cq:wrap(self.sending, self)
+ cq:wrap(self.recving, self)
+ cq:wrap(self.sending, self)
+ print('irc',cq:loop())
end
function Irc.recving(self)
@@ -51,13 +50,15 @@ function Irc.recving(self)
if msg.op == 'PING' then
self:_send{'PONG', msg.args[1]}
elseif msg.op == 'PRIVMSG' then
- local channel = msg.args[1]
+ local channel_name = msg.args[1]
local body = msg.args[2]
- local source = msg.source
- self.network:post(self.pylonname, channel, {
+ local sender = msg.source
+ self.wilson:deliver(Channel(self, channel_name), {
body = body,
- source = source..'[i]'
+ sender = sender..'[i]'
})
+ elseif msg.op == 'ERROR' then
+ error(msg.args[1])
end
end
end
@@ -81,39 +82,14 @@ function Irc.sending(self)
say('WILSON', '#test', 'i am wilson')
- for ch, msg in self.inbox:iter() do
- say(msg.source, ch, msg.body)
+ for dest_channel, message in self.inbox:iter() do
+ local channel_name = dest_channel.descriptor
+ say(message.sender, channel_name, message.body)
end
end
-return Irc
-
--- local cq = cqueues.new()
--- local conf = {
--- host = 'localhost',
--- port = '6667',
--- password = 'mypassword',
--- nodename = 'wilson.ubq323',
--- }
-
--- local dummy_network = {
--- post = function(self, pylonname, channel, message)
--- pprint(pylonname, channel, message)
--- end
--- }
+function Irc.post(self, dest_channel, message)
+ self.inbox.enqueue(dest_channel, message)
+end
--- local pylon = Irc.makepylon('test', conf, cq, dummy_network)
--- pylon:run()
--- cq:wrap(function()
--- local i = 0
--- while true do
--- cqueues.sleep(1)
--- pylon.inbox:enqueue{
--- source = 'helen',
--- channel = '#test',
--- body = 'i am helen '..i,
--- }
--- i = i + 1
--- end
--- end)
--- pprint('cheese', cq:loop())
+return Irc
diff --git a/main.lua b/main.lua
index 7d8c97d..b347410 100644
--- a/main.lua
+++ b/main.lua
@@ -1,86 +1,97 @@
local cqueues = require'cqueues'
+local config = require'config'
+local Channel = require'channel'
-local pylon_types = {
+local pylon_classes = {
irc = require'irc',
xmpp = require'xmpp',
}
-local Network = {}
-function Network.make(pylon_confs, bus_confs)
+local Wilson = {}
+function Wilson.make(config)
local self = {
pylons={},
busses={},
cq = cqueues.new(),
}
- for pylonname, conf in pairs(pylon_confs) do
- local pty = pylon_types[conf.type]
- self.pylons[pylonname] = pty.makepylon(pylonname, conf, self.cq, self)
- print("constructed pylon",pylonname,conf.type)
+ for name, pylon_conf in pairs(config.pylon) do
+ pylon_conf.name = name
+ local pylon_class = pylon_classes[pylon_conf.type]
+ assert(pylon_class, "no such pylon type "..pylon_conf.type)
+ self.pylons[name] = pylon_class.make(self, pylon_conf)
+ print("constructed pylon",name,pylon_conf.type)
end
- for busname, conf in pairs(bus_confs) do
+ for name, bus_conf in pairs(config.bus) do
local bus = {}
- for _, item in ipairs(conf) do
- table.insert(bus, {pylonname=item[1], channel=item[2]})
+ for _, item in ipairs(bus_conf) do
+ local pylon_name, channel_descriptor = item[1], item[2]
+ local pylon = self.pylons[pylon_name]
+ assert(pylon,"no such pylon named "..pylon_name)
+ table.insert(bus, Channel(pylon, channel_descriptor))
end
- self.busses[busname] = bus
+ print("constructed bus ",name,'with '..#bus_conf..' channels')
+ self.busses[name] = bus
end
- return setmetatable(self, {__index=Network})
+ return setmetatable(self, {__index=Wilson})
end
-function Network._find_bus(self, pylonname, channel)
+
+-- find bus containing given channel
+function Wilson._find_bus(self, channel)
for name, bus in pairs(self.busses) do
- for _, entry in ipairs(bus) do
- if entry.pylonname == pylonname and entry.channel == channel then
- return bus
- end
- end
+ for _, q in ipairs(bus) do if q == channel then return bus end end
end
- print("warning: unfound bus for",pylonname,channel)
+ print("warning: unfound bus for",channel)
end
-function Network.post(self, pylonname, channel, message)
- local bus = self:_find_bus(pylonname, channel)
+function Wilson.deliver(self, source_channel, message)
+ local bus = self:_find_bus(source_channel)
if bus then
- for _, dest in ipairs(bus) do
- if not (dest.pylonname == pylonname and dest.channel == channel) then
- local target_pylon = self.pylons[dest.pylonname]
- target_pylon.inbox:enqueue(dest.channel, message)
+ for _, dest_channel in ipairs(bus) do
+ if source_channel ~= dest_channel then
+ dest_channel.pylon:post(dest_channel, message)
end
end
end
end
-function Network.run(self)
+function Wilson.run(self)
for pylonname, pylon in pairs(self.pylons) do
self.cq:wrap(pylon.run, pylon)
print("now running pylon", pylonname)
end
- print(self.cq:loop())
+ print('toplevel',self.cq:loop())
end
+local config_file = io.open("wilson.ini","r")
+local conf = config.parse(config_file)
+config_file:close()
+local wilson = Wilson.make(conf)
+wilson:run()
+
-local pylon_confs = {
- xmpp_ubq323 = {
- type='xmpp',
- jid='wilson@ubq323.website',
- server='localhost',
- component='wilson.ubq323.website',
- component_secret='super_secret_wilson_password',
- resource='wilson',
- },
- irc_local = {
- type='irc',
- host='localhost',
- port=6667,
- password='mypassword',
- nodename='wilson.ubq323',
- },
-}
-local bus_confs = {
- d = {
- {'xmpp_ubq323', 'd@conference.ubq323.website'},
- {'irc_local', '#test'},
- },
-}
+-- local pylon_confs = {
+-- xmpp_ubq323 = {
+-- type='xmpp',
+-- jid='wilson@ubq323.website',
+-- server='localhost',
+-- component='wilson.ubq323.website',
+-- component_secret='super_secret_wilson_password',
+-- resource='wilson',
+-- },
+-- irc_local = {
+-- type='irc',
+-- host='localhost',
+-- port=6667,
+-- password='mypassword',
+-- nodename='wilson.ubq323',
+-- },
+-- }
+-- local bus_confs = {
+-- d = {
+-- {'xmpp_ubq323', 'd@conference.ubq323.website'},
+-- {'irc_local', '#test'},
+-- },
+-- }
-local the_network = Network.make(pylon_confs, bus_confs)
-the_network:run()
+-- local the_network = Wilson.make(pylon_confs, bus_confs)
+-- the_network:run()
diff --git a/terminology.txt b/terminology.txt
new file mode 100644
index 0000000..e4157b2
--- /dev/null
+++ b/terminology.txt
@@ -0,0 +1,17 @@
+note: i'm writing this after not working on this project for 6 months
+so i'm kind of figuring this out for myself
+
+anyway
+
+pylon: a chat server that wilson makes a connection to
+ which could be "some irc server", "some xmpp server"
+ or "discord" (discord is centralized so there's only 1 discord to connect to)
+ note that discord's concept of "server" aka "guild" is unrelated to anything
+
+channel: a particular chatroom on a particular service
+ eg "#a on apionet" or "#general in some discord guild"
+ in the code, a channel is identified by a combination of a pylon,
+ and a string 'channel descriptor', whose format depends on the pylon type
+ for instance xmpp channel descriptors are jids, discord uses numeric ids
+
+bus: a family of connected channels that messages will be bridged between
diff --git a/wilson.ini b/wilson.ini
index b875b4f..df80e09 100644
--- a/wilson.ini
+++ b/wilson.ini
@@ -1,33 +1,35 @@
-[pylon discord]
-type=discord
-token=abcdef123456
+# [pylon discord]
+# type=discord
+# token=abcdef123456
[pylon apionet-irc]
type=irc
host=ubq323.website
nick=wilson
-
-[pylon other-irc]
-type=irc
-host=someotherhost.example
-nick=gregory
+port=6667
+password=secretpassword
+nodename=wilson.ubq323
[pylon ubq-xmpp]
type=xmpp
jid=wilson@ubq323.website
+server=ubq323.website
password=zyxw9876
default-service=conference.ubq323.website
+resource=wilson
+component=wilson.ubq323.website
+component_secret=my_secret_password
-[channel a]
+[bus a]
# apionet a
-discord 12345678 # esoserver #apionet
-discord 98765432 # apionet discord #a
+# discord 12345678 # esoserver #apionet
+# discord 98765432 # apionet discord #a
ubq-xmpp a@
apionet-irc a
-[channel ja]
-discord 3141592654 # apionet discord #ja
+[bus ja]
+# discord 3141592654 # apionet discord #ja
apionet-irc ja
ubq-xmpp ja@
diff --git a/xml_old.lua b/xml_old.lua
deleted file mode 100644
index a63c0f7..0000000
--- a/xml_old.lua
+++ /dev/null
@@ -1,48 +0,0 @@
-function parseargs(s)
- local arg = {}
- string.gsub(s, "([%-%w]+)=([\"'])(.-)%2", function (w, _, a)
- arg[w] = a
- end)
- return arg
-end
-
-function collect(s)
- local stack = {}
- local top = {}
- table.insert(stack, top)
- local ni,c,label,xarg, empty
- local i, j = 1, 1
- while true do
- ni,j,c,label,xarg, empty = string.find(s, "<(%/?)([%w:]+)(.-)(%/?)>", i)
- if not ni then break end
- local text = string.sub(s, i, ni-1)
- if not string.find(text, "^%s*$") then
- table.insert(top, text)
- end
- if empty == "/" then -- empty element tag
- table.insert(top, {label=label, xarg=parseargs(xarg), empty=1})
- elseif c == "" then -- start tag
- top = {label=label, xarg=parseargs(xarg)}
- table.insert(stack, top) -- new level
- else -- end tag
- local toclose = table.remove(stack) -- remove top
- top = stack[#stack]
- if #stack < 1 then
- error("nothing to close with "..label)
- end
- if toclose.label ~= label then
- error("trying to close "..toclose.label.." with "..label)
- end
- table.insert(top, toclose)
- end
- i = j+1
- end
- local text = string.sub(s, i)
- if not string.find(text, "^%s*$") then
- table.insert(stack[#stack], text)
- end
- if #stack > 1 then
- error("unclosed "..stack[#stack].label)
- end
- return stack[1]
-end
diff --git a/xmpp.lua b/xmpp.lua
index 24a5a3c..0b7a33d 100644
--- a/xmpp.lua
+++ b/xmpp.lua
@@ -17,11 +17,10 @@ local function make_auth(authz, authn, password)
return base64.encode(authz..'\0'..authn..'\0'..password)
end
-function Xmpp.makepylon(pylonname, conf, cq, network)
+function Xmpp.make(wilson, conf)
local self = {
- pylonname = pylonname,
- cq = cq,
- network = network,
+ name = conf.name,
+ wilson = wilson,
inbox = Queue.make(),
}
local function conf_var(name)
@@ -36,7 +35,6 @@ function Xmpp.makepylon(pylonname, conf, cq, network)
setmetatable(self, {__index=Xmpp})
return self
-
end
function Xmpp._connect_c2s(self)
@@ -124,9 +122,9 @@ function Xmpp.recving(self)
local fr = x.xarg.from
local t = x.xarg.to
if body and fr:match"/" and t == 'wilson@'..self.component then
- self.network:post(self.pylonname, THE_MUC, {
+ self.wilson:deliver(Channel(self, THE_MUC), {
body = body,
- source = '[x]'..x.xarg.from:match("/(.*)")
+ sender = '[x]'..x.xarg.from:match("/(.*)")
})
end
end
@@ -151,10 +149,10 @@ function Xmpp.sending(self)
X.history{maxstanzas='0'}}}))
end
ensure_joined(THE_MUC, 'wilson', 'wilson')
- for ch, msg in self.inbox:iter() do
- pprint(ch,msg)
- ensure_joined(THE_MUC, msg.source, msg.source)
- local user = msg.source:gsub("[^a-zA-Z0-9%.]","."):match("^%.*(.-)%.*$")
+ for dest_channel, message in self.inbox:iter() do
+ pprint(dest_channel, message)
+ ensure_joined(THE_MUC, message.sender, message.sender)
+ local user = message.sender:gsub("[^a-zA-Z0-9%.]","."):match("^%.*(.-)%.*$")
local jid = user..'@'..self.component
self.sock:write(xmlify(
X.message{to=THE_MUC, type='groupchat', from=jid,