tweetomator 1.0.3 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4fe9ed9b8f25c3191d276c5bfb8f1ab7e667743f
4
- data.tar.gz: 44d3f6ca11bad6d7fc44d40bf9692ffd15dfcc92
3
+ metadata.gz: 499c4df1bff2a96595cffa6188a37a5d3aa05e87
4
+ data.tar.gz: 853355749050b089c4bd9a80d3ebb8b6aa923853
5
5
  SHA512:
6
- metadata.gz: 6956fea6e07501934c388484916b6cb31a2cdd768f54764b7ae1303528cf74918bc2d426a4c56d6a2c60ddc8edbd6e556be97647f01ce32d550c4fa01a97cf70
7
- data.tar.gz: 9420997993d3a8a833ffb88b5aaf0626408764abf9b0b8cd50fef96ef7a5c6cea4fc288f3c9e1e52930b4e719fcfaa64fa1466ba97e17f908ed54011c4ca2c7d
6
+ metadata.gz: a184e4fdbb68947988767bc8b2ef1a2e85260f3b624fc2e2b3fa4eb350f492a285fa24cfec06765b212e2578a22f187f69ced5cf4a51d01c51bb384ddc0864e3
7
+ data.tar.gz: f41e1638fac1a420ef0610aeab93583786f9e4ad36fef8087b44acec8e48252b2a26734f4d025c7a51387b734921268a3784520fce3d9b18467f02e8065cf4f6
@@ -0,0 +1,18 @@
1
+ class Composer
2
+ def initialize(config, twitter_client, tweet_finder, emoji_finder)
3
+ @blacklist = config.blacklist
4
+ @max_length = config.max_length
5
+ @stop_words = config.stop_words
6
+ @twitter_client = twitter_client
7
+ @tweet_finder = tweet_finder
8
+ @emoji_finder = emoji_finder
9
+ end
10
+
11
+ def compose_tweet
12
+ raise NoMethodError 'implement compose_tweet'
13
+ end
14
+
15
+ def compose_image_query(query)
16
+ raise NoMethodError 'implement compose_image_query(query)'
17
+ end
18
+ end
@@ -0,0 +1,66 @@
1
+ require_relative '../tweet'
2
+ require_relative '../io/text_reader'
3
+ require_relative 'composer'
4
+
5
+ class MarkovComposer < Composer
6
+ def initialize(config, twitter_client, tweet_finder, emoji_finder)
7
+ super(config, twitter_client, tweet_finder, emoji_finder)
8
+ @chain = {}
9
+ reader = TextReader.new(config.input_text_file)
10
+ reader.process_input_text_file
11
+ @beginnings = reader.beginnings
12
+ @lines = reader.lines
13
+ build_chain(@lines.join.split)
14
+ end
15
+
16
+ def compose_tweet
17
+ text = ''
18
+ word = @beginnings.sample
19
+ until text.count('.') == 1 || text.length + word.length + 1 >= @max_length
20
+ text = "#{text}#{word} "
21
+ word = get(word)
22
+ end
23
+ text = clean_up(text)
24
+ hashtag = @lines.sample.split.reject { |w| w.strip.nil? || /[^A-Za-z]/.match(w.strip) != nil || @stop_words.include?(w.strip) }.sample
25
+ if hashtag != nil && (text.length + hashtag.length + 1) < @max_length && (1..4).to_a.sample == 4
26
+ return Tweet.new(clean_up(text), hashtag.downcase, @twitter_client)
27
+ end
28
+ return Tweet.new(clean_up(text), '', @twitter_client)
29
+ end
30
+
31
+ def compose_image_query(query)
32
+ "##{@chain.keys.reject { |k| @stop_words.include?(k) }.sample}"
33
+ end
34
+
35
+ private
36
+
37
+ def build_chain(words)
38
+ words.each_with_index do |word, i|
39
+ add(word, words[i + 1]) unless i >= words.size - 2
40
+ end
41
+ end
42
+
43
+ def add(word, next_word)
44
+ @chain[word] = Hash.new(0) if @chain[word] == nil
45
+ @chain[word][next_word] += 1
46
+ end
47
+
48
+ def get(word)
49
+ return '' if @chain[word] == nil
50
+ sum = @chain[word].reduce(0) { |sum, kv| sum += kv[1] }
51
+ random = rand(sum) + 1
52
+ count = 0
53
+ @chain[word].find do |w, i|
54
+ count += i
55
+ count >= random
56
+ end.first
57
+ end
58
+
59
+ def clean_up(text)
60
+ text = text.strip.capitalize
61
+ if text[text.length-1].match('[A-Za-z]') != nil && text.length < @max_length
62
+ text << '.'
63
+ end
64
+ text
65
+ end
66
+ end
@@ -1,19 +1,16 @@
1
- require 'cgi'
2
1
  require_relative '../tweet'
