comparison mod_storage_muc_log/mod_storage_muc_log.lua @ 1566:9158882dd9a1

mod_storage_muc_log: Provides an archive API to mod_muc_log data
author Kim Alvefur <zash@zash.se>
date Mon, 10 Nov 2014 14:41:01 +0100
parents
children c357039c1ab1
comparison
equal deleted inserted replaced
1565:f9cb09d451c7 1566:9158882dd9a1
1
2 local datamanager = require"core.storagemanager".olddm;
3 local xml_parse = require"util.xml".parse;
4 local data_load, data_store = datamanager.load, datamanager.store;
5 local datastore = "muc_log";
6 local datetime = require"util.datetime"
7 local lfs = require"lfs";
8 local noop = function () end;
9 local os_date = os.date;
10
11 local timef, datef = "!%X", "!%y%m%d";
12 local host = module.host;
13
14 local driver = {};
15 local driver_mt = { __index = driver };
16
17 do
18 -- Sanity check
19 -- Fun fact: 09:00 and 21:00 en_HK are both "09:00:00 UTC"
20 local t = os_date("!*t");
21 t.hour = 9;
22 local am = os_date(timef, os.time(t));
23 t.hour = 21;
24 local pm = os_date(timef, os.time(t));
25 if am == pm then
26 module:log("warn", "Timestamps in AM and PM are identical in your locale, expect timestamps to be wrong");
27 end
28 end
29
30 local function parse_silly(date, time)
31 local year, month, day = date:match("^(%d%d)(%d%d)(%d%d)");
32 year = "20"..year;
33 -- year = (year < "70" and "20" or "19") .. year;
34 local hour, min, sec = time:match("(%d%d)%D+(%d%d)%D+(%d%d)");
35 if hour == "12" and time:find("[Aa][Mm]") then
36 hour = "00";
37 elseif hour < "12" and time:find("[Pp][Mm]") then
38 hour = tostring(tonumber(hour) % 12 + 12);
39 end
40 return datetime.parse(("%s-%s-%sT%s:%s:%sZ"):format(year, month, day, hour or "00", min or "00", sec or "00"));
41 end
42
43 local function st_with(tag)
44 local with = tag.attr.type;
45 return with and tag.name .. "<" .. with or tag.name;
46 end
47
48 function driver:append(node, key, when, with, stanza)
49 local today = os_date(datef, when);
50 local now = os_date(timef, when);
51 local data = data_load(node, host, datastore .. "/" .. today) or {};
52 data[#data + 1] = "<stanza time=\"".. now .. "\">" .. tostring(stanza) .. "</stanza>\n";
53 datamanager.getpath(node, host, datastore, nil, true); -- create the datastore dir
54 local ok, err = data_store(node, host, datastore .. "/" .. today, data);
55 if not ok then
56 return ok, err;
57 end
58 return today .. "_" .. #data;
59 end
60
61 function driver:find(node, query)
62 local path = datamanager.getpath(node, host, datastore):match("(.*)/");
63
64 local ok, iter, state, var = pcall(lfs.dir, path);
65 if not ok then
66 module:log("warn", iter);
67 return nil, iter;
68 end
69
70 local dates, i = {}, 1;
71 for dir in iter, state, var do
72 if lfs.attributes(datamanager.getpath(node, host, datastore .. "/" .. dir), "mode") == "file" then
73 dates[i], i = dir, i+1;
74 end
75 end
76 if dates[1] == nil then return noop, 0; end
77 table.sort(dates);
78
79 return coroutine.wrap(function ()
80 local query = query;
81 local dates = dates;
82 local start_date = query and query.start and os_date(datef, query.start) or dates[1];
83 local end_date = query and query["end"] and os_date(datef, query["end"]) or dates[#dates];
84 local start_time = query and query.start and os_date(timef, query.start) or dates[1];
85 local end_time = query and query["end"] and os_date(timef, query["end"]) or dates[#dates];
86 local query_with = query and query.with;
87 local query_limit = query and query.limit;
88 local seek_once = query and query.after;
89
90 local today, time, data, err, item;
91 local inner_start, inner_stop, inner_step;
92 local outer_start, outer_stop, outer_step = 1, #dates, 1;
93 if query and query.reverse then
94 outer_start, outer_stop, outer_step = outer_stop, outer_start, -outer_step;
95 seek_once = query.before;
96 if seek_once then
97 end_date = seek_once:match"^(%d+)_%d";
98 end
99 elseif seek_once then
100 start_date = seek_once:match"^(%d+)_%d";
101 end
102 local matches = 0;
103 for i = outer_start, outer_stop, outer_step do
104 today = dates[i];
105 if today >= start_date and today <= end_date then
106 data, err = data_load(node, host, datastore .. "/" .. today);
107 if data then
108 inner_start, inner_stop, inner_step = 1, #data, 1;
109 if query and query.reverse then
110 inner_start, inner_stop, inner_step = inner_stop, inner_start, -inner_step;
111 end
112 if seek_once then
113 inner_start = tonumber(seek_once:match("_(%d+)$"));
114 inner_start = inner_start + (query and query.reverse and -1 or 1);
115 seek_once = nil;
116 end
117 for i = inner_start, inner_stop, inner_step do
118 item, err = data[i];
119 if item then
120 item, err = xml_parse(item);
121 end
122 if item then
123 time = item.attr.time;
124 item = item.tags[1];
125 if (today >= start_date or time >= start_time) and
126 (today <= end_date or time <= end_time) and
127 (not query_with or query_with == st_with(item)) and
128 item:get_child_text("alreadyJoined") ~= "true" then
129 matches = matches + 1;
130 coroutine.yield(today.."_"..i, item, parse_silly(today, time));
131 if query_limit and matches >= query_limit then
132 return;
133 end
134 end
135 elseif err then
136 module:log("warn", err);
137 end
138 end
139 elseif err then
140 module:log("warn", err);
141 end
142 end
143 end
144 end);
145 end
146
147 function open(_, store, typ)
148 if typ ~= "archive" then
149 return nil, "unsupported-store";
150 end
151 return setmetatable({ store = store, type = typ }, driver_mt);
152 end
153
154 module:provides "storage";