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
|
local cqueues = require'cqueues'
local signal = require'cqueues.signal'
local class = require'r.class'
local rtable = require'r.table'
local pprint = require'pprint'
local config = require'config'
local Thing = require'thing'
local Log = require 'log'
local Store = require 'store'
local pylon_classes = {
irc = require'irc.pylon',
xmpp = require'xmpp.pylon',
discord = require'discord.pylon',
nanochat = require'nanochat.pylon',
quaddle = require'quaddle.pylon',
}
local Wilson = class()
function Wilson.make(cls, conf)
local tlc = conf.top.level or {}
local self = setmetatable({
pylons={},
busses={},
cq = cqueues.new(),
log = Log('(toplevel)',tlc.loglevel or 'info'),
store = Store(),
}, Wilson)
for name, pylon_conf in pairs(conf.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)
self.log("constructed pylon",name,pylon_conf.type)
end
for name, bus_conf in pairs(conf.bus) do
local bus = {}
for _, item in ipairs(bus_conf) do
local pylon_name, channel_id = item[1], item[2]
local pylon = self.pylons[pylon_name]
assert(pylon,"no such pylon named "..pylon_name)
table.insert(bus, Thing(pylon, channel_id))
end
self.log("constructed bus ",name,'with '..#bus_conf..' channels')
self.busses[name] = bus
end
return self
end
function Wilson.add_channels_for(self, pylon)
if not pylon.channels then return end
for busname, bus in pairs(busses) do
for _, channel in ipairs(bus) do
if channel.pylon == pylon
and not pylon.channels[channel.id]
then
pylon:add_channel(channel)
end end end end
function Wilson.load_config(self, conf)
local pneed,phave = rtable.keydiff(conf.pylon, self.pylons)
if next(phave) then error"don't know how to remove pylons yet" end
for pname,p in pairs(conf.pylons) do if self.pylons[pname] then
for k,v in pairs(p) do if v ~= self.pylons[pname][k] then
error("different field "..k.." on "..pname) end end end end
-- loop thru buses, form list of channels we need to care about
-- don't know how to remove channels yet
-- then for pylons need to be constructed, pass them the list of channels to connect to
-- verify pylon names are valid here too
local ch4p = {} for k in pairs(conf.pylon) do ch4p[k] = {} end
for busname,b in pairs(conf.bus) do for i,item in ipairs(b) do
local pname,cid = item[1],item[2]
if not ch4p[pname] then error("no such pylon "..pname) end
table.insert(ch4p[pname], cid) end end
for pname,p in pairs(conf.pylon) do
p.name = pname
if self.pylons[pname] then
self.pylons[pname]:sync_channels(ch4p[pname])
else
local cls = assert(pylon_class[p.type], "no such pylon type "..p.type)
self.pylon[name] = cls(self, p, ch4p[pname])
end
end
end
-- find bus containing given channel
function Wilson._find_bus(self, channel)
for name, bus in pairs(self.busses) do
for _, q in ipairs(bus) do if q == channel then return bus end end
end
self.log:warn("unfound bus for",channel) end
function Wilson.deliver(self, message)
local bus = self:_find_bus(assert(message.channel))
if bus then
self.store:store(message)
for _, dest_channel in ipairs(bus) do
if message.channel ~= dest_channel then
self.log:debug(message.channel, "-->", dest_channel, message)
dest_channel.pylon:post(dest_channel, message)
end end end end
function Wilson.run(self)
for pylonname, pylon in pairs(self.pylons) do
self.cq:wrap(pylon.run, pylon)
self.log("now running pylon", pylonname)
end
self.cq:wrap(self.store.run, self.store)
self.cq:wrap(self.hup, self)
self.log:loop(self.cq)
end
function Wilson.hup(self)
signal.block(signal.SIGHUP)
local sl = signal.listen(signal.SIGHUP)
while true do
local s = sl:wait()
self:reload_config()
end
end
local config_file = assert(io.open("wilson.ini","r"))
local conf = config.parse(config_file)
config_file:close()
local wilson = Wilson:make(conf)
wilson:run()
|