twexicon 0.1.6

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a1cd602f292631f449f3fe0f20536abbdf98ad4a
4
+ data.tar.gz: b0a6f7d8c6635cd17ec42754875761ee977b1388
5
+ SHA512:
6
+ metadata.gz: 392e64484aace879ae529f9f54c44e8e8de403ed5126bc2b182f224d556667dd2b2b4760d0c0a10be21c92fd3cd82c383f072e10613646aeea4c2add05226e26
7
+ data.tar.gz: fe9d1014c544a9712f97b0bcbf0b18a6758d9763ff628e45a02edef0371590424130d76d344728aeda0779361d8b07367bc63e1f2c0c26aef626448a4b6b4e65
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "twexicon"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/bin/twexicon ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/twexicon'
4
+
5
+ Twexicon::CLI.new.call
@@ -0,0 +1,8 @@
1
+ require 'nokogiri'
2
+ require 'open-uri'
3
+
4
+ require_relative '../lib/twexicon/version'
5
+ require_relative '../lib/twexicon/cli'
6
+ require_relative '../lib/twexicon/analyzer'
7
+ require_relative '../lib/twexicon/scraper'
8
+ require_relative '../lib/twexicon/validator'
data/lib/twexicon.rb ADDED
@@ -0,0 +1 @@
1
+ require_relative '../config/environment'
@@ -0,0 +1,184 @@
1
+ class Twexicon::Analyzer
2
+ attr_reader :tweets, :username, :input, :int
3
+
4
+ def initialize(username, tweets)
5
+ @username = username
6
+ @tweets = tweets
7
+ puts "\nIndexed the #{tweets.size} most recent tweet(s) for @#{username}."
8
+ run
9
+ end
10
+
11
+ def run
12
+ quit = false
13
+ until quit
14
+ @input = nil
15
+ @int = ""
16
+ puts "What would you like to do next? For options, type 'help'."
17
+ until is_valid?
18
+ @input = gets.strip.gsub(/\W/, "").downcase
19
+ end
20
+ case input
21
+ when "help", "h"
22
+ puts "\nTo retrieve one of @#{username}'s #{tweets.size} tweet(s), type 'GET'."
23
+ puts "To view @#{username}'s ten favorite words, type 'WORDS'."
24
+ puts "For @#{username}'s favorite things to scream about, type 'SHOUTS'."
25
+ puts "To see the #hashtags that consume @#{username}'s thoughts, type 'HASHTAGS'."
26
+ puts "For some Twitter handles that @#{username} has tweeted at, type 'USERNAMES'."
27
+ puts "To see links for embedded pictures and/or videos, type 'PIX'."
28
+ puts "To view any other links @#{username} deemed worthy of sharing, type 'LINKS'."
29
+ puts "For some numbers that @#{username} mentioned, type 'NUMBERS'."
30
+ puts "To see which short acronyms @#{username} is fond of, type 'ACRONYMS'."
31
+ puts "To look up a different Twitter handle or to exit the program, type 'EXIT'."
32
+ puts "Short commands, in order: 'g', 'w', 's', 'ht', 'u', 'p', 'l', 'n', 'a', 'e', and 'h' for 'help'."
33
+ when "get", "g" then get_tweet
34
+ when "words", "w" then get_words
35
+ when "shouts", "s" then get_shouts
36
+ when "pix", "p" then get_pix
37
+ when "links", "l" then get_links
38
+ when "numbers", "n" then get_numbers
39
+ when "acronyms", "a" then get_acronyms
40
+ when "hashtags", "ht" then get_hashtags
41
+ when "usernames", "handles", "u" then get_usernames
42
+ when "exit", "e" then quit = true
43
+ end
44
+ end
45
+ end
46
+
47
+ def is_valid?
48
+ if input =~ /(^help$|^h$|^get$|^g$|^exit$|^e$|^words$|^w$|^shouts$|^s$|^pix$|^p$|^links$|^l$|^numbers$|^n$|^acronyms$|^a$|^hashtags$|^ht$|^usernames$|^handles$|^u$)/
49
+ true
50
+ elsif input == nil
51
+ false
52
+ else
53
+ puts "Hm, I didn't recognize that. Please make a valid selection."
54
+ puts "To display all options, type 'help'."
55
+ end
56
+ end
57
+
58
+ def get_tweet
59
+ if tweets.size == 1
60
+ puts "\n@#{username}'s only tweet is: #{tweets[int].keys[1]}"
61
+ else
62
+ puts "\nWhich tweet would you like to view?"
63
+ int = ""
64
+ until int =~ /^\d+$/ && int.to_i >= 1 && int.to_i <= tweets.size
65
+ puts "Please specify a number between 1 and #{tweets.size}."
66
+ int = gets.strip.gsub(/\D/, "")
67
+ end
68
+ puts "\n@#{username} said: #{tweets[int.to_i].keys[0]}"
69
+ end
70
+ end
71
+
72
+ # 38 words from https://en.wikipedia.org/wiki/Most_common_words_in_English –– Left out some common yet still interesting ones (People, Good, Think, Work, First, One, Two, Want, New, Give, Know)
73
+ COMMON_WORDS = ["A", "About", "An", "And", "Are", "As", "At", "Be", "Been", "But", "By", "For", "From", "Get", "Had", "Has", "Have", "In", "Into", "Is", "It", "It's", "Its", "Just", "Not", "Of", "On", "Or", "Say", "So", "Some", "That", "The", "There", "These", "This", "Those", "To", "Up", "With", "I", "My", "Your", "They", "He", "You", "Do", "His", "We", "Her", "She", "Will", "All", "Would", "Their", "What", "Out", "If", "Who", "Which", "Go", "Me", "When", "Make", "Can", "Like", "Time", "No", "Him", "Take", "Year", "Could", "Them", "See", "Other", "Than", "Then", "Now", "Look", "Only", "Come", "Over", "Also", "Back", "After", "Use", "How", "Our", "Well", "Way", "Even", "Because", "Any", "Day", "Most", "Us",
74
+ # other additions
75
+ "Wouldn't", "Couldn't", "Shouldn't", "Mustn't", "Would've", "Could've", "Should've", "Must've", "Hadn't", "Wasn't", "Weren't", "Ain't", "Aint", "Here", "Seem", "Seems", "That's", "Took", "Much", "More", "You're", "We're", "We've", "I've", "I'm",
76
+ # contraction endings until I fix the parsing error
77
+ "Re", "Ll", "Ve",
78
+ # Letters until I fix the contraction parsing error
79
+ "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
80
+
81
+ def get_words
82
+ words = {}
83
+ word_array = []
84
+ tweets.each_value do |v| # Count up instances of each word
85
+ v.values[0][:words].each do |w|
86
+ word = w.gsub(/[^\'\w]/, "").capitalize
87
+ if words.has_key?(word) && !COMMON_WORDS.include?(word)
88
+ words[word] += 1
89
+ else
90
+ words[word] = 1
91
+ end
92
+ end
93
+ end
94
+ words.each do |w, n| # Create strings for the words that occur >1 times
95
+ case n
96
+ when 1..9 then word_array << "0#{n}x #{w}"
97
+ when 10..99 then word_array << "#{n}x #{w}"
98
+ end
99
+ end
100
+ if word_array.empty?
101
+ puts "\nIt appears that @#{username} is not much of a talker."
102
+ else
103
+ puts "\n@#{username}'s current favorite word(s):"
104
+ puts word_array.sort.reverse.take(10)
105
+ end
106
+ end
107
+
108
+ def get_shouts
109
+ collection = []
110
+ tweets.each{|num, tweet| collection << tweet.values[0][:shouts]}
111
+ if collection.flatten.compact.empty?
112
+ puts "\nIt appears that @#{username} isn't excited by anything."
113
+ else
114
+ puts "\n@#{username} shouted about the following things:"
115
+ collection.flatten.compact.uniq.each{|s| puts s}
116
+ end
117
+ end
118
+
119
+ def get_pix
120
+ collection = []
121
+ tweets.each{|num, tweet| collection << tweet.values[0][:pix]}
122
+ if collection.flatten.compact.empty?
123
+ puts "\nIt appears that @#{username} doesn't think a picture is worth 140 characters."
124
+ else
125
+ puts "\n@#{username} shared the following risky clicks:"
126
+ collection.flatten.compact.uniq.each{|p| puts p}
127
+ end
128
+ end
129
+
130
+ def get_links
131
+ collection = []
132
+ tweets.each{|num, tweet| collection << tweet.values[0][:links]}
133
+ if collection.flatten.compact.empty?
134
+ puts "\nIt appears that @#{username} hasn't found anything worth sharing on the interwebs."
135
+ else
136
+ puts "\n@#{username} directed traffic to the following sites:"
137
+ collection.flatten.compact.uniq.each{|l| puts l}
138
+ end
139
+ end
140
+
141
+ def get_numbers
142
+ collection = []
143
+ tweets.each{|num, tweet| collection << tweet.values[0][:numbers]}
144
+ if collection.flatten.compact.empty?
145
+ puts "\nIt appears that @#{username} isn't a numbers person."
146
+ else
147
+ puts "\n@#{username} loves them some digits:"
148
+ collection.flatten.compact.uniq.each{|n| puts n}
149
+ end
150
+ end
151
+
152
+ def get_acronyms
153
+ collection = []
154
+ tweets.each{|num, tweet| collection << tweet.values[0][:acronyms]}
155
+ if collection.flatten.compact.empty?
156
+ puts "\nIt appears that @#{username} really likes to spell everything out."
157
+ else
158
+ puts "\n@#{username} was too lazy to type out the following acronyms:"
159
+ collection.flatten.compact.uniq.each{|a| puts a}
160
+ end
161
+ end
162
+
163
+ def get_hashtags
164
+ collection = []
165
+ tweets.each{|num, tweet| collection << tweet.values[0][:hashtags]}
166
+ if collection.flatten.compact.empty?
167
+ puts "\nIt appears that @#{username} isn't maximizing their #impressions."
168
+ else
169
+ puts "\n@#{username} ameliorated the lack of Thought Leadership on these topics:"
170
+ collection.flatten.compact.uniq.each{|h| puts h}
171
+ end
172
+ end
173
+
174
+ def get_usernames
175
+ collection = []
176
+ tweets.each{|num, tweet| collection << tweet.values[0][:usernames]}
177
+ if collection.flatten.compact.empty?
178
+ puts "\nIt appears that @#{username} is a bit of a solipsist."
179
+ else
180
+ puts "\n@#{username} thinkfluenced the following users:"
181
+ collection.flatten.compact.uniq{|u| u.downcase}.each{|u| puts u}
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,25 @@
1
+ class Twexicon::CLI
2
+ def call
3
+ puts "Welcome to Twexicon."
4
+ run = true
5
+
6
+ while run
7
+ username = Twexicon::Validator.new.run
8
+ tweets = Twexicon::Scraper.new(username).refine_tweets
9
+ if tweets.empty?
10
+ puts "Unfortunately, @#{username} has never tweeted or has their tweets protected. Sorry about that."
11
+ else
12
+ Twexicon::Analyzer.new(username, tweets)
13
+ end
14
+
15
+ input = ""
16
+ until input =~ /^[yn]$/
17
+ puts "Would you like to look up tweets from another Twitter handle? [Y/N]"
18
+ input = gets.strip.gsub(/\W/, "").downcase
19
+ end
20
+ input =~ /[n]/ ? run = false : run = true
21
+ end
22
+
23
+ puts "Thank you –– come again!"
24
+ end
25
+ end
@@ -0,0 +1,27 @@
1
+ class Twexicon::Scraper
2
+ attr_reader :tweets
3
+
4
+ def initialize(username)
5
+ @tweets = {}
6
+ scrape_tweets(username)
7
+ end
8
+
9
+ def scrape_tweets(username)
10
+ Nokogiri::HTML(open("https://twitter.com/#{username}")).css(".tweet-text").each{|t| tweets[tweets.length+1] = {t.text => {:pix => [], :links => [], :hashtags => [], :usernames => [], :numbers => [], :acronyms => [], :shouts => [], :words => []}}}
11
+ end
12
+
13
+ def refine_tweets
14
+ tweets.each do |num, tweet|
15
+ t = tweet.keys[0].dup
16
+ t.scan(/pic.twitter.com\/\w{10}/){|p| tweet.values[0][:pix] << p.strip}.gsub!(/pic.twitter.com\/\w{10}/, " ")
17
+ t.scan(/https?:\/\/[\w\.\?\=\&\-\/\#]+/){|w| tweet.values[0][:links] << w.strip}.gsub!(/https?:\/\/[\w\.\?\=\&\-\/\#]+/, " ")
18
+ t.scan(/#\w+/){|h| tweet.values[0][:hashtags] << h.gsub(/\W/, "").prepend("#")}.gsub!(/#\w+/, " ")
19
+ t.scan(/@\w+/){|u| tweet.values[0][:usernames] << u.gsub(/\W/, "").prepend("@")}.gsub!(/@\w+/, " ")
20
+ t.scan(/(\d+[:\.\b]?\d*)+/){|n| tweet.values[0][:numbers] << n.first.gsub(/(^\W+|\W+$)/, "")}.gsub!(/(\d+[:\.\b]?\d*)+/, " ")
21
+ t.scan(/(\b[A-Z][\.\b][A-Z][\.\b][A-Z][\.\b]|\b[A-Z][\.\b][A-Z][\.\b])/){|a| tweet.values[0][:acronyms] << a.first.strip}.gsub!(/(\b[A-Z][\.\b][A-Z][\.\b][A-Z][\.\b]|\b[A-Z][\.\b][A-Z][\.\b])/, " ")
22
+ t.scan(/(([A-Z]+\W){2,}|[A-Z]{4,}\W)/){|s| tweet.values[0][:shouts] << s.first.gsub(/\W/, " ").strip}.gsub!(/(([A-Z]+\W){2,}|[A-Z]{4,}\W)/, " ")
23
+ t.scan(/\b[A-Z]{2,3}\b/){|a| tweet.values[0][:acronyms] << a.strip}.gsub!(/\b[A-Z]{2,3}\b/, " ")
24
+ t.scan(/\w+['\/]?\w*/){|w| tweet.values[0][:words] << w.strip}.gsub!(/\w+['\/]?\w*/, " ")
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,40 @@
1
+ class Twexicon::Validator
2
+ attr_reader :username
3
+
4
+ TWITTER_RESERVED_WORDS = ["settings", "i", "search", "logout", "login", "download", "signup", "tos", "privacy", "account"]
5
+
6
+ def initialize
7
+ @username = ""
8
+ end
9
+
10
+ def run
11
+ until username.length > 0
12
+ puts "\nPlease enter a valid Twitter handle:"
13
+ @username = gets.gsub(/\W/, "")
14
+ until is_valid?
15
+ puts "Sorry, that doesn't seem to be a valid Twitter account. Please try again."
16
+ @username = gets.gsub(/\W/, "")
17
+ end
18
+ begin
19
+ @username = Nokogiri::HTML(open("https://twitter.com/#{username}")).css("span.u-linkComplex-target").first.text
20
+ rescue OpenURI::HTTPError, NoMethodError
21
+ puts "Hm, that doesn't seem to be an active Twitter account."
22
+ @username = ""
23
+ run
24
+ rescue SocketError
25
+ puts "Sorry, the network isn't responding. Please try again."
26
+ @username = ""
27
+ run
28
+ rescue
29
+ puts "Something went wrong. Please try again."
30
+ @username = ""
31
+ run
32
+ end
33
+ end
34
+ "#{username}"
35
+ end
36
+
37
+ def is_valid?
38
+ username.match(/^\w{1,15}$/) && !TWITTER_RESERVED_WORDS.include?(username.downcase)
39
+ end
40
+ end
@@ -0,0 +1,3 @@
1
+ module Twexicon
2
+ VERSION = "0.1.6"
3
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: twexicon
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.6
5
+ platform: ruby
6
+ authors:
7
+ - Gabe Jackson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-07-19 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.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.10'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.10'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: nokogiri
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.5'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.5'
69
+ description: 'When provided a valid Twitter handle, allows for command-line access
70
+ to the given user''s most recent tweets. Permits the retrieval of individual tweets
71
+ and can provide ordered lists outlining the following details from the user''s recent
72
+ tweets: their most frequently used words; any embedded pictures or videos; any embedded
73
+ hyperlinks; any #hashtags or @usernames mentioned; any numbers, such as years or
74
+ times of day; any acronyms mentioned; and, last but definitely not least, anything
75
+ the tweeter felt passionately enough about to SCREAM IN ALL CAPS.'
76
+ email: gj+code@mail.co.de
77
+ executables:
78
+ - twexicon
79
+ extensions: []
80
+ extra_rdoc_files: []
81
+ files:
82
+ - bin/console
83
+ - bin/setup
84
+ - bin/twexicon
85
+ - config/environment.rb
86
+ - lib/twexicon.rb
87
+ - lib/twexicon/analyzer.rb
88
+ - lib/twexicon/cli.rb
89
+ - lib/twexicon/scraper.rb
90
+ - lib/twexicon/validator.rb
91
+ - lib/twexicon/version.rb
92
+ homepage: https://github.com/gj/twexicon-cli-gem
93
+ licenses:
94
+ - MIT
95
+ metadata: {}
96
+ post_install_message:
97
+ rdoc_options: []
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ requirements: []
111
+ rubyforge_project:
112
+ rubygems_version: 2.6.6
113
+ signing_key:
114
+ specification_version: 4
115
+ summary: CLI gem that returns a given Twitter user's most recent tweets and does some
116
+ basic textual analysis.
117
+ test_files: []