Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: anoved/WheresThatSat
base: 25192adf29
...
head fork: anoved/WheresThatSat
compare: f638141e11
Checking mergeability… Don't worry, you can still create the pull request.
  • 3 commits
  • 1 file changed
  • 0 commit comments
  • 1 contributor
Commits on May 01, 2012
@anoved Stripped out API call counting -
- in preparation for switch to using the 'twitter' gem instead of 'chatterbot'. The twitter gem supports querying the rate limit directly.
2590bb3
@anoved Factored tweet posting out of search/mention loop and into respondToT…
…weet itself
02278be
Commits on May 02, 2012
@anoved First draft of using Twitter gem instead of Chatterbot. f638141
Showing with 101 additions and 176 deletions.
  1. +101 −176 WheresThatSat.rb
View
277 WheresThatSat.rb
@@ -7,11 +7,7 @@
end
require 'rubygems'
-require 'chatterbot/dsl'
-
-# Ignore our own tweets to prevent a silly cycle of self-replies
-blacklist "wheresthatsat"
-
+require 'twitter'
require 'yaml'
require 'cgi'
require 'geocoder'
@@ -36,7 +32,7 @@ class WTSObserver
#
def parseTweetPlaceTag(tweet)
geo = nil
- if (tweet[:text].match(/\#place "([^"]+)"/i))
+ if (tweet.text.match(/\#place "([^"]+)"/i))
geoquery = $1
geocode = Geocoder.search(geoquery)
if (geocode.length > 0)
@@ -45,23 +41,25 @@ def parseTweetPlaceTag(tweet)
geo.lon = geocode[0].longitude
geo.name = "\"#{geoquery}\""
end
- elsif (tweet[:geo] != nil)
- geo = WTSObserver.new
- geo.lat = tweet[:geo][:coordinates][0]
- geo.lon = tweet[:geo][:coordinates][1]
- user = from_user(tweet)
- if (user[-1,1] == 's')
- geo.name = "#{user}' coordinates"
- else
- geo.name = "#{user}'s coordinates"
- end
- elsif (tweet[:place] != nil)
- geo = WTSObserver.new
- bbox = tweet[:place][:bounding_box][:coordinates][0]
- geo.lat = (bbox[0][1] + bbox[2][1]) / 2.0
- geo.lon = (bbox[0][0] + bbox[2][0]) / 2.0
- geo.name = tweet[:place][:name]
end
+## Need to adjust to suit twitter gem's geo/place format
+# elsif (tweet[:geo] != nil)
+# geo = WTSObserver.new
+# geo.lat = tweet[:geo][:coordinates][0]
+# geo.lon = tweet[:geo][:coordinates][1]
+# user = from_user(tweet)
+# if (user[-1,1] == 's')
+# geo.name = "#{user}' coordinates"
+# else
+# geo.name = "#{user}'s coordinates"
+# end
+# elsif (tweet[:place] != nil)
+# geo = WTSObserver.new
+# bbox = tweet[:place][:bounding_box][:coordinates][0]
+# geo.lat = (bbox[0][1] + bbox[2][1]) / 2.0
+# geo.lon = (bbox[0][0] + bbox[2][0]) / 2.0
+# geo.name = tweet[:place][:name]
+# end
return geo
end
@@ -173,42 +171,14 @@ def theresThatSat(satellite_name, tle_data, user_name, tweet_id, mention_time, r
end
# return complete reply text
- reply_text = format "#USER# When you mentioned %s, it was above %.4f%s %.4f%s. Here's more info: %s",
- satellite_name, mention_lat.abs, mention_lat >= 0 ? "N" : "S", mention_lon.abs, mention_lon >= 0 ? "E" : "W", url
+ reply_text = format "@%s When you mentioned %s, it was above %.4f%s %.4f%s. Here's more info: %s",
+ user_name, satellite_name, mention_lat.abs, mention_lat >= 0 ? "N" : "S", mention_lon.abs, mention_lon >= 0 ? "E" : "W", url
end
#
# Parameters:
-# created_at, string representation of search result time: Fri, 13 Apr 2012 13:06:22 +0000
-#
-# Returns:
-# UTC Time object
-#
-def parseSearchTimestamp(created_at)
- parts = created_at.split(' ')
- hms = parts[4].split(':')
- return Time.utc(parts[3], parts[2], parts[1], hms[0], hms[1], hms[2], 0)
-end
-
-#
-# Parameters:
-# created_at, string representation of reply/mention time: Fri Apr 13 13:06:22 +0000 2012
-#
-# Returns:
-# UTC Time object
-#
-def parseReplyTimestamp(created_at)
- parts = created_at.split(' ')
- hms = parts[3].split(':')
- return Time.utc(parts[5], parts[1], parts[2], hms[0], hms[1], hms[2], 0)
-end
-
-#
-# NOTE: currently responds to first recognized satellite name found in tweet,
-# and ignores any others. Preferred behavior is to reply to all known references.
-#
-# Parameters:
+# twitter connection
# tweetText, text of tweet
# tweetId, id of tweet
# tweetTimestamp, time of tweet
@@ -218,15 +188,17 @@ def parseReplyTimestamp(created_at)
# (if selectedSatellites is empty, respond to any satellite name in catalog)
#
# Returns:
-# string containing tweet response text (including map link),
-# or nil if no satellite name is recognized in tweetText
+# number of responses posted to tweet. (Maybe be zero if no satellite names
+# were matched, or more than one if there were multiple matches)
#
-def getTweetResponse(tweetText, tweetId, tweetTimestamp, userName, location, selectedSatellites=[])
+def respondToTweet(twitter, tweetText, tweetId, tweetTimestamp, userName, location, selectedSatellites=[])
if selectedSatellites.empty?
selectedSatellites = $catalog[:tle].keys
end
+ responseCount = 0
+
selectedSatellites.each do |satelliteName|
# match hyphenated or non-hyphenated forms of satellite_name
@@ -239,154 +211,92 @@ def getTweetResponse(tweetText, tweetId, tweetTimestamp, userName, location, sel
# access other tweet properties (such as geo or place) besides text
tweetTimestamp, hasTimeTag = parseTweetTimeTag(tweetText, tweetTimestamp)
- return theresThatSat(satelliteName, $catalog[:tle][satelliteName],
+ response = theresThatSat(satelliteName, $catalog[:tle][satelliteName],
userName, tweetId, tweetTimestamp.to_i, responseTimestamp.to_i,
hasTimeTag, location)
+
+ if $testmode
+ puts response
+ else
+ twitter.update(response, :in_reply_to_status_id => tweetId)
+ end
+
+ responseCount += 1
end
end
- return nil
+ return responseCount
end
-
#
-# Parameters:
-# acc_available, number of API calls available
-# search_quota, cutoff for how many search-related api calls to perform,
-# regardless of how many calls are available. will not perform more
-# than min of search_quota and acc_available.
+# Parameter:
+# twitter connection
+# since_id, only consider search results since this tweet
#
# Results:
# posts replies to search results
#
# Returns:
-# number of API calls consumed (acc)
+# id of most recent search result
#
-def respondToSearches(acc_available, search_quota=20)
-
- if (acc_available < search_quota) then search_quota = acc_available end
-
+def respondToSearches(twitter, since_id)
+ max = since_id
+
# load the list of satellite names to search for
satellite_queries = YAML.load_file('config/sat_searches.yml')
if satellite_queries == nil then return 0 end
# assemble the list of names into a single OR query w/each name quoted
- query_text = satellite_queries.map {|name| "\"#{name}\""}.join(' OR ')
+ searchQuery = satellite_queries.map {|name| "\"#{name}\""}.join(' OR ')
- acc = 1
- search(query_text) do |tweet|
+ searchResults = twitter.search(searchQuery, :since_id => since_id, :result_type => "recent")
+
+ searchResults.each do |tweet|
+ if tweet.id > max then max = tweet.id end
+ if (tweetAuthor = getTweetAuthor(tweet)) == 'WheresThatSat' then next end
# skip any results that refer to us: they're handled as Mentions
- if tweet[:text].match(/@WheresThatSat/i) then next end
-
- response = getTweetResponse(
- tweet[:text], tweet[:id],
- parseSearchTimestamp(tweet[:created_at]),
- from_user(tweet), parseTweetPlaceTag(tweet),
- satellite_queries)
-
- # a nil response indicates no satellite was mentioned
- # (more accurately - Twitter returned a match for our query, but we
- # couldn't find any matches, indicating some matching discrepancy)
- if response == nil then next end
-
- if $testmode
- puts response
- else
- if (acc + 1 >= search_quota)
- puts STDERR, format("Not responding to search %s: rate limit/quota.", tweet[:id].to_s)
- return acc
- end
- acc += 1
- reply response, tweet
- end
+ if tweet.text.match(/@WheresThatSat/i) then next end
+ respondToTweet(twitter, tweet.text, tweet.id, tweet.created_at.utc,
+ tweetAuthor, parseTweetPlaceTag(tweet), satellite_queries)
end
-
- return acc
+ return max
end
#
-# Parameters:
-# acc_available, number of API calls available
+# Parameter:
+# twitter connection
+# since_id, only consider mentions since this tweet
#
# Results:
# posts replies to mentions
#
# Returns:
-# number of API calls consumed (acc)
-#
-def respondToMentions(acc_available)
- if (acc_available == 0)
- puts STDERR, "Not responding to mentions: rate limit"
- return 0
- end
- acc = 1
- replies do |tweet|
-
+# id of most recent mention
+#
+def respondToMentions(twitter, since_id)
+ max = since_id
+ mentions = twitter.mentions(:since_id => since_id)
+ mentions.each do |tweet|
+ if tweet.id > max then max = tweet.id end
+ if (tweetAuthor = getTweetAuthor(tweet)) == 'WheresThatSat' then next end
+
# To avoid redundant replies to retweets/quotes of our own tweets,
# ignore mentions that aren't actually direct @replies.
- if !tweet[:text].match(/^@WheresThatSat/i) then next end
-
- response = getTweetResponse(
- tweet[:text], tweet[:id],
- parseReplyTimestamp(tweet[:created_at]),
- from_user(tweet), parseTweetPlaceTag(tweet))
+ if !tweet.text.match(/^@WheresThatSat/i) then next end
- # a nil response indicates no satellite was mentioned
- if response == nil then next end
-
- if ($testmode)
- # In test mode, just print the response for inspection.
- puts response
- else
- # Otherwise, post the response in reply to the input Tweet.
- if (acc + 1 >= acc_available)
- puts STDERR, format("Not responding to mention %s or earlier: rate limit.", tweet[:id].to_s)
- return acc
- end
- acc += 1
- reply response, tweet
- end
+ respondToTweet(twitter, tweet.text, tweet.id, tweet.created_at.utc,
+ tweetAuthor, parseTweetPlaceTag(tweet))
end
- return acc
+ return max
end
-#
-# Returns:
-# current API call count (cumulative for the past hour, or whatever
-# period is represented by the set of per-interval call counts in :intervals)
-#
-def readAPICallCount()
- acc_intervals = YAML.load_file("config/intervals.yml")[:intervals]
- return acc_intervals.inject(0) {|sum, value| sum + value}
+def getTweetAuthor(tweet)
+ return tweet.from_user || tweet.user.screen_name
end
#
-# Parameters:
-# acc, call count for this run
-#
-# Results:
-# updates interval file (pushes acc into the record and discards oldest interval count)
-#
-# Returns:
-# final call count
-#
-def writeAPICallCount(acc)
-
- intervals = YAML.load_file("config/intervals.yml")[:intervals]
-
- # drop all but the most recent five intervals from the list of intervals
- # (assuming a tracking period of six intervals - 6 x 10 minutes = 1 hour)
- while intervals.length > 5 do intervals.shift end
-
- # add the most recent count to the interval list
- intervals.push acc
-
- File.open("config/intervals.yml", "w") {|file| YAML.dump({:intervals => intervals}, file)}
-
- return intervals.inject(0) {|sum, value| sum + value}
-end
-
+# NOTE: this doesn't really need to store catalog as a global variable
#
# Parameters:
# catalog_path, path to satellite catalog file (generated by UpdateCatalog.rb)
@@ -408,18 +318,33 @@ def loadSatelliteCatalog(catalog_path='config/catalog.yml')
end
end
-loadSatelliteCatalog()
-
-ac_initial = readAPICallCount()
-ac_available = 150 - ac_initial
-ac_consumed = 0
+#
+# Parameters:
+# twitter_path, path to YAML file containing Twitter OAuth credentials
+#
+# Returns:
+# authenticated Twitter object
+#
+def loginToTwitter(twitter_path='config/twitter.yml')
+ credentials = YAML.load_file(twitter_path)
+ return Twitter.new(
+ :oauth_token => credentials[:oauth_token],
+ :oauth_token_secret => credentials[:oauth_token_secret],
+ :consumer_key => credentials[:consumer_key],
+ :consumer_secret => credentials[:consumer_secret]);
+end
-acc = respondToMentions(ac_available)
-ac_consumed += acc
-ac_available -= acc
+def readSinceId(since_path='config/since.yml')
+ return YAML.load_file(since_path)
+end
-acc = respondToSearches(ac_available)
-ac_consumed += acc
-ac_available -= acc
+def writeSinceId(since_id, since_path='config/since.yml')
+ File.open(since_path, 'w') {|f| YAML.dump(since_id, f)}
+end
-writeAPICallCount(ac_consumed)
+loadSatelliteCatalog
+twitter = loginToTwitter
+since_id = readSinceId
+mentionLastId = respondToMentions(twitter, since_id)
+searchLastId = respondToSearches(twitter, since_id)
+writeSinceId([since_id, mentionLastId, searchLastId].max)

No commit comments for this range

Something went wrong with that request. Please try again.