searchlink 2.3.59
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/searchlink +84 -0
- data/lib/searchlink/array.rb +7 -0
- data/lib/searchlink/config.rb +230 -0
- data/lib/searchlink/curl/html.rb +482 -0
- data/lib/searchlink/curl/json.rb +90 -0
- data/lib/searchlink/curl.rb +7 -0
- data/lib/searchlink/help.rb +103 -0
- data/lib/searchlink/output.rb +270 -0
- data/lib/searchlink/parse.rb +668 -0
- data/lib/searchlink/plist.rb +213 -0
- data/lib/searchlink/search.rb +70 -0
- data/lib/searchlink/searches/amazon.rb +25 -0
- data/lib/searchlink/searches/applemusic.rb +123 -0
- data/lib/searchlink/searches/bitly.rb +50 -0
- data/lib/searchlink/searches/definition.rb +67 -0
- data/lib/searchlink/searches/duckduckgo.rb +167 -0
- data/lib/searchlink/searches/github.rb +245 -0
- data/lib/searchlink/searches/google.rb +67 -0
- data/lib/searchlink/searches/helpers/chromium.rb +318 -0
- data/lib/searchlink/searches/helpers/firefox.rb +135 -0
- data/lib/searchlink/searches/helpers/safari.rb +133 -0
- data/lib/searchlink/searches/history.rb +166 -0
- data/lib/searchlink/searches/hook.rb +77 -0
- data/lib/searchlink/searches/itunes.rb +97 -0
- data/lib/searchlink/searches/lastfm.rb +41 -0
- data/lib/searchlink/searches/lyrics.rb +91 -0
- data/lib/searchlink/searches/pinboard.rb +183 -0
- data/lib/searchlink/searches/social.rb +105 -0
- data/lib/searchlink/searches/software.rb +27 -0
- data/lib/searchlink/searches/spelling.rb +59 -0
- data/lib/searchlink/searches/spotlight.rb +28 -0
- data/lib/searchlink/searches/stackoverflow.rb +31 -0
- data/lib/searchlink/searches/tmdb.rb +52 -0
- data/lib/searchlink/searches/twitter.rb +46 -0
- data/lib/searchlink/searches/wikipedia.rb +33 -0
- data/lib/searchlink/searches/youtube.rb +48 -0
- data/lib/searchlink/searches.rb +194 -0
- data/lib/searchlink/semver.rb +140 -0
- data/lib/searchlink/string.rb +469 -0
- data/lib/searchlink/url.rb +153 -0
- data/lib/searchlink/util.rb +87 -0
- data/lib/searchlink/version.rb +93 -0
- data/lib/searchlink/which.rb +175 -0
- data/lib/searchlink.rb +66 -0
- data/lib/tokens.rb +3 -0
- 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
|