|
1 local storage_backends = {}; |
|
2 function riddim.plugins.factoids(bot) |
|
3 local factoids = {}; |
|
4 local conf_backend = bot.config.factoid_backend; |
|
5 local backend; |
|
6 |
|
7 -- if backend == nil then forget everything on restart |
|
8 |
|
9 if conf_backend and storage_backends[conf_backend] then |
|
10 backend = storage_backends[conf_backend](bot, factoids); |
|
11 end |
|
12 |
|
13 bot:hook("unhandled-command", function(cmd) |
|
14 local name, param = cmd.command, cmd.param; |
|
15 local factoid = factoids[name]; |
|
16 if factoid then |
|
17 return factoid; |
|
18 elseif param and #param > 4 and param:match("^is ") then |
|
19 factoids[name] = param:sub(4); |
|
20 return "I'll remember that."; |
|
21 end |
|
22 end); |
|
23 bot:hook("commands/forget", function(cmd) |
|
24 local name = cmd.param; |
|
25 local factoid = factoids[name]; |
|
26 if factoid then |
|
27 factoids[name] = nil; |
|
28 if backend and backend.del then |
|
29 backend.del(name); |
|
30 end |
|
31 return ("Okay, I'll forget that %s is %s"):format(name, factoid); |
|
32 end |
|
33 end); |
|
34 end |
|
35 |
|
36 -- A simple file backend |
|
37 function storage_backends.file(bot, factoids) |
|
38 local factoids_file = bot.config.factoids_file or "./factoids.txt"; |
|
39 local function format_line(k, v) |
|
40 return ("%s=%s\n"):format(k, v) |
|
41 end |
|
42 local actions = { |
|
43 load = function() |
|
44 local fd = io.open(factoids_file, "r"); |
|
45 if fd then |
|
46 for line in fd:lines() do |
|
47 local k, v = line:match("^(.+)=(.*)$"); |
|
48 if k and v then |
|
49 factoids[k] = v; |
|
50 end |
|
51 end |
|
52 fd:close(); |
|
53 end |
|
54 end, |
|
55 save = function() |
|
56 local fd = io.open(factoids_file, "w"); |
|
57 if fd then |
|
58 for k,v in pairs(factoids) do |
|
59 fd:write(format_line(k, v)); |
|
60 end |
|
61 fd:close(); |
|
62 end |
|
63 end, |
|
64 add = function(k, v) |
|
65 local fd = io.open(factoids_file, "a"); |
|
66 fd:write(format_line(k, v)); |
|
67 fd:close(); |
|
68 end, |
|
69 }; |
|
70 function actions.del(...) actions.save(); end |
|
71 actions.load(); |
|
72 setmetatable(factoids, { |
|
73 __newindex = function(t, k, v) |
|
74 v = v:gsub("\n"," "); |
|
75 rawset(t, k, v); |
|
76 actions.add(k, v); |
|
77 end, |
|
78 }); |
|
79 return actions; |
|
80 end |
|
81 |
|
82 -- A variant of the above except serializing lua code |
|
83 function storage_backends.lua(bot, factoids) |
|
84 local factoids_file = bot.config.factoids_file or "./factoids.dat"; |
|
85 local function format_line(k, v) |
|
86 return ("_G[%q] = %q;\n"):format(k, v); |
|
87 end |
|
88 local actions = { |
|
89 load = function() |
|
90 local chunk, err = loadfile(factoids_file); |
|
91 if chunk then |
|
92 local old_mt = getmetatable(factoids); |
|
93 setmetatable(factoids, { |
|
94 __newindex = function (...) rawset(...); print(...) end |
|
95 }); |
|
96 setfenv(chunk, {_G = factoids} ); |
|
97 chunk(); |
|
98 setmetatable(factoids, old_mt); |
|
99 else |
|
100 print(err); |
|
101 end |
|
102 end, |
|
103 save = function() |
|
104 local fd, err = io.open(factoids_file, "w"); |
|
105 if err then print(err) end |
|
106 if fd then |
|
107 for k,v in pairs(factoids) do |
|
108 fd:write(format_line(k, v)); |
|
109 end |
|
110 fd:close(); |
|
111 end |
|
112 end, |
|
113 add = function(k,v) |
|
114 local fd, err = io.open(factoids_file, "a"); |
|
115 if err then print(err) end |
|
116 if fd then |
|
117 fd:write(format_line(k, v)); |
|
118 fd:close(); |
|
119 end |
|
120 end, |
|
121 del = function(k) |
|
122 local fd, err = io.open(factoids_file, "a"); |
|
123 if err then print(err) end |
|
124 if fd then |
|
125 fd:write(("_G[%q] = nil;\n"):format(k)); |
|
126 fd:close(); |
|
127 end |
|
128 end, |
|
129 } |
|
130 actions.load(); |
|
131 setmetatable(factoids, { |
|
132 __newindex = function(t, k, v) |
|
133 print("__newindex", t, k, v); |
|
134 rawset(t, k, v); |
|
135 actions.add(k, v); |
|
136 end, |
|
137 }); |
|
138 return actions; |
|
139 end |
|
140 |
|
141 -- Yet another variant, using util.serialization |
|
142 function storage_backends.serialize(bot, factoids) |
|
143 local serialize = require "util.serialization".serialize; |
|
144 local deserialize = require "util.serialization".deserialize; |
|
145 local factoids_file = bot.config.factoids_file or "./factoids.dat"; |
|
146 local actions = { |
|
147 load = function() |
|
148 local fd = io.open(factoids_file, "r"); |
|
149 if fd then |
|
150 local old_mt = getmetatable(factoids); |
|
151 setmetatable(factoids, {}); |
|
152 factoids = deserialize(fd:read("*a")); |
|
153 setmetatable(factoids, old_mt); |
|
154 df:close(); |
|
155 end |
|
156 end, |
|
157 save = function() |
|
158 local fd, err = io.open(factoids_file, "w"); |
|
159 if err then print(err) end |
|
160 if fd then |
|
161 fd:write(serialize(factoids)); |
|
162 fd:close(); |
|
163 end |
|
164 end, |
|
165 } |
|
166 function actions.add(...) actions.save(); end |
|
167 function actions.del(...) actions.save(); end |
|
168 actions.load(); |
|
169 setmetatable(factoids, { |
|
170 __newindex = function(t, k, v) |
|
171 print("__newindex", t, k, v); |
|
172 rawset(t, k, v); |
|
173 actions.add(k, v); |
|
174 end, |
|
175 }); |
|
176 return actions; |
|
177 end |
|
178 |
|
179 -- A backend using XEP 49 storage |
|
180 function storage_backends.iq_private(bot, factoids) |
|
181 bot.stream:add_plugin("private"); |
|
182 local factoids_node = "factoids"; |
|
183 local factoids_xmlns = "http://code.zash.se/riddim/plugins/factoids"; |
|
184 local actions = { |
|
185 load = function() |
|
186 bot.stream:private_get(factoids_node, factoids_xmlns, function (storage) |
|
187 if storage then |
|
188 local old_mt = getmetatable(factoids); |
|
189 setmetatable(factoids, { |
|
190 __newindex = function (...) rawset(...); print(...) end |
|
191 }); |
|
192 for factoid in storage:children() do |
|
193 factoids[factoid.attr.name or "something-invalid"] = factoid:get_text(); |
|
194 end |
|
195 setmetatable(factoids, old_mt); |
|
196 end |
|
197 end); |
|
198 end, |
|
199 save = function() |
|
200 local st = verse.stanza(factoids_node, { xmlns = factoids_xmlns }); |
|
201 for name, text in pairs(factoids) do |
|
202 st:tag("factoid", { name = name }):text(text):up(); |
|
203 end |
|
204 bot.stream:private_set(factoids_node, factoids_xmlns, st); |
|
205 end, |
|
206 } |
|
207 function actions.add(...) actions.save(); end |
|
208 function actions.del(...) actions.save(); end |
|
209 bot:hook("started", function() |
|
210 actions.load(); |
|
211 setmetatable(factoids, { |
|
212 __newindex = function(t, k, v) |
|
213 print("__newindex", t, k, v); |
|
214 rawset(t, k, v); |
|
215 actions.add(k, v); |
|
216 end, |
|
217 }); |
|
218 end); |
|
219 return actions; |
|
220 end |