searchlink 2.3.59

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/bin/searchlink +84 -0
  3. data/lib/searchlink/array.rb +7 -0
  4. data/lib/searchlink/config.rb +230 -0
  5. data/lib/searchlink/curl/html.rb +482 -0
  6. data/lib/searchlink/curl/json.rb +90 -0
  7. data/lib/searchlink/curl.rb +7 -0
  8. data/lib/searchlink/help.rb +103 -0
  9. data/lib/searchlink/output.rb +270 -0
  10. data/lib/searchlink/parse.rb +668 -0
  11. data/lib/searchlink/plist.rb +213 -0
  12. data/lib/searchlink/search.rb +70 -0
  13. data/lib/searchlink/searches/amazon.rb +25 -0
  14. data/lib/searchlink/searches/applemusic.rb +123 -0
  15. data/lib/searchlink/searches/bitly.rb +50 -0
  16. data/lib/searchlink/searches/definition.rb +67 -0
  17. data/lib/searchlink/searches/duckduckgo.rb +167 -0
  18. data/lib/searchlink/searches/github.rb +245 -0
  19. data/lib/searchlink/searches/google.rb +67 -0
  20. data/lib/searchlink/searches/helpers/chromium.rb +318 -0
  21. data/lib/searchlink/searches/helpers/firefox.rb +135 -0
  22. data/lib/searchlink/searches/helpers/safari.rb +133 -0
  23. data/lib/searchlink/searches/history.rb +166 -0
  24. data/lib/searchlink/searches/hook.rb +77 -0
  25. data/lib/searchlink/searches/itunes.rb +97 -0
  26. data/lib/searchlink/searches/lastfm.rb +41 -0
  27. data/lib/searchlink/searches/lyrics.rb +91 -0
  28. data/lib/searchlink/searches/pinboard.rb +183 -0
  29. data/lib/searchlink/searches/social.rb +105 -0
  30. data/lib/searchlink/searches/software.rb +27 -0
  31. data/lib/searchlink/searches/spelling.rb +59 -0
  32. data/lib/searchlink/searches/spotlight.rb +28 -0
  33. data/lib/searchlink/searches/stackoverflow.rb +31 -0
  34. data/lib/searchlink/searches/tmdb.rb +52 -0
  35. data/lib/searchlink/searches/twitter.rb +46 -0
  36. data/lib/searchlink/searches/wikipedia.rb +33 -0
  37. data/lib/searchlink/searches/youtube.rb +48 -0
  38. data/lib/searchlink/searches.rb +194 -0
  39. data/lib/searchlink/semver.rb +140 -0
  40. data/lib/searchlink/string.rb +469 -0
  41. data/lib/searchlink/url.rb +153 -0
  42. data/lib/searchlink/util.rb +87 -0
  43. data/lib/searchlink/version.rb +93 -0
  44. data/lib/searchlink/which.rb +175 -0
  45. data/lib/searchlink.rb +66 -0
  46. data/lib/tokens.rb +3 -0
  47. metadata +299 -0
