tweetomator 1.0.3 → 1.1.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 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: