comparison mod_pubsub_twitter/mod_pubsub_twitter.lua @ 945:dbcbcec37d24

mod_pubsub_twitter: Plugin to subscribe (poll) to Twitter search queries, and announce matching tweets over pubsub
author Matthew Wild <mwild1@gmail.com>
date Wed, 03 Apr 2013 14:40:03 +0100
parents
children c8f4502c764f
comparison
equal deleted inserted replaced
944:21e81fcb8896 945:dbcbcec37d24
1 -- Publishes Twitter search results over pubsub
2 --
3 -- Config:
4 -- Component "pubsub.example.com" "pubsub"
5 -- modules_enabled = {
6 -- "pubsub_twitter";
7 -- }
8 -- twitter_searches = { -- node -> query
9 -- prosody = "prosody xmpp";
10 -- }
11 -- twitter_pull_interval = 20 -- minutes
12 --
13
14 local pubsub = module:depends"pubsub";
15
16 local json = require "util.json";
17 local http = require "net.http";
18 local set = require "util.set";
19 local it = require "util.iterators";
20 local array = require "util.array";
21
22 local st = require "util.stanza";
23 --local dump = require"util.serialization".serialize;
24
25 local xmlns_atom = "http://www.w3.org/2005/Atom";
26
27 local twitter_searches = module:get_option("twitter_searches", {});
28 local refresh_interval = module:get_option_number("twitter_pull_interval", 20) * 60;
29 local api_url = module:get_option_string("twitter_search_url", "http://search.twitter.com/search.json");
30
31 local month_number = {
32 Jan = "01", Feb = "02", Mar = "03";
33 Apr = "04", May = "05", Jun = "06";
34 Jul = "07", Aug = "08", Sep = "09";
35 Oct = "10", Nov = "11", Dec = "12";
36 };
37
38 local active_searches = {};
39
40 local function publish_result(search_name, result)
41 local node, id = search_name, result.id_str;
42 --"Tue, 02 Apr 2013 15:40:54 +0000"
43 local timestamp_date, timestamp_month, timestamp_year, timestamp_time =
44 result.created_at:match(" (%d+) (%a+) (%d+) (%d%d:%d%d:%d%d)");
45
46 local timestamp = ("%s-%s-%sT%sZ"):format(timestamp_year, month_number[timestamp_month], timestamp_date, timestamp_time);
47
48 local item = st.stanza("entry", { xmlns = xmlns_atom })
49 :tag("id"):text(id):up()
50 :tag("author")
51 :tag("name"):text(result.from_user_name.." (@"..result.from_user..")"):up()
52 :tag("uri"):text("http://twitter.com/"..result.from_user):up()
53 :up()
54 :tag("published"):text(timestamp):up()
55 :tag("title"):text(result.text):up()
56 :tag("link", { rel = "alternate" , href = "https://twitter.com/"..result.from_user.."/status/"..id}):up();
57
58 module:log("debug", "Publishing Twitter result: %s", tostring(item));
59
60 local ok, err = pubsub.service:publish(node, true, id, item);
61 if not ok then
62 if err == "item-not-found" then -- try again
63 local ok, err = pubsub.service:create(node, true);
64 if not ok then
65 module:log("error", "could not create node %s: %s", node, err);
66 return;
67 end
68 local ok, err = pubsub.service:publish(node, true, id, item);
69 if not ok then
70 module:log("error", "could not create or publish node %s: %s", node, err);
71 return
72 end
73 else
74 module:log("error", "publishing %s failed: %s", node, err);
75 end
76 end
77 end
78
79 local function is_retweet(tweet)
80 return not not tweet.text:match("^RT ");
81 end
82
83 function update_all()
84 module:log("debug", "Updating all searches");
85 for name, search in pairs(active_searches) do
86 module:log("debug", "Fetching new results for '%s'", name);
87 http.request(search.refresh_url or search.url, nil, function (result_json, code)
88 if code ~= 200 then
89 module:log("warn", "Twitter search query '%s' failed with code %d", name, code);
90 return;
91 end
92 local response = json.decode(result_json);
93 module:log("debug", "Processing %d results for %s", #response.results, name);
94 search.refresh_url = api_url..response.refresh_url;
95 for _, result in ipairs(response.results) do
96 if not is_retweet(result) then
97 publish_result(name, result);
98 end
99 end
100 end);
101 end
102 return refresh_interval;
103 end
104
105 function module.load()
106 local config_searches = set.new(array.collect(it.keys(twitter_searches)));
107 local current_searches = set.new(array.collect(it.keys(active_searches)));
108
109 local disable_searches = current_searches - config_searches;
110 local new_searches = config_searches - current_searches;
111
112 for search_name in disable_searches do
113 module:log("debug", "Disabled old Twitter search '%s'", search_name);
114 active_searches[search_name] = nil;
115 end
116
117 for search_name in new_searches do
118 module:log("debug", "Created new Twitter search '%s'", search_name);
119 local query = twitter_searches[search_name];
120 active_searches[search_name] = {
121 url = api_url.."?q="..http.urlencode(query);
122 };
123 end
124 end
125
126 module:add_timer(5, update_all);