@@ -0,0 +1,213 @@
1
+ # = plist
2
+ #
3
+ # Copyright 2006-2010 Ben Bleything and Patrick May
4
+ # Distributed under the MIT License
5
+ module Plist ; end
6
+
7
+ # === Load a plist file
8
+ # This is the main point of the library:
9
+ #
10
+ # r = Plist::parse_xml( filename_or_xml )
11
+ module Plist
12
+ def Plist::parse_xml( filename_or_xml )
13
+ listener = Listener.new
14
+ parser = StreamParser.new(filename_or_xml, listener)
15
+ parser.parse
16
+ listener.result
17
+ end
18
+
19
+ class Listener
20
+ attr_accessor :result, :open
21
+
22
+ def initialize
23
+ @result = nil
24
+ @open = Array.new
25
+ end
26
+
27
+ def tag_start(name, attributes)
28
+ @open.push PTag::mappings[name].new
29
+ end
30
+
31
+ def text( contents )
32
+ @open.last.text = contents if @open.last
33
+ end
34
+
35
+ def tag_end(name)
36
+ last = @open.pop
37
+ if @open.empty?
38
+ @result = last.to_ruby
39
+ else
40
+ @open.last.children.push last
41
+ end
42
+ end
43
+ end
44
+
45
+ class StreamParser
46
+ def initialize( plist_data_or_file, listener )
47
+ if plist_data_or_file.respond_to? :read
48
+ @xml = plist_data_or_file.read
49
+ elsif File.exists? plist_data_or_file
50
+ @xml = File.read( plist_data_or_file )
51
+ else
52
+ @xml = plist_data_or_file
53
+ end
54
+
55
+ @listener = listener
56
+ end
57
+
58
+ TEXT = /([^<]+)/
59
+ XMLDECL_PATTERN = /<\?xml\s+(.*?)\?>*/um
60
+ DOCTYPE_PATTERN = /\s*<!DOCTYPE\s+(.*?)(\[|>)/um
61
+ COMMENT_START = /\A<!--/u
62
+ COMMENT_END = /.*?-->/um
63
+
64
+ def parse
65
+ plist_tags = PTag::mappings.keys.join('|')
66
+ start_tag = /<(#{plist_tags})([^>]*)>/i
67
+ end_tag = /<\/(#{plist_tags})[^>]*>/i
68
+
69
+ require 'strscan'
70
+
71
+ @scanner = StringScanner.new(@xml)
72
+ until @scanner.eos?
73
+ if @scanner.scan(COMMENT_START)
74
+ @scanner.scan(COMMENT_END)
75
+ elsif @scanner.scan(XMLDECL_PATTERN)
76
+ elsif @scanner.scan(DOCTYPE_PATTERN)
77
+ elsif @scanner.scan(start_tag)
78
+ @listener.tag_start(@scanner[1], nil)
79
+ if (@scanner[2] =~ /\/$/)
80
+ @listener.tag_end(@scanner[1])
81
+ end
82
+ elsif @scanner.scan(TEXT)
83
+ @listener.text(@scanner[1])
84
+ elsif @scanner.scan(end_tag)
85
+ @listener.tag_end(@scanner[1])
86
+ else
87
+ raise "Unimplemented element"
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ class PTag
94
+ @@mappings = { }
95
+ def PTag::mappings
96
+ @@mappings
97
+ end
98
+
99
+ def PTag::inherited( sub_class )
100
+ key = sub_class.to_s.downcase
101
+ key.gsub!(/^plist::/, '' )
102
+ key.gsub!(/^p/, '') unless key == "plist"
103
+
104
+ @@mappings[key] = sub_class
105
+ end
106
+
107
+ attr_accessor :text, :children
108
+ def initialize
109
+ @children = Array.new
110
+ end
111
+
112
+ def to_ruby
113
+ raise "Unimplemented: " + self.class.to_s + "#to_ruby on #{self.inspect}"
114
+ end
115
+ end
116
+
117
+ class PList < PTag
118
+ def to_ruby
119
+ children.first.to_ruby if children.first
120
+ end
121
+ end
122
+
123
+ class PDict < PTag
124
+ def to_ruby
125
+ dict = Hash.new
126
+ key = nil
127
+
128
+ children.each do |c|
129
+ if key.nil?
130
+ key = c.to_ruby
131
+ else
132
+ dict[key] = c.to_ruby
133
+ key = nil
134
+ end
135
+ end
136
+
137
+ dict
138
+ end
139
+ end
140
+
141
+ class PKey < PTag
142
+ def to_ruby
143
+ CGI::unescapeHTML(text || '')
144
+ end
145
+ end
146
+
147
+ class PString < PTag
148
+ def to_ruby
149
+ CGI::unescapeHTML(text || '')
150
+ end
151
+ end
152
+
153
+ class PArray < PTag
154
+ def to_ruby
155
+ children.collect do |c|
156
+ c.to_ruby
157
+ end
158
+ end
159
+ end
160
+
161
+ class PInteger < PTag
162
+ def to_ruby
163
+ text.to_i
164
+ end
165
+ end
166
+
167
+ class PTrue < PTag
168
+ def to_ruby
169
+ true
170
+ end
171
+ end
172
+
173
+ class PFalse < PTag
174
+ def to_ruby
175
+ false
176
+ end
177
+ end
178
+
179
+ class PReal < PTag
180
+ def to_ruby
181
+ text.to_f
182
+ end
183
+ end
184
+
185
+ require 'date'
186
+ class PDate < PTag
187
+ def to_ruby
188
+ DateTime.parse(text)
189
+ end
190
+ end
191
+
192
+ require 'base64'
193
+ class PData < PTag
194
+ def to_ruby
195
+ data = Base64.decode64(text.gsub(/\s+/, ''))
196
+
197
+ begin
198
+ return Marshal.load(data)
199
+ rescue Exception
200
+ io = StringIO.new
201
+ io.write data
202
+ io.rewind
203
+ return io
204
+ end
205
+ end
206
+ end
207
+ end
208
+
209
+
210
+ # module Plist
211
+ # VERSION = '3.1.0'
212
+ # end
213
+
@@ -0,0 +1,70 @@
1
+ # Main SearchLink class
2
+ module SL
3
+ include URL
4
+
5
+ class SearchLink
6
+ include Plist
7
+
8
+ attr_reader :originput, :output, :clipboard
9
+
10
+ private
11
+
12
+ #
13
+ # Run a search
14
+ #
15
+ # @param[String] search_type The search type (abbreviation)
16
+ # @param[String] search_terms The search terms
17
+ # @param[String] link_text The link text
18
+ # @param[Integer] search_count The current search count
19
+ #
20
+ # @return [Array] [Url, link, text]
21
+ #
22
+ def do_search(search_type, search_terms, link_text = '', search_count = 0)
23
+ if (search_count % 5).zero?
24
+ SL.notify('Throttling for 5s')
25
+ sleep 5
26
+ end
27
+
28
+ description = SL::Searches.description_for_search(search_type)
29
+
30
+ SL.notify(description, search_terms)
31
+ return [false, search_terms, link_text] if search_terms.empty?
32
+
33
+ if SL::Searches.valid_search?(search_type)
34
+ url, title, link_text = SL::Searches.do_search(search_type, search_terms, link_text)
35
+ else
36
+ case search_type
37
+ when /^r$/ # simple replacement
38
+ if SL.config['validate_links'] && !SL::URL.valid_link?(search_terms)
39
+ return [false, "Link not valid: #{search_terms}", link_text]
40
+ end
41
+
42
+ title = SL::URL.title(search_terms) || search_terms
43
+
44
+ link_text = title if link_text == ''
45
+ return [search_terms, title, link_text]
46
+ else
47
+ if search_terms
48
+ if search_type =~ /.+?\.\w{2,}$/
49
+ url, title, link_text = SL.ddg(%(site:#{search_type} #{search_terms}), link_text)
50
+ else
51
+ url, title, link_text = SL.ddg(search_terms, link_text)
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ if link_text == ''
58
+ link_text = SL.titleize ? title : search_terms
59
+ end
60
+
61
+ if url && SL.config['validate_links'] && !SL::URL.valid_link?(url) && search_type !~ /^sp(ell)?/
62
+ [false, "Not found: #{url}", link_text]
63
+ elsif !url
64
+ [false, "No results: #{url}", link_text]
65
+ else
66
+ [url, title, link_text]
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,25 @@
1
+ module SL
2
+ # Amazon Search
3
+ class AmazonSearch
4
+ class << self
5
+ def settings
6
+ {
7
+ trigger: 'a',
8
+ searches: [
9
+ ['a', 'Amazon Search']
10
+ ]
11
+ }
12
+ end
13
+
14
+ def search(_, search_terms, link_text)
15
+ az_url, = SL.ddg("site:amazon.com #{search_terms}", link_text)
16
+ url, title = SL::URL.amazon_affiliatize(az_url, SL.config['amazon_partner'])
17
+ title ||= search_terms
18
+
19
+ [url, title, link_text]
20
+ end
21
+ end
22
+
23
+ SL::Searches.register 'amazon', :search, self
24
+ end
25
+ end
@@ -0,0 +1,123 @@
1
+ # title: Apple Music Search
2
+ # description: Search Apple Music
3
+ module SL
4
+ # Apple Music Search
5
+ class AppleMusicSearch
6
+ class << self
7
+ def settings
8
+ {
9
+ trigger: 'am(pod|art|alb|song)?e?',
10
+ searches: [
11
+ ['am', 'Apple Music'],
12
+ ['ampod', 'Apple Music Podcast'],
13
+ ['amart', 'Apple Music Artist'],
14
+ ['amalb', 'Apple Music Album'],
15
+ ['amsong', 'Apple Music Song'],
16
+ ['amalbe', 'Apple Music Album Embed'],
17
+ ['amsong', 'Apple Music Song Embed']
18
+ ]
19
+ }
20
+ end
21
+
22
+ def search(search_type, search_terms, link_text)
23
+ stype = search_type.downcase.sub(/^am/, '')
24
+ otype = :link
25
+ if stype =~ /e$/
26
+ otype = :embed
27
+ stype.sub!(/e$/, '')
28
+ end
29
+ result = case stype
30
+ when /^pod$/
31
+ applemusic(search_terms, 'podcast')
32
+ when /^art$/
33
+ applemusic(search_terms, 'music', 'musicArtist')
34
+ when /^alb$/
35
+ applemusic(search_terms, 'music', 'album')
36
+ when /^song$/
37
+ applemusic(search_terms, 'music', 'musicTrack')
38
+ else
39
+ applemusic(search_terms)
40
+ end
41
+
42
+ return [false, "Not found: #{search_terms}", link_text] unless result
43
+
44
+ # {:type=>,:id=>,:url=>,:title=>}
45
+ if otype == :embed && result[:type] =~ /(album|song)/
46
+ url = 'embed'
47
+ if result[:type] =~ /song/
48
+ link = %(https://embed.music.apple.com/#{SL.config['country_code'].downcase}/album/#{result[:album]}?i=#{result[:id]}&app=music#{SL.config['itunes_affiliate']})
49
+ height = 150
50
+ else
51
+ link = %(https://embed.music.apple.com/#{SL.config['country_code'].downcase}/album/#{result[:id]}?app=music#{SL.config['itunes_affiliate']})
52
+ height = 450
53
+ end
54
+
55
+ title = [
56
+ %(<iframe src="#{link}" allow="autoplay *; encrypted-media *;"),
57
+ %(frameborder="0" height="#{height}"),
58
+ %(style="width:100%;max-width:660px;overflow:hidden;background:transparent;"),
59
+ %(sandbox="allow-forms allow-popups allow-same-origin),
60
+ %(allow-scripts allow-top-navigation-by-user-activation"></iframe>)
61
+ ].join(' ')
62
+ else
63
+ url = result[:url]
64
+ title = result[:title]
65
+ end
66
+ [url, title, link_text]
67
+ end
68
+
69
+ # Search apple music
70
+ # terms => search terms (unescaped)
71
+ # media => music, podcast
72
+ # entity => optional: artist, song, album, podcast
73
+ # returns {:type=>,:id=>,:url=>,:title}
74
+ def applemusic(terms, media = 'music', entity = '')
75
+ url = "http://itunes.apple.com/search?term=#{terms.url_encode}&country=#{SL.config['country_code']}&media=#{media}&entity=#{entity}"
76
+ page = Curl::Json.new(url, compressed: true, symbolize_names: true)
77
+ json = page.json
78
+ return false unless json[:resultCount]&.positive?
79
+
80
+ output = process_result(json[:results][0])
81
+
82
+ return false if output.empty?
83
+
84
+ output
85
+ end
86
+
87
+ def process_result(result)
88
+ output = {}
89
+ aff = SL.config['itunes_affiliate']
90
+
91
+ case result[:wrapperType]
92
+ when 'track'
93
+ if result[:kind] == 'podcast'
94
+ output[:type] = 'podcast'
95
+ output[:id] = result[:collectionId]
96
+ output[:url] = result[:collectionViewUrl].to_am + aff
97
+ output[:title] = result[:collectionName]
98
+ else
99
+ output[:type] = 'song'
100
+ output[:album] = result[:collectionId]
101
+ output[:id] = result[:trackId]
102
+ output[:url] = result[:trackViewUrl].to_am + aff
103
+ output[:title] = "#{result[:trackName]} by #{result[:artistName]}"
104
+ end
105
+ when 'collection'
106
+ output[:type] = 'album'
107
+ output[:id] = result[:collectionId]
108
+ output[:url] = result[:collectionViewUrl].to_am + aff
109
+ output[:title] = "#{result[:collectionName]} by #{result[:artistName]}"
110
+ when 'artist'
111
+ output[:type] = 'artist'
112
+ output[:id] = result[:artistId]
113
+ output[:url] = result[:artistLinkUrl].to_am + aff
114
+ output[:title] = result[:artistName]
115
+ end
116
+
117
+ output
118
+ end
119
+ end
120
+
121
+ SL::Searches.register 'applemusic', :search, self
122
+ end
123
+ end
@@ -0,0 +1,50 @@
1
+ module SL
2
+ # Bit.ly link shortening
3
+ class BitlySearch
4
+ class << self
5
+ def settings
6
+ {
7
+ trigger: 'b(l|itly)',
8
+ searches: [
9
+ ['bl', 'bit.ly Shorten'],
10
+ ['bitly', 'bit.ly shorten']
11
+ ]
12
+ }
13
+ end
14
+
15
+ def search(_, search_terms, link_text)
16
+ if SL::URL.url?(search_terms)
17
+ link = search_terms
18
+ else
19
+ link, rtitle = SL.ddg(search_terms, link_text)
20
+ end
21
+
22
+ url, title = bitly_shorten(link, rtitle)
23
+ link_text = title || url
24
+ [url, title, link_text]
25
+ end
26
+
27
+ def bitly_shorten(url, title = nil)
28
+ unless SL.config.key?('bitly_access_token') && !SL.config['bitly_access_token'].empty?
29
+ SL.add_error('Bit.ly not configured', 'Missing access token')
30
+ return [false, title]
31
+ end
32
+
33
+ domain = SL.config.key?('bitly_domain') ? SL.config['bitly_domain'] : 'bit.ly'
34
+ long_url = url.dup
35
+ curl = TTY::Which.which('curl')
36
+ cmd = [
37
+ %(#{curl} -SsL -H 'Authorization: Bearer #{SL.config['bitly_access_token']}'),
38
+ %(-H 'Content-Type: application/json'),
39
+ '-X POST', %(-d '{ "long_url": "#{url}", "domain": "#{domain}" }'), 'https://api-ssl.bitly.com/v4/shorten'
40
+ ]
41
+ data = JSON.parse(`#{cmd.join(' ')}`.strip)
42
+ link = data['link']
43
+ title ||= SL::URL.title(long_url)
44
+ [link, title]
45
+ end
46
+ end
47
+
48
+ SL::Searches.register 'bitly', :search, self
49
+ end
50
+ end
@@ -0,0 +1,67 @@
1
+ module SL
2
+ # Dictionary Definition Search
3
+ class DefinitionSearch
4
+ class << self
5
+ # Returns a hash of settings for the search
6
+ #
7
+ # @return [Hash] the settings for the search
8
+ #
9
+ def settings
10
+ {
11
+ trigger: 'def(?:ine)?',
12
+ searches: [
13
+ ['def', 'Dictionary Definition'],
14
+ ['define', nil]
15
+ ]
16
+ }
17
+ end
18
+
19
+ # Searches for a definition of the given terms
20
+ #
21
+ # @param _ [String] unused
22
+ # @param search_terms [String] the terms to
23
+ # search for
24
+ # @param link_text [String] the text to use
25
+ # for the link
26
+ # @return [Array] the url, title, and link text for the
27
+ # search
28
+ #
29
+ def search(_, search_terms, link_text)
30
+ fix = SL.spell(search_terms)
31
+
32
+ if fix && search_terms.downcase != fix.downcase
33
+ SL.add_error('Spelling', "Spelling altered for '#{search_terms}' to '#{fix}'")
34
+ search_terms = fix
35
+ link_text = fix
36
+ end
37
+
38
+ url, title = define(search_terms)
39
+
40
+ url ? [url, title, link_text] : [false, false, link_text]
41
+ end
42
+
43
+ # Searches for a definition of the given terms
44
+ #
45
+ # @param terms [String] the terms to search for
46
+ # @return [Array] the url and title for the search
47
+ #
48
+ def define(terms)
49
+ def_url = "https://www.wordnik.com/words/#{terms.url_encode}"
50
+ curl = TTY::Which.which('curl')
51
+ body = `#{curl} -sSL '#{def_url}'`
52
+ if body =~ /id="define"/
53
+ first_definition = body.match(%r{(?mi)(?:id="define"[\s\S]*?<li>)([\s\S]*?)</li>})[1]
54
+ parts = first_definition.match(%r{<abbr title="partOfSpeech">(.*?)</abbr> (.*?)$})
55
+ return [def_url, "(#{parts[1]}) #{parts[2]}".gsub(%r{</?.*?>}, '').strip]
56
+ end
57
+
58
+ false
59
+ rescue StandardError
60
+ false
61
+ end
62
+ end
63
+
64
+ # Registers the search with the SL::Searches module
65
+ SL::Searches.register 'definition', :search, self
66
+ end
67
+ end