tweetomator 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +69 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +53 -0
- data/LICENSE.txt +22 -0
- data/README.md +37 -0
- data/Rakefile +9 -0
- data/lib/clients/flickr_client.rb +21 -0
- data/lib/clients/twitter_client.rb +32 -0
- data/lib/composer.rb +54 -0
- data/lib/config/config.rb +50 -0
- data/lib/finders/emoji_finder.rb +11 -0
- data/lib/finders/flickr_findr.rb +10 -0
- data/lib/finders/tweet_finder.rb +29 -0
- data/lib/io/downloader.rb +12 -0
- data/lib/tweet.rb +26 -0
- data/lib/tweetomator.rb +75 -0
- data/lib/tweetomator/version.rb +3 -0
- data/test/lib/composer_test.rb +40 -0
- data/test/lib/finders/flickr_findr_test.rb +16 -0
- data/test/lib/finders/tweet_finder_test.rb +28 -0
- data/tweetomator.gemspec +28 -0
- metadata +153 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 825073922927da43ac05a2c1a35dcd873121991c
|
4
|
+
data.tar.gz: 0d1d48bf51e633eec1ad54cd6a3866497650e5ed
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 99538aab29a3f37c9747c3010658858d121c92dfe7c9522d8e1f71e04a9b6b773bcea4790c066403dabfe85cb9d80be9cba9d3ac70b1b0863da6d3a97c3d92f7
|
7
|
+
data.tar.gz: 375e71144fcce36676bb8faf358d873825b8bc73cad01384bea4334f0781bbf0c86119ee58dcdb4151e585b813356c29deb4642d82ca2f1a2ad5cb93de180020
|
data/.gitignore
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion
|
2
|
+
|
3
|
+
*.iml
|
4
|
+
|
5
|
+
## Directory-based project format:
|
6
|
+
.idea/
|
7
|
+
# if you remove the above rule, at least ignore the following:
|
8
|
+
|
9
|
+
# User-specific stuff:
|
10
|
+
# .idea/workspace.xml
|
11
|
+
# .idea/tasks.xml
|
12
|
+
# .idea/dictionaries
|
13
|
+
|
14
|
+
# Sensitive or high-churn files:
|
15
|
+
# .idea/dataSources.ids
|
16
|
+
# .idea/dataSources.xml
|
17
|
+
# .idea/sqlDataSources.xml
|
18
|
+
# .idea/dynamic.xml
|
19
|
+
# .idea/uiDesigner.xml
|
20
|
+
|
21
|
+
# Gradle:
|
22
|
+
# .idea/gradle.xml
|
23
|
+
# .idea/libraries
|
24
|
+
|
25
|
+
# Mongo Explorer plugin:
|
26
|
+
# .idea/mongoSettings.xml
|
27
|
+
|
28
|
+
## File-based project format:
|
29
|
+
*.ipr
|
30
|
+
*.iws
|
31
|
+
|
32
|
+
## Plugin-specific files:
|
33
|
+
|
34
|
+
# IntelliJ
|
35
|
+
/out/
|
36
|
+
|
37
|
+
# mpeltonen/sbt-idea plugin
|
38
|
+
.idea_modules/
|
39
|
+
|
40
|
+
# JIRA plugin
|
41
|
+
atlassian-ide-plugin.xml
|
42
|
+
|
43
|
+
# Crashlytics plugin (for Android Studio and IntelliJ)
|
44
|
+
com_crashlytics_export_strings.xml
|
45
|
+
crashlytics.properties
|
46
|
+
crashlytics-build.properties
|
47
|
+
|
48
|
+
*.gem
|
49
|
+
*.rbc
|
50
|
+
.bundle
|
51
|
+
.config
|
52
|
+
.yardoc
|
53
|
+
Gemfile.lock
|
54
|
+
InstalledFiles
|
55
|
+
_yardoc
|
56
|
+
coverage
|
57
|
+
doc/
|
58
|
+
lib/bundler/man
|
59
|
+
pkg
|
60
|
+
rdoc
|
61
|
+
spec/reports
|
62
|
+
test/tmp
|
63
|
+
test/version_tmp
|
64
|
+
tmp
|
65
|
+
*.bundle
|
66
|
+
*.so
|
67
|
+
*.o
|
68
|
+
*.a
|
69
|
+
mkmf.log
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
tweetomator (1.0.0)
|
5
|
+
flickraw
|
6
|
+
rufus-scheduler
|
7
|
+
twitter
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
addressable (2.3.7)
|
13
|
+
buftok (0.2.0)
|
14
|
+
equalizer (0.0.9)
|
15
|
+
faraday (0.9.0)
|
16
|
+
multipart-post (>= 1.2, < 3)
|
17
|
+
flickraw (0.9.8)
|
18
|
+
http (0.6.3)
|
19
|
+
http_parser.rb (~> 0.6.0)
|
20
|
+
http_parser.rb (0.6.0)
|
21
|
+
json (1.8.2)
|
22
|
+
memoizable (0.4.2)
|
23
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
24
|
+
minitest (5.5.1)
|
25
|
+
multipart-post (2.0.0)
|
26
|
+
naught (1.0.0)
|
27
|
+
rake (10.4.2)
|
28
|
+
rufus-scheduler (3.0.9)
|
29
|
+
tzinfo
|
30
|
+
simple_oauth (0.3.0)
|
31
|
+
thread_safe (0.3.5)
|
32
|
+
twitter (5.13.0)
|
33
|
+
addressable (~> 2.3)
|
34
|
+
buftok (~> 0.2.0)
|
35
|
+
equalizer (~> 0.0.9)
|
36
|
+
faraday (~> 0.9.0)
|
37
|
+
http (~> 0.6.0)
|
38
|
+
http_parser.rb (~> 0.6.0)
|
39
|
+
json (~> 1.8)
|
40
|
+
memoizable (~> 0.4.0)
|
41
|
+
naught (~> 1.0)
|
42
|
+
simple_oauth (~> 0.3.0)
|
43
|
+
tzinfo (1.2.2)
|
44
|
+
thread_safe (~> 0.1)
|
45
|
+
|
46
|
+
PLATFORMS
|
47
|
+
ruby
|
48
|
+
|
49
|
+
DEPENDENCIES
|
50
|
+
bundler (~> 1.6)
|
51
|
+
minitest
|
52
|
+
rake
|
53
|
+
tweetomator!
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Nat Young
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# Tweetomator
|
2
|
+
|
3
|
+
Investigating the principal curvatures of the twittersphere.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'tweetomator'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install tweetomator
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
Create a new instance of each config class in config/config.rb
|
22
|
+
|
23
|
+
e.g.
|
24
|
+
|
25
|
+
twitter_config = TwitterConfig.new([...])
|
26
|
+
flickr_config = FlickrConfig.new([...])
|
27
|
+
tweetomator_config = TweetomatorConfig.new(twitter, flickr, [...])
|
28
|
+
tweetomator = Tweetomator.new(tweetomator_config)
|
29
|
+
tweetomator.run
|
30
|
+
|
31
|
+
## Contributing
|
32
|
+
|
33
|
+
1. Fork it ( https://github.com/[my-github-username]/tweetomator/fork )
|
34
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
35
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
36
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
37
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'flickraw'
|
2
|
+
|
3
|
+
class FlickrClient
|
4
|
+
module Fixed
|
5
|
+
MIN_UPLOAD_DATE = 1357028420
|
6
|
+
freeze
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(flickr_config)
|
10
|
+
FlickRaw.api_key = flickr_config.api_key
|
11
|
+
FlickRaw.shared_secret = flickr_config.shared_secret
|
12
|
+
end
|
13
|
+
|
14
|
+
def uri(uri)
|
15
|
+
FlickRaw.url(uri)
|
16
|
+
end
|
17
|
+
|
18
|
+
def search(query)
|
19
|
+
flickr.photos.search(tags: query, min_upload_date: Fixed::MIN_UPLOAD_DATE)
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class TwitterClient
|
2
|
+
def initialize(twitter_config)
|
3
|
+
@rest_client = Twitter::REST::Client.new do |config|
|
4
|
+
config.consumer_key = twitter_config.consumer_key
|
5
|
+
config.consumer_secret = twitter_config.consumer_secret
|
6
|
+
config.access_token = twitter_config.access_token
|
7
|
+
config.access_token_secret = twitter_config.access_token_secret
|
8
|
+
end
|
9
|
+
@username = twitter_config.username
|
10
|
+
end
|
11
|
+
|
12
|
+
def random_follower_of_random_follower
|
13
|
+
follower = @rest_client.followers.attrs[:users].sample[:screen_name]
|
14
|
+
@rest_client.friend_ids(follower).attrs[:ids].sample
|
15
|
+
end
|
16
|
+
|
17
|
+
def update_with_media(text, image_file)
|
18
|
+
@rest_client.update_with_media(text, image_file)
|
19
|
+
end
|
20
|
+
|
21
|
+
def update(text)
|
22
|
+
@rest_client.update(text)
|
23
|
+
end
|
24
|
+
|
25
|
+
def search(query)
|
26
|
+
@rest_client.search("#{query} -rt -from:#{@username}", { lang: 'en' }).first.text
|
27
|
+
end
|
28
|
+
|
29
|
+
def follow(user)
|
30
|
+
@rest_client.follow(user)
|
31
|
+
end
|
32
|
+
end
|
data/lib/composer.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require_relative 'tweet'
|
3
|
+
|
4
|
+
class Composer
|
5
|
+
def initialize(tweet_finder, emoji_finder, blacklist, twitter_client, max_length)
|
6
|
+
@tweet_finder = tweet_finder
|
7
|
+
@emoji_finder = emoji_finder
|
8
|
+
@blacklist = blacklist
|
9
|
+
@twitter_client = twitter_client
|
10
|
+
@max_length = max_length
|
11
|
+
end
|
12
|
+
|
13
|
+
def compose
|
14
|
+
tweets = @tweet_finder.find_tweets
|
15
|
+
tweets = tweets.map { |t| normalize(t) }
|
16
|
+
hashtag = @tweet_finder.find_random_word(tweets.sample)
|
17
|
+
emoji = @emoji_finder.find
|
18
|
+
text = tweets.join([', ', ' - ', '! '].sample)
|
19
|
+
text = filter(text)
|
20
|
+
text = trim(text, hashtag.length)
|
21
|
+
text = replace_a_word(text.split.sample, text)
|
22
|
+
text = replace_a_word(emoji, text)
|
23
|
+
Tweet.new(text.capitalize, hashtag, @twitter_client)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def normalize(tweet)
|
29
|
+
CGI.unescapeHTML(tweet.gsub(/(@([^\s])+|http([^\s])+|([^\s])+\/([^\s])+|)/, ''))
|
30
|
+
.gsub(/\s+/, ' ')
|
31
|
+
.strip
|
32
|
+
.downcase
|
33
|
+
.gsub(/[^[:word:]\s]/, '')
|
34
|
+
end
|
35
|
+
|
36
|
+
def filter(tweet)
|
37
|
+
tweet.split.reject { |w| @blacklist.include?(w.downcase) }.join(' ')
|
38
|
+
end
|
39
|
+
|
40
|
+
def replace_a_word(replacement, target)
|
41
|
+
split = target.split
|
42
|
+
out = split.select { |w| w.length >= replacement.length }.sample
|
43
|
+
split.map { |w| w == out ? replacement : w }.join(' ')
|
44
|
+
end
|
45
|
+
|
46
|
+
def trim(tweet, hashtag_length)
|
47
|
+
if tweet.size > (@max_length - (hashtag_length > 0 ? hashtag_length + 1 : 0))
|
48
|
+
split = tweet.split
|
49
|
+
split.delete_at(split.length - 1)
|
50
|
+
return trim(split.join(' '), hashtag_length)
|
51
|
+
end
|
52
|
+
tweet
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Validatable
|
2
|
+
def valid?
|
3
|
+
self.instance_variables.none? do |v|
|
4
|
+
attr = instance_variable_get(v)
|
5
|
+
attr.class.included_modules.include?(Validatable) ? !attr.valid? : attr == nil
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class TwitterConfig
|
11
|
+
include Validatable
|
12
|
+
|
13
|
+
attr_reader :consumer_key, :consumer_secret, :access_token, :access_token_secret,
|
14
|
+
:username, :tweet_max_length
|
15
|
+
|
16
|
+
def initialize(consumer_key, consumer_secret, access_token, access_token_secret, username, tweet_max_length)
|
17
|
+
@consumer_key = consumer_key
|
18
|
+
@consumer_secret = consumer_secret
|
19
|
+
@access_token = access_token
|
20
|
+
@access_token_secret = access_token_secret
|
21
|
+
@username = username
|
22
|
+
@tweet_max_length = tweet_max_length
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class FlickrConfig
|
27
|
+
include Validatable
|
28
|
+
|
29
|
+
attr_reader :api_key, :shared_secret
|
30
|
+
|
31
|
+
def initialize(api_key, shared_secret)
|
32
|
+
@api_key = api_key
|
33
|
+
@shared_secret = shared_secret
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class TweetomatorConfig
|
38
|
+
include Validatable
|
39
|
+
|
40
|
+
attr_reader :twitter_config, :flickr_config, :topics, :blacklist, :stop_words, :delay_in_seconds
|
41
|
+
|
42
|
+
def initialize(twitter_config, flickr_config, topics, blacklist, stop_words, delay_in_seconds)
|
43
|
+
@twitter_config = twitter_config
|
44
|
+
@flickr_config = flickr_config
|
45
|
+
@topics = topics
|
46
|
+
@blacklist = blacklist
|
47
|
+
@stop_words = stop_words
|
48
|
+
@delay_in_seconds = delay_in_seconds
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class TweetFinder
|
2
|
+
def initialize(client, topics, stop_words)
|
3
|
+
@client = client
|
4
|
+
@topics = topics
|
5
|
+
@stop_words = stop_words
|
6
|
+
end
|
7
|
+
|
8
|
+
def find_tweets
|
9
|
+
@topics.reduce([]) do |t, topic|
|
10
|
+
t << Thread.new(topic) { |topic| find_tweet(topic) }
|
11
|
+
end.each { |t| t.join }.map { |t| t.value }
|
12
|
+
end
|
13
|
+
|
14
|
+
def find_tweet(query)
|
15
|
+
@client.search(query)
|
16
|
+
end
|
17
|
+
|
18
|
+
def find_random_word(query)
|
19
|
+
term = extract_one_word(query)
|
20
|
+
tweet = find_tweet(term)
|
21
|
+
extract_one_word(tweet)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def extract_one_word(tweet)
|
27
|
+
tweet.split.reject { |w| /[^A-Za-z0-9]/.match(w.strip) != nil || @stop_words.include?(w.strip) }.sample
|
28
|
+
end
|
29
|
+
end
|
data/lib/tweet.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
class Tweet
|
2
|
+
attr_reader :hashtag
|
3
|
+
|
4
|
+
def initialize(text, hashtag, twitter_client)
|
5
|
+
@text = text
|
6
|
+
@hashtag = hashtag
|
7
|
+
@twitter_client = twitter_client
|
8
|
+
end
|
9
|
+
|
10
|
+
def post!(image = '')
|
11
|
+
if image.length > 0 && File.exists?(image)
|
12
|
+
@twitter_client.update_with_media(self.with_hashtag, File.new(image))
|
13
|
+
File.delete(image)
|
14
|
+
else
|
15
|
+
@twitter_client.update(self.with_hashtag)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def with_hashtag
|
20
|
+
(@hashtag && @hashtag.length > 0) ? "#{@text} ##{@hashtag}" : @text
|
21
|
+
end
|
22
|
+
|
23
|
+
def length
|
24
|
+
with_hashtag.length
|
25
|
+
end
|
26
|
+
end
|
data/lib/tweetomator.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'tweetomator/version'
|
2
|
+
require 'twitter'
|
3
|
+
require 'rufus-scheduler'
|
4
|
+
require_relative 'tweet'
|
5
|
+
require_relative 'tweetomator'
|
6
|
+
require_relative 'composer'
|
7
|
+
require_relative 'config/config'
|
8
|
+
require_relative 'finders/tweet_finder'
|
9
|
+
require_relative 'finders/flickr_findr'
|
10
|
+
require_relative 'finders/emoji_finder'
|
11
|
+
require_relative 'clients/flickr_client'
|
12
|
+
require_relative 'clients/twitter_client'
|
13
|
+
require_relative 'io/downloader'
|
14
|
+
|
15
|
+
class Tweetomator
|
16
|
+
def initialize(tweetomator_config)
|
17
|
+
if tweetomator_config.valid?
|
18
|
+
somebody_set_us_up_the_bot(tweetomator_config)
|
19
|
+
else
|
20
|
+
raise 'Invalid TweetomatorConfig'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def run
|
25
|
+
begin
|
26
|
+
@scheduler.interval("#{@delay}s") { do_stuff }
|
27
|
+
@scheduler.join
|
28
|
+
rescue
|
29
|
+
retry
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def somebody_set_us_up_the_bot(config)
|
36
|
+
@delay = config.delay_in_seconds
|
37
|
+
@twitter_client = TwitterClient.new(config.twitter_config)
|
38
|
+
@tweet_finder = TweetFinder.new(@twitter_client, config.topics, config.stop_words)
|
39
|
+
flickr_client = FlickrClient.new(config.flickr_config)
|
40
|
+
@flickr_finder = FlickrFindr.new(flickr_client)
|
41
|
+
emoji_finder = EmojiFinder.new
|
42
|
+
@composer = Composer.new(@tweet_finder, emoji_finder, config.blacklist, @twitter_client, config.twitter_config.tweet_max_length)
|
43
|
+
@downloader = Downloader.new
|
44
|
+
@scheduler = Rufus::Scheduler.new
|
45
|
+
end
|
46
|
+
|
47
|
+
def do_stuff
|
48
|
+
r = (1..4).to_a.sample
|
49
|
+
if r < 3
|
50
|
+
tweet_with_image!
|
51
|
+
elsif r < 4
|
52
|
+
tweet!
|
53
|
+
else
|
54
|
+
follow!
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def tweet!
|
59
|
+
tweet = @composer.compose
|
60
|
+
tweet.post!
|
61
|
+
end
|
62
|
+
|
63
|
+
def tweet_with_image!
|
64
|
+
tweet = @composer.compose
|
65
|
+
flickr_query = @tweet_finder.find_random_word(tweet.hashtag)
|
66
|
+
image_uri = @flickr_finder.find(flickr_query)
|
67
|
+
image_local = @downloader.download(image_uri, 'image')
|
68
|
+
tweet.post!(image_local)
|
69
|
+
end
|
70
|
+
|
71
|
+
def follow!
|
72
|
+
user = @twitter_client.random_follower_of_random_follower
|
73
|
+
@twitter_client.follow(user)
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require_relative '../../lib/composer'
|
3
|
+
require_relative '../../lib/tweetomator'
|
4
|
+
|
5
|
+
class TestComposer < Minitest::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
tweet_1 = 'I just backedeyeyeyey Potato Salad on @Kickstarter kck.st/1pLytCe'
|
8
|
+
tweet_2 = 'I HAVE NYANED FOR 48.9 SECONDS! http://nyan.cat via @nyannyancat'
|
9
|
+
cool_story = "So there I am, in Sri Lanka, formerly Ceylon, at about 3 o'clock in the morning, looking for one thousand brown M&Ms to fill a brandy glass, or Ozzy wouldn't go on stage that night. So, Jeff Beck pops his head 'round the door, and mentions there's a little sweets shop on the edge of town. So - we go. And - it's closed. So there's me, and Keith Moon, and David Crosby, breaking into that little sweets shop, eh. Well, instead of a guard dog, they've got this bloody great big Bengal tiger. I managed to take out the tiger with a can of mace, but the shopkeeper and his son... that's a different story altogether. I had to beat them to death with their own shoes. Nasty business, really, but sure enough I got the M&Ms, and Ozzy went on stage and did a great show."
|
10
|
+
mock_emoji_finder = Minitest::Mock.new
|
11
|
+
mock_emoji_finder.expect :find, ':trollface:'
|
12
|
+
mock_tweet_finder = Minitest::Mock.new
|
13
|
+
mock_tweet_finder.expect :find_tweets, [tweet_1, tweet_2, cool_story]
|
14
|
+
mock_tweet_finder.expect :find_random_word, 'amazing', [String]
|
15
|
+
mock_tweet_client = Minitest::Mock.new
|
16
|
+
@blacklist = %w(sri lanka)
|
17
|
+
@max_length = 100
|
18
|
+
@composer = Composer.new(mock_tweet_finder, mock_emoji_finder, @blacklist, mock_tweet_client, @max_length)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_that_tweet_is_short_enough
|
22
|
+
tweet = @composer.compose
|
23
|
+
assert tweet.with_hashtag.length <= @max_length, true
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_that_stop_words_are_removed
|
27
|
+
tweet = @composer.compose
|
28
|
+
refute tweet.with_hashtag.split.any? { |word| @blacklist.include?(word) }, true
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_that_links_are_removed
|
32
|
+
tweet = @composer.compose
|
33
|
+
refute tweet.with_hashtag.split.any? { |word| %w(kck.st/1pLytCe http://nyan.cat).include?(word) }, true
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_that_emoji_is_trololo
|
37
|
+
tweet = @composer.compose
|
38
|
+
assert tweet.with_hashtag.split.any? { |word| word == ':trollface:' }, true
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require_relative '../../../lib/finders/flickr_findr'
|
3
|
+
|
4
|
+
class TestFlickrFindr < Minitest::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
mock_flickr_client = Minitest::Mock.new
|
7
|
+
mock_flickr_client.expect :search, ['http://www.flickr.com/garbage'], [String]
|
8
|
+
mock_flickr_client.expect :uri, 'http://www.flickr.com/garbage', [String]
|
9
|
+
@finder = FlickrFindr.new(mock_flickr_client)
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_that_it_returns_an_image_path
|
13
|
+
path = @finder.find('something')
|
14
|
+
assert_equal path, 'http://www.flickr.com/garbage'
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require_relative '../../../lib/finders/tweet_finder'
|
3
|
+
|
4
|
+
class TestTweetFinder < Minitest::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
mock_search_result = Minitest::Mock.new
|
7
|
+
result_1 = 'I just backed Potato Salad on @Kickstarter kck.st/1pLytCe'
|
8
|
+
result_2 = 'I am a chicken'
|
9
|
+
result_3 = 'And there I was in Sri Lanka, formerly Ceylon'
|
10
|
+
mock_search_result.expect :text, result_1
|
11
|
+
mock_search_result.expect :text, result_2
|
12
|
+
mock_search_result.expect :text, result_3
|
13
|
+
mock_client = Minitest::Mock.new
|
14
|
+
3.times { mock_client.expect :search, [mock_search_result], [String, Hash] }
|
15
|
+
stop_words = result_1.split + result_2.split + result_3.split
|
16
|
+
@finder = TweetFinder.new(mock_client, '@mrnatyoung', ['Trumpets', 'Bookmarks', 'Potato Salads'], stop_words)
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_that_it_finds_a_tweet_per_query
|
20
|
+
tweets = @finder.find_tweets
|
21
|
+
assert_equal tweets.size, 3
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_stop_words_are_filtered_from_find_random_word
|
25
|
+
word = @finder.find_random_word('something')
|
26
|
+
assert_equal word, nil
|
27
|
+
end
|
28
|
+
end
|
data/tweetomator.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'tweetomator/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'tweetomator'
|
8
|
+
spec.version = Tweetomator::VERSION
|
9
|
+
spec.authors = ['Nat Young']
|
10
|
+
spec.email = ['nat.young7@gmail.com']
|
11
|
+
spec.summary = %q{Twitter automation for the time poor.}
|
12
|
+
spec.description = %q{Captivate your twitter audience with a series of interesting tweets and beautiful photos.}
|
13
|
+
spec.homepage = 'https://github.com/natyoung/tweetomator'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
22
|
+
spec.add_development_dependency 'rake'
|
23
|
+
spec.add_development_dependency 'minitest'
|
24
|
+
|
25
|
+
spec.add_runtime_dependency 'flickraw'
|
26
|
+
spec.add_runtime_dependency 'twitter'
|
27
|
+
spec.add_runtime_dependency 'rufus-scheduler'
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tweetomator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nat Young
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-06-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: flickraw
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: twitter
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rufus-scheduler
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: Captivate your twitter audience with a series of interesting tweets and
|
98
|
+
beautiful photos.
|
99
|
+
email:
|
100
|
+
- nat.young7@gmail.com
|
101
|
+
executables: []
|
102
|
+
extensions: []
|
103
|
+
extra_rdoc_files: []
|
104
|
+
files:
|
105
|
+
- .gitignore
|
106
|
+
- Gemfile
|
107
|
+
- Gemfile.lock
|
108
|
+
- LICENSE.txt
|
109
|
+
- README.md
|
110
|
+
- Rakefile
|
111
|
+
- lib/clients/flickr_client.rb
|
112
|
+
- lib/clients/twitter_client.rb
|
113
|
+
- lib/composer.rb
|
114
|
+
- lib/config/config.rb
|
115
|
+
- lib/finders/emoji_finder.rb
|
116
|
+
- lib/finders/flickr_findr.rb
|
117
|
+
- lib/finders/tweet_finder.rb
|
118
|
+
- lib/io/downloader.rb
|
119
|
+
- lib/tweet.rb
|
120
|
+
- lib/tweetomator.rb
|
121
|
+
- lib/tweetomator/version.rb
|
122
|
+
- test/lib/composer_test.rb
|
123
|
+
- test/lib/finders/flickr_findr_test.rb
|
124
|
+
- test/lib/finders/tweet_finder_test.rb
|
125
|
+
- tweetomator.gemspec
|
126
|
+
homepage: https://github.com/natyoung/tweetomator
|
127
|
+
licenses:
|
128
|
+
- MIT
|
129
|
+
metadata: {}
|
130
|
+
post_install_message:
|
131
|
+
rdoc_options: []
|
132
|
+
require_paths:
|
133
|
+
- lib
|
134
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - '>='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
141
|
+
- - '>='
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
144
|
+
requirements: []
|
145
|
+
rubyforge_project:
|
146
|
+
rubygems_version: 2.2.2
|
147
|
+
signing_key:
|
148
|
+
specification_version: 4
|
149
|
+
summary: Twitter automation for the time poor.
|
150
|
+
test_files:
|
151
|
+
- test/lib/composer_test.rb
|
152
|
+
- test/lib/finders/flickr_findr_test.rb
|
153
|
+
- test/lib/finders/tweet_finder_test.rb
|