1 local io = io; |
1 local io, string = io, string; |
|
2 local error = error; |
|
3 local print = print; |
|
4 |
2 local http = require "socket.http"; |
5 local http = require "socket.http"; |
3 local st = require "stanza"; |
6 local st = require "stanza"; |
4 local new_stream = require "xmppstream".new; |
7 local new_stream = require "xmppstream".new; |
5 |
8 |
6 local xmlns_atom = "http://www.w3.org/2005/Atom"; |
9 local xmlns_atom = "http://www.w3.org/2005/Atom"; |
7 |
10 |
8 module "feeds" |
11 module "feeds" |
9 |
12 |
|
13 local translate_entry = {}; |
|
14 |
|
15 function translate_entry.atom(feed, stanza) |
|
16 if stanza.name == "entry" then |
|
17 feed[#feed+1] = stanza; |
|
18 else |
|
19 feed[stanza.name] = stanza:get_text(); |
|
20 end |
|
21 end |
|
22 |
|
23 -- RSS->Atom translator |
|
24 |
|
25 -- Helpers to translate item child elements |
|
26 local rss2atom = {}; |
|
27 function rss2atom.title(atom_entry, tag) |
|
28 atom_entry:tag("title"):text(tag:get_text()):up(); |
|
29 end |
|
30 |
|
31 function rss2atom.link(atom_entry, tag) |
|
32 atom_entry:tag("link", { href = tag:get_text() }):up(); |
|
33 end |
|
34 |
|
35 function rss2atom.author(atom_entry, tag) |
|
36 atom_entry:tag("author") |
|
37 :tag("email"):text(tag:get_text()):up() |
|
38 :up(); |
|
39 end |
|
40 |
|
41 function rss2atom.guid(atom_entry, tag) |
|
42 atom_entry:tag("id"):text(tag:get_text()):up(); |
|
43 end |
|
44 |
|
45 function rss2atom.category(atom_entry, tag) |
|
46 atom_entry:tag("category", { term = tag:get_text(), scheme = tag.attr.domain }):up(); |
|
47 end |
|
48 |
|
49 function rss2atom.description(atom_entry, tag) |
|
50 atom_entry:tag("summary"):text(tag:get_text()):up(); |
|
51 end |
|
52 |
|
53 local months = { |
|
54 jan = "01", feb = "02", mar = "03", apr = "04", may = "05", jun = "06"; |
|
55 jul = "07", aug = "08", sep = "09", oct = "10", nov = "11", dec = "12"; |
|
56 }; |
|
57 |
|
58 function rss2atom.pubDate(atom_entry, tag) |
|
59 local pubdate = tag:get_text():gsub("^%a+,", ""):gsub("^%s*", ""); |
|
60 local date, month, year, hour, minute, second, zone = |
|
61 pubdate:match("^(%d%d?) (%a+) (%d+) (%d+):(%d+):?(%d*) ?(.*)$"); |
|
62 if not date then return; end |
|
63 if #date == 1 then |
|
64 date = "0"..date; |
|
65 end |
|
66 month = months[month:sub(1,3):lower()]; |
|
67 if #year == 2 then -- GAH! |
|
68 if tonumber(year) > 80 then |
|
69 year = "19"..year; |
|
70 else |
|
71 year = "20"..year; |
|
72 end |
|
73 end |
|
74 if zone == "UT" or zone == "GMT" then zone = "Z"; end |
|
75 if #second == 0 then |
|
76 second = "00"; |
|
77 end |
|
78 local date_string = string.format("%s-%s-%sT%s:%s:%s%s", year, month, date, hour, minute, second, zone); |
|
79 atom_entry:tag("published"):text(date_string):up(); |
|
80 end |
|
81 |
|
82 -- Translate a single item to atom |
|
83 function translate_entry.rss(feed, stanza) |
|
84 if stanza.name == "item" then |
|
85 local atom_entry = st.stanza("entry", { xmlns = xmlns_atom }); |
|
86 for tag in stanza:childtags() do |
|
87 local translator = rss2atom[tag.name]; |
|
88 if translator then |
|
89 translator(atom_entry, tag); |
|
90 end |
|
91 end |
|
92 translate_entry.atom(feed, atom_entry:reset()); |
|
93 else |
|
94 translate_entry.atom(feed, stanza); |
|
95 end |
|
96 end |
|
97 |
10 local function new_feed_stream(feed) |
98 local function new_feed_stream(feed) |
11 local callbacks = { |
99 local callbacks = { |
12 default_ns = xmlns_atom; |
100 streamopened = function (feed, attr, name) |
13 stream_ns = xmlns_atom; stream_tag = "feed"; |
101 if name == "feed" and attr.xmlns == xmlns_atom then |
14 |
102 feed.type = "atom"; |
15 streamopened = function (feed, attr) |
103 feed.xmlns = xmlns_atom; |
16 feed.notopen = nil; |
104 feed.notopen = nil; |
|
105 elseif name == "rss" and attr.xmlns == "" then |
|
106 feed.type = "rss"; |
|
107 feed.xmlns = ""; |
|
108 -- Don't open until channel |
|
109 elseif feed.type == "rss" and name == "channel" and attr.xmlns == feed.xmlns then |
|
110 feed.notopen = nil; |
|
111 else |
|
112 error("Unsupported feed type: <" |
|
113 ..name |
|
114 ..(attr.xmlns and (" xmlns='"..attr.xmlns.."'") or "") |
|
115 ..">" |
|
116 ); |
|
117 end |
17 end; |
118 end; |
18 |
119 |
19 streamclosed = function (feed) |
120 streamclosed = function (feed) |
20 end; |
121 end; |
21 |
122 |
22 handlestanza = function (feed, stanza) |
123 handlestanza = function (feed, stanza) |
23 -- Skip tags not in the feed's default namespace |
124 -- Skip tags not in the feed's default namespace |
24 if stanza.attr.xmlns ~= nil then |
125 if stanza.attr.xmlns ~= feed.xmlns then |
25 return; |
126 return; |
26 end |
127 end |
27 |
128 |
28 if stanza.name == "entry" then |
129 translate_entry[feed.type](feed, stanza); |
29 feed[#feed+1] = stanza; |
130 end; |
30 else |
131 |
31 feed[stanza.name] = stanza:get_text(); |
132 error = function (feed, err, d) |
32 end |
133 error(err..": "..d); |
33 end; |
134 end; |
34 }; |
135 }; |
35 |
136 |
36 return new_stream(feed, callbacks); |
137 return new_stream(feed, callbacks); |
37 end |
138 end |