plugins/xkcd2.lua

1
local http = require("prosody.net.http");
2
local json = require"prosody.util.json";
3
local bare_jid = require "prosody.util.jid".bare;
4
 
5
local current_url = "http://xkcd.com/info.0.json";
6
local numbered_url = "http://xkcd.com/%d/info.0.json";
7
local formatted_url = "http://xkcd.com/%d/";
8
 
9
local strips = {};
10
local latest, last_update;
11
 
12
function riddim.plugins.xkcd2(bot)
13
	bot:hook("commands/xkcd", function(command)
14
		local url = current_url;
15
		local q = command.param;
16
		local strip;
17
 
18
		if q then
19
			local t, num = q:match("^([#\"]?)(%-?%d+)\"?$");
20
			if t ~= '"' then
21
				num = tonumber(num);
22
				if num and num < 0 then
23
					num = table.maxn(strips) + num;
24
				end
25
			end
26
			strip = strips[num or q:lower()];
27
			if strip == "" or strip == 404 then
28
				strip = nil
29
			end
30
			if not strip and num then
31
				url = numbered_url:format(num) or url;
32
			end
33
		elseif os.difftime(os.time(), last_update) <= 3 * 60 * 60 then
34
			strip = latest;
35
		end
36
 
37
		if q and not strip then
38
			local pat = q:lower():gsub("[-()%[]", "%%%0")
39
				:gsub("%%(%b[])",function(s) return (#s > 2 and "" or "%") .. s end);
40
			local results = {};
41
 
42
			for i, strip in ipairs(strips) do
43
				if strip:lower():match(pat) then
44
					results[#results+1] = i;
45
				end
46
			end
47
 
48
			if #results == 0 then
49
				return "Sorry, I couldn't find a match";
50
			elseif #results == 1 then
51
				strip = results[1];
52
			else
53
				-- We have more than one match
54
				local ret, title = "Multiple matches:";
55
				for i, num in ipairs(results) do
56
					title = strips[num];
57
					ret = string.format("%s %s (%d)%s", ret, title, num, ((i < #results) and ",") or "");
58
					if i > 5 then ret = ret .. " " .. (#results - 5) .. " more"; break; end
59
				end
60
				return ret;
61
			end
62
		end
63
 
64
		if strip then
65
			local t, n;
66
			if type(strip) == "number" then
67
				t, n = strips[strip], strip;
68
			else
69
				t, n = strip, strips[strip:lower()];
70
			end
71
			return ("%s, "..formatted_url.." "):format(t, n);
72
		end
73
 
74
		http.request(url, nil, function (data, code)
75
			if code == 200 then
76
				data = json.decode(data);
77
				if not data then return end
78
				local n, t = tonumber(data.num), data.safe_title;
79
				strips[n], strips[t:lower()] = t, n;
80
				if not q then
81
					latest = n;
82
					last_update = os.time();
83
				end
84
				command:reply(("%s, "..formatted_url.." "):format(t, n));
85
			elseif code == 404 then
86
				command:reply("Strip not found");
87
			end
88
		end);
89
		return true;
90
 
91
	end);
92
 
93
	local admin = bot.config.admin;
94
	bot:hook("commands/xkcdlist", function(command)
95
 
96
		local actor = bare_jid(command.sender.real_jid or command.sender.jid);
97
		if actor ~= admin then
98
			return "I shall not";
99
		end
100
 
101
		local get_next;
102
		local function handle_reply(data, code)
103
			if code == 200 then
104
				data = json.decode(data);
105
				if not data then return end
106
				local n, t = tonumber(data.num), data.safe_title;
107
				strips[n], strips[t:lower()] = t, n;
108
				if n > 1 then
109
					return get_next(n - 1);
110
				end
111
			end
112
		end
113
		function get_next(i)
114
			if strips[i] then return end
115
			local url = i and numbered_url:format(i) or current_url;
116
			http.request(url, nil, handle_reply);
117
		end
118
		get_next(tonumber(command.param));
119
		return true;
120
	end);
121
 
122
	local function do_load()
123
		local ok, loaded_strips = pcall(dofile,"xkcd-list.lua");
124
		if ok then
125
			strips = loaded_strips;
126
			return true;
127
		else
128
			return nil, loaded_strips;
129
		end
130
	end
131
 
132
 
133
	bot:hook("commands/xkcdsave", function(command)
134
 
135
		local actor = bare_jid(command.sender.real_jid or command.sender.jid);
136
		if actor ~= admin then
137
			return "I shall not";
138
		end
139
 
140
		local dump = "return "..require"prosody.util.serialization".serialize(strips);
141
		local f, err = io.open("xkcd-list.lua~", "w");
142
		if f then
143
			f:write(dump);
144
			f:close();
145
			os.rename("xkcd-list.lua~", "xkcd-list.lua");
146
			return "Saved list of strips";
147
		end
148
		return err;
149
	end);
150
 
151
	bot:hook("commands/xkcdload", function(command)
152
 
153
		local actor = bare_jid(command.sender.real_jid or command.sender.jid);
154
		if actor ~= admin then
155
			return "I shall not";
156
		end
157
 
158
		local ok, err = do_load()
159
		if not ok then return err end
160
		return "List of strips loaded"
161
	end);
162
 
163
	bot:hook("commands/xkcdreset", function(command)
164
 
165
		local actor = bare_jid(command.sender.real_jid or command.sender.jid);
166
		if actor ~= admin then
167
			return "I shall not";
168
		end
169
 
170
		strips = {};
171
		return "List of strips emptied";
172
	end);
173
 
174
	do_load();
175
end