Mercurial > prosody-modules
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); |