2
+ require_relative 'composer'
3
+ require 'cgi'
3
4
 
4
- class RandomComposer
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
5
+ class RandomComposer < Composer
6
+ def initialize(config, twitter_client, tweet_finder, emoji_finder)
7
+ super(config, twitter_client, tweet_finder, emoji_finder)
11
8
  end
12
9
 
13
- def compose
10
+ def compose_tweet
14
11
  tweets = @tweet_finder.find_tweets
15
12
  tweets = tweets.map { |t| normalize(t) }
16
- hashtag = @tweet_finder.find_random_word(tweets.sample)
13
+ hashtag = find_random_word(tweets.sample)
17
14
  emoji = @emoji_finder.find
18
15
  text = tweets.join([', ', ' - ', '! '].sample)
19
16
  text = filter(text)
@@ -23,8 +20,22 @@ class RandomComposer
23
20
  Tweet.new(text.capitalize, hashtag, @twitter_client)
24
21
  end
25
22
 
23
+ def compose_image_query(query)
24
+ find_random_word(query)
25
+ end
26
+
26
27
  private
27
28
 
29
+ def find_random_word(query)
30
+ term = extract_one_word(query)
31
+ tweet = @tweet_finder.find_tweet(term)
32
+ extract_one_word(tweet)
33
+ end
34
+
35
+ def extract_one_word(tweet)
36
+ tweet.split.reject { |w| /[^A-Za-z0-9]/.match(w.strip) != nil || @stop_words.include?(w.strip) }.sample
37
+ end
38
+
28
39
  def normalize(tweet)
29
40
  CGI.unescapeHTML(tweet.gsub(/(@([^\s])+|http([^\s])+|([^\s])+\/([^\s])+|)/, ''))
30
41
  .gsub(/\s+/, ' ')
data/lib/config/config.rb CHANGED
@@ -39,12 +39,13 @@ class TweetomatorConfig
39
39
 
40
40
  attr_reader :twitter_config, :flickr_config, :topics, :blacklist, :stop_words, :delay_in_seconds
41
41
 
42
- def initialize(twitter_config, flickr_config, topics, blacklist, stop_words, delay_in_seconds)
42
+ def initialize(twitter_config, flickr_config, topics, blacklist, stop_words, delay_in_seconds, composer_type)
43
43
  @twitter_config = twitter_config
44
44
  @flickr_config = flickr_config
45
45
  @topics = topics
46
46
  @blacklist = blacklist
47
47
  @stop_words = stop_words
48
48
  @delay_in_seconds = delay_in_seconds
49
+ @composer_type = composer_type
49
50
  end
50
51
  end
@@ -1,8 +1,7 @@
1
1
  class TweetFinder
2
- def initialize(client, topics, stop_words)
2
+ def initialize(client, topics)
3
3
  @client = client
4
4
  @topics = topics
5
- @stop_words = stop_words
6
5
  end
7
6
 
8
7
  def find_tweets
@@ -14,16 +13,4 @@ class TweetFinder
14
13
  def find_tweet(query)
15
14
  @client.search(query)
16
15
  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
16
  end
@@ -0,0 +1,15 @@
1
+ class TextReader
2
+ attr_accessor :beginnings, :lines
3
+
4
+ def initialize(input_text_file)
5
+ @input_text_file = input_text_file
6
+ @beginnings = []
7
+ @lines = []
8
+ end
9
+
10
+ def process_input_text_file
11
+ @lines = File.readlines(@input_text_file)
12
+ @beginnings = @lines.collect { |s| s.split(' ').first }.reject { |w| w.nil? }
13
+ return self
14
+ end
15
+ end
@@ -1,3 +1,3 @@
1
1
  class Tweetomator
2
- VERSION = '1.0.3'
2
+ VERSION = '1.1.0'
3
3
  end
data/lib/tweetomator.rb CHANGED
@@ -35,20 +35,20 @@ class Tweetomator
35
35
  def somebody_set_us_up_the_bot(config)
36
36
  @delay = config.delay_in_seconds
37
37
  @twitter_client = TwitterClient.new(config.twitter_config)
38
- @tweet_finder = TweetFinder.new(@twitter_client, config.topics, config.stop_words)
38
+ tweet_finder = TweetFinder.new(@twitter_client, config.topics)
39
39
  flickr_client = FlickrClient.new(config.flickr_config)
40
40
  @flickr_finder = FlickrFindr.new(flickr_client)
41
41
  emoji_finder = EmojiFinder.new
42
- @random_composer = RandomComposer.new(@tweet_finder, emoji_finder, config.blacklist, @twitter_client, config.twitter_config.tweet_max_length)
42
+ @composer = config.composer_type.class.new(config, @twitter_client, tweet_finder, emoji_finder)
43
43
  @downloader = Downloader.new
44
44
  @scheduler = Rufus::Scheduler.new
45
45
  end
46
46
 
47
47
  def do_stuff
48
- r = (1..4).to_a.sample
49
- if r < 3
48
+ r = (1..10).to_a.sample
49
+ if r < 2
50
50
  tweet_with_image!
51
- elsif r < 4
51
+ elsif r < 9
52
52
  tweet!
53
53
  else
54
54
  follow!
@@ -56,13 +56,13 @@ class Tweetomator
56
56
  end
57
57
 
58
58
  def tweet!
59
- tweet = @random_composer.compose
59
+ tweet = @composer.compose_tweet
60
60
  tweet.post!
61
61
  end
62
62
 
63
63
  def tweet_with_image!
64
- tweet = @random_composer.compose
65
- flickr_query = @tweet_finder.find_random_word(tweet.hashtag)
64
+ tweet = @composer.compose_tweet
65
+ flickr_query = @composer.compose_image_query(tweet.hashtag)
66
66
  image_uri = @flickr_finder.find(flickr_query)
67
67
  image_local = @downloader.download(image_uri, 'image')
68
68
  tweet.post!(image_local)
@@ -0,0 +1,36 @@
1
+ require 'minitest/autorun'
2
+ require_relative '../../../lib/composers/markov_composer'
3
+ require_relative '../../../lib/tweetomator'
4
+
5
+ class TestMarkovComposer < Minitest::Unit::TestCase
6
+ def setup
7
+ cool_story = "So there I am, in Sri Lanka, formerly Ceylon, at about 3 o'clock in the morning, \nlooking 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, \n 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."
8
+ riveting_tale = "And even in this cosmopolitan trend, he did that precede and open the way to other more advanced civilizations. \n About henceforth in Europe, no cease to be a citizen of his country, it is not, in any way, International? no one \n can shut, like the snail, by its shell: all need air, space wider for match the new needs, their aspirations."
9
+ @amazing_narrative = "#{cool_story} #{riveting_tale}"
10
+ mock_emoji_finder = Minitest::Mock.new
11
+ mock_emoji_finder.expect :find, ':trollface:'
12
+ mock_tweet_finder = Minitest::Mock.new
13
+ mock_tweet_client = Minitest::Mock.new
14
+ @max_length = 100
15
+ @blacklist = %w(sri lanka)
16
+ @stop_words = %w(Potato Salad)
17
+ mock_config = Minitest::Mock.new
18
+ mock_config.expect :max_length, @max_length
19
+ mock_config.expect :blacklist, @blacklist
20
+ mock_config.expect :stop_words, @stop_words
21
+ mock_config.expect :input_text_file, 'text.txt'
22
+ mock_text_reader = MiniTest::Mock.new
23
+ mock_text_reader.expect :process_input_text_file, mock_text_reader
24
+ mock_text_reader.expect :beginnings, @amazing_narrative.split
25
+ mock_text_reader.expect :lines, @amazing_narrative.split("\n")
26
+ TextReader.stub :new, mock_text_reader do
27
+ @markov_composer = MarkovComposer.new(mock_config, mock_tweet_client, mock_tweet_finder, mock_emoji_finder)
28
+ end
29
+ end
30
+
31
+ def test_that_tweet_is_short_enough
32
+ tweet = @markov_composer.compose_tweet
33
+ puts tweet.with_hashtag
34
+ assert tweet.with_hashtag.length <= @max_length, true
35
+ end
36
+ end
@@ -11,25 +11,31 @@ class TestRandomComposer < Minitest::Unit::TestCase
11
11
  mock_emoji_finder.expect :find, ':trollface:'
12
12
  mock_tweet_finder = Minitest::Mock.new
13
13
  mock_tweet_finder.expect :find_tweets, [tweet_1, tweet_2, cool_story]
14
+ mock_tweet_finder.expect :find_tweet, tweet_1, [String]
14
15
  mock_tweet_finder.expect :find_random_word, 'amazing', [String]
15
16
  mock_tweet_client = Minitest::Mock.new
16
- @blacklist = %w(sri lanka)
17
17
  @max_length = 100
18
- @random_composer = RandomComposer.new(mock_tweet_finder, mock_emoji_finder, @blacklist, mock_tweet_client, @max_length)
18
+ @blacklist = %w(sri lanka)
19
+ @stop_words = %w(Potato Salad)
20
+ mock_config = Minitest::Mock.new
21
+ mock_config.expect :max_length, @max_length
22
+ mock_config.expect :blacklist, @blacklist
23
+ mock_config.expect :stop_words, @stop_words
24
+ @random_composer = RandomComposer.new(mock_config, mock_tweet_client, mock_tweet_finder, mock_emoji_finder)
19
25
  end
20
26
 
21
27
  def test_that_tweet_is_short_enough
22
- tweet = @random_composer.compose
28
+ tweet = @random_composer.compose_tweet
23
29
  assert tweet.with_hashtag.length <= @max_length, true
24
30
  end
25
31
 
26
- def test_that_stop_words_are_removed
27
- tweet = @random_composer.compose
32
+ def test_that_blacklisted_words_are_removed
33
+ tweet = @random_composer.compose_tweet
28
34
  refute tweet.with_hashtag.split.any? { |word| @blacklist.include?(word) }, true
29
35
  end
30
36
 
31
37
  def test_that_links_are_removed
32
- tweet = @random_composer.compose
38
+ tweet = @random_composer.compose_tweet
33
39
  refute tweet.with_hashtag.split.any? { |word| %w(kck.st/1pLytCe http://nyan.cat).include?(word) }, true
34
40
  end
35
41
  end
@@ -7,16 +7,11 @@ class TestTweetFinder < Minitest::Unit::TestCase
7
7
  mock_client = Minitest::Mock.new
8
8
  3.times { mock_client.expect :search, search_result, [String] }
9
9
  stop_words = search_result.split
10
- @finder = TweetFinder.new(mock_client, ['Trumpets', 'Bookmarks', 'Potato Salads'], stop_words)
10
+ @finder = TweetFinder.new(mock_client, ['Trumpets', 'Bookmarks', 'Potato Salads'])
11
11
  end
12
12
 
13
13
  def test_that_it_finds_a_tweet_per_query
14
14
  tweets = @finder.find_tweets
15
15
  assert_equal tweets.size, 3
16
16
  end
17
-
18
- def test_stop_words_are_filtered_from_find_random_word
19
- word = @finder.find_random_word('something')
20
- assert_equal word, nil
21
- end
22
17
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tweetomator
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nat Young
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-14 00:00:00.000000000 Z
11
+ date: 2015-06-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -110,15 +110,19 @@ files:
110
110
  - Rakefile
111
111
  - lib/clients/flickr_client.rb
112
112
  - lib/clients/twitter_client.rb
113
+ - lib/composers/composer.rb
114
+ - lib/composers/markov_composer.rb
113
115
  - lib/composers/random_composer.rb
114
116
  - lib/config/config.rb
115
117
  - lib/finders/emoji_finder.rb
116
118
  - lib/finders/flickr_findr.rb
117
119
  - lib/finders/tweet_finder.rb
118
120
  - lib/io/downloader.rb
121
+ - lib/io/text_reader.rb
119
122
  - lib/tweet.rb
120
123
  - lib/tweetomator.rb
121
124
  - lib/tweetomator/version.rb
125
+ - test/lib/composers/markov_composer_test.rb
122
126
  - test/lib/composers/random_composer_test.rb
123
127
  - test/lib/finders/flickr_findr_test.rb
124
128
  - test/lib/finders/tweet_finder_test.rb
@@ -148,6 +152,8 @@ signing_key:
148
152
  specification_version: 4
149
153
  summary: Twitter automation for the time poor.
150
154
  test_files:
155
+ - test/lib/composers/markov_composer_test.rb
151
156
  - test/lib/composers/random_composer_test.rb
152
157
  - test/lib/finders/flickr_findr_test.rb
153
158
  - test/lib/finders/tweet_finder_test.rb
159
+ has_rdoc: