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,167 @@
|
|
1
|
+
module SL
|
2
|
+
# DuckDuckGo Search
|
3
|
+
class DuckDuckGoSearch
|
4
|
+
class << self
|
5
|
+
# Returns a hash of settings for the DuckDuckGoSearch
|
6
|
+
# class
|
7
|
+
#
|
8
|
+
# @return [Hash] settings for the DuckDuckGoSearch
|
9
|
+
# class
|
10
|
+
#
|
11
|
+
def settings
|
12
|
+
{
|
13
|
+
trigger: '(?:g|ddg|z|ddgimg)',
|
14
|
+
searches: [
|
15
|
+
['g', 'Google/DuckDuckGo Search'],
|
16
|
+
['ddg', 'DuckDuckGo Search'],
|
17
|
+
['z', 'DDG Zero Click Search'],
|
18
|
+
['ddgimg', 'Return the first image from the destination page']
|
19
|
+
]
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
# Searches DuckDuckGo for the given search terms
|
24
|
+
#
|
25
|
+
# @param search_type [String] the type of
|
26
|
+
# search to perform
|
27
|
+
# @param search_terms [String] the terms to
|
28
|
+
# search for
|
29
|
+
# @param link_text [String] the text to
|
30
|
+
# display for the link
|
31
|
+
# @return [Array] an array containing the URL, title, and
|
32
|
+
# link text
|
33
|
+
#
|
34
|
+
def search(search_type, search_terms, link_text)
|
35
|
+
return zero_click(search_terms, link_text) if search_type =~ /^z$/
|
36
|
+
|
37
|
+
# return SL.ddg(search_terms, link_text) if search_type == 'g' && SL::GoogleSearch.test_for_key
|
38
|
+
|
39
|
+
begin
|
40
|
+
terms = "%5C#{search_terms.url_encode}"
|
41
|
+
page = Curl::Html.new("https://duckduckgo.com/?q=#{terms}", compressed: true)
|
42
|
+
|
43
|
+
locs = page.meta['refresh'].match(%r{/l/\?uddg=(.*?)$})
|
44
|
+
locs = page.body.match(%r{/l/\?uddg=(.*?)'}) if locs.nil?
|
45
|
+
locs = page.body.match(/url=(.*?)'/) if locs.nil?
|
46
|
+
|
47
|
+
return false if locs.nil?
|
48
|
+
|
49
|
+
url = locs[1].url_decode.sub(/&rut=\w+/, '')
|
50
|
+
|
51
|
+
result = url.strip.url_decode || false
|
52
|
+
return false unless result
|
53
|
+
|
54
|
+
return false if result =~ /internal-search\.duckduckgo\.com/
|
55
|
+
|
56
|
+
# output_url = CGI.unescape(result)
|
57
|
+
output_url = result
|
58
|
+
|
59
|
+
output_title = if SL.config['include_titles'] || SL.titleize
|
60
|
+
SL::URL.title(output_url) || ''
|
61
|
+
else
|
62
|
+
''
|
63
|
+
end
|
64
|
+
|
65
|
+
output_url = SL.first_image(output_url) if search_type =~ /img$/
|
66
|
+
|
67
|
+
[output_url, output_title, link_text]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Searches DuckDuckGo for the given search terms and
|
72
|
+
# returns a zero click result
|
73
|
+
#
|
74
|
+
# @param search_terms [String] the terms to
|
75
|
+
# search for
|
76
|
+
# @param link_text [String] the text to
|
77
|
+
# display for the link
|
78
|
+
# @param disambiguate [Boolean] whether to
|
79
|
+
# disambiguate the search
|
80
|
+
#
|
81
|
+
# @return [Array] an array containing the URL,
|
82
|
+
# title, and link text
|
83
|
+
#
|
84
|
+
def zero_click(search_terms, link_text, disambiguate: false)
|
85
|
+
search_terms.gsub!(/%22/, '"')
|
86
|
+
d = disambiguate ? '0' : '1'
|
87
|
+
url = "http://api.duckduckgo.com/?q=#{search_terms.url_encode}&format=json&no_redirect=1&no_html=1&skip_disambig=#{d}"
|
88
|
+
result = Curl::Json.new(url, symbolize_names: true).json
|
89
|
+
return SL.ddg(terms, link_text) unless result
|
90
|
+
|
91
|
+
wiki_link = result[:AbstractURL] || result[:Redirect]
|
92
|
+
title = result[:Heading] || false
|
93
|
+
|
94
|
+
if !wiki_link.empty? && !title.empty?
|
95
|
+
[wiki_link, title, link_text]
|
96
|
+
elsif disambiguate
|
97
|
+
SL.ddg(search_terms, link_text)
|
98
|
+
else
|
99
|
+
zero_click(search_terms, link_text, disambiguate: true)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Registers the DuckDuckGoSearch class with the Searches
|
105
|
+
# module
|
106
|
+
# @param name [String] the name of the search
|
107
|
+
# @param type [Symbol] the type of search to
|
108
|
+
# perform
|
109
|
+
# @param klass [Class] the class to register
|
110
|
+
SL::Searches.register 'duckduckgo', :search, self
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# SL module methods
|
115
|
+
module SL
|
116
|
+
class << self
|
117
|
+
# Performs a Google search if API key is available,
|
118
|
+
# otherwise defaults to DuckDuckGo
|
119
|
+
#
|
120
|
+
# @param search_terms [String] The search terms
|
121
|
+
# @param link_text [String] The link text
|
122
|
+
# @param timeout [Integer] The timeout
|
123
|
+
#
|
124
|
+
def google(search_terms, link_text = nil, timeout: SL.config['timeout'], image: false)
|
125
|
+
if SL::GoogleSearch.test_for_key
|
126
|
+
s_class = 'google'
|
127
|
+
s_type = image ? 'img' : 'gg'
|
128
|
+
else
|
129
|
+
s_class = 'duckduckgo'
|
130
|
+
s_type = image ? 'ddgimg' : 'g'
|
131
|
+
end
|
132
|
+
search = proc { SL::Searches.plugins[:search][s_class][:class].search(s_type, search_terms, link_text) }
|
133
|
+
SL::Util.search_with_timeout(search, timeout)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Performs a DuckDuckGo search with the given search
|
137
|
+
# terms and link text. If link text is not provided, the
|
138
|
+
# first result will be returned. The search will timeout
|
139
|
+
# after the given number of seconds.
|
140
|
+
#
|
141
|
+
# @param search_terms [String] The search terms to
|
142
|
+
# use
|
143
|
+
# @param link_text [String] The text of the
|
144
|
+
# link to search for
|
145
|
+
# @param timeout [Integer] The timeout for
|
146
|
+
# the search in seconds
|
147
|
+
# @return [SL::Searches::Result] The search result
|
148
|
+
#
|
149
|
+
def ddg(search_terms, link_text = nil, timeout: SL.config['timeout'], google: true, image: false)
|
150
|
+
if google && SL::GoogleSearch.test_for_key
|
151
|
+
s_class = 'google'
|
152
|
+
s_type = image ? 'img' : 'gg'
|
153
|
+
else
|
154
|
+
s_class = 'duckduckgo'
|
155
|
+
s_type = image ? 'ddgimg' : 'g'
|
156
|
+
end
|
157
|
+
|
158
|
+
search = proc { SL::Searches.plugins[:search][s_class][:class].search(s_type, search_terms, link_text) }
|
159
|
+
SL::Util.search_with_timeout(search, timeout)
|
160
|
+
end
|
161
|
+
|
162
|
+
def first_image(url)
|
163
|
+
images = Curl::Html.new(url).images
|
164
|
+
images.filter { |img| img[:type] == 'img' }.first[:src]
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,245 @@
|
|
1
|
+
module SL
|
2
|
+
# GitHub search
|
3
|
+
class GitHubSearch
|
4
|
+
class << self
|
5
|
+
def settings
|
6
|
+
{
|
7
|
+
trigger: '(?:giste?|ghu?)',
|
8
|
+
searches: [
|
9
|
+
['gh', 'GitHub User/Repo Link'],
|
10
|
+
['ghu', 'GitHub User Search'],
|
11
|
+
['gist', 'Gist Search'],
|
12
|
+
['giste', 'Gist Embed']
|
13
|
+
]
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def search(search_type, search_terms, link_text)
|
18
|
+
case search_type
|
19
|
+
when /^gist/
|
20
|
+
url, title, link_text = gist(search_terms, search_type, link_text)
|
21
|
+
when /^ghu$/
|
22
|
+
url, title, link_text = github_user(search_terms, link_text)
|
23
|
+
else
|
24
|
+
url, title, link_text = github(search_terms, link_text)
|
25
|
+
end
|
26
|
+
|
27
|
+
link_text = title if link_text == '' || link_text == search_terms
|
28
|
+
|
29
|
+
[url, title, link_text]
|
30
|
+
end
|
31
|
+
|
32
|
+
def github_search_curl(endpoint, query)
|
33
|
+
headers = {
|
34
|
+
'Accept' => 'application/vnd.github+json',
|
35
|
+
'X-GitHub-Api-Version' => '2022-11-28',
|
36
|
+
}
|
37
|
+
headers['Authorization'] = "Bearer #{Secrets::GH_AUTH_TOKEN}" if Secrets::GH_AUTH_TOKEN
|
38
|
+
|
39
|
+
url = "https://api.github.com/search/#{endpoint}?q=#{query.url_encode}&per_page=1&page=1&order=desc"
|
40
|
+
res = Curl::Json.new(url, headers: headers)
|
41
|
+
|
42
|
+
if res.json.key?('total_count') && res.json['total_count'].positive?
|
43
|
+
res.json['items'][0]
|
44
|
+
else
|
45
|
+
false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def user_gists(user, search_terms, page = 1)
|
50
|
+
headers = {
|
51
|
+
'Accept' => 'application/vnd.github+json',
|
52
|
+
'X-GitHub-Api-Version' => '2022-11-28'
|
53
|
+
}
|
54
|
+
headers['Authorization'] = "Bearer #{Secrets::GH_AUTH_TOKEN}" if Secrets::GH_AUTH_TOKEN
|
55
|
+
|
56
|
+
url = "https://api.github.com/users/#{user}/gists?per_page=100&page=#{page}"
|
57
|
+
|
58
|
+
res = Curl::Json.new(url, headers: headers).json
|
59
|
+
|
60
|
+
best = nil
|
61
|
+
best = filter_gists(res, search_terms) if res
|
62
|
+
|
63
|
+
if !best && res.count == 100
|
64
|
+
SL.notify('Paging', "Getting page #{page + 1} of #{user} gists")
|
65
|
+
best = user_gists(user, search_terms, page + 1)
|
66
|
+
end
|
67
|
+
|
68
|
+
best
|
69
|
+
end
|
70
|
+
|
71
|
+
def github(search_terms, link_text)
|
72
|
+
terms = search_terms.split(%r{[ /]+})
|
73
|
+
# SL.config['remove_seo'] = false
|
74
|
+
|
75
|
+
url = case terms.count
|
76
|
+
when 2
|
77
|
+
"https://github.com/#{terms[0]}/#{terms[1]}"
|
78
|
+
when 1
|
79
|
+
"https://github.com/#{terms[0]}"
|
80
|
+
else
|
81
|
+
nurl, title, link_text = SL.ddg("site:github.com #{search_terms}", link_text)
|
82
|
+
nurl
|
83
|
+
end
|
84
|
+
|
85
|
+
if SL::URL.valid_link?(url)
|
86
|
+
title = SL::URL.title(url) if url && title.nil?
|
87
|
+
|
88
|
+
[url, title, link_text]
|
89
|
+
else
|
90
|
+
SL.notify('Searching GitHub', 'Repo not found, performing search')
|
91
|
+
search_github(search_terms, link_text)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def github_user(search_terms, link_text)
|
96
|
+
if search_terms.split(/ /).count > 1
|
97
|
+
query = %(#{search_terms} in:name)
|
98
|
+
res = github_search_curl('users', query)
|
99
|
+
else
|
100
|
+
query = %(user:#{search_terms})
|
101
|
+
res = github_search_curl('users', query)
|
102
|
+
res ||= github_search_curl('users', search_terms)
|
103
|
+
end
|
104
|
+
|
105
|
+
if res
|
106
|
+
url = res['html_url']
|
107
|
+
title = res['login']
|
108
|
+
|
109
|
+
[url, title, link_text]
|
110
|
+
else
|
111
|
+
[false, false, link_text]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def search_github(search_terms, link_text)
|
116
|
+
search_terms.gsub!(%r{(\S+)/(\S+)}, 'user:\1 \2')
|
117
|
+
search_terms.gsub!(/\bu\w*:(\w+)/, 'user:\1')
|
118
|
+
search_terms.gsub!(/\bl\w*:(\w+)/, 'language:\1')
|
119
|
+
search_terms.gsub!(/\bin?:r\w*/, 'in:readme')
|
120
|
+
search_terms.gsub!(/\bin?:t\w*/, 'in:topics')
|
121
|
+
search_terms.gsub!(/\bin?:d\w*/, 'in:description')
|
122
|
+
search_terms.gsub!(/\bin?:(t(itle)?|n(ame)?)/, 'in:name')
|
123
|
+
search_terms.gsub!(/\br:/, 'repo:')
|
124
|
+
|
125
|
+
search_terms += ' in:title' unless search_terms =~ /(in|user|repo):/
|
126
|
+
|
127
|
+
res = github_search_curl('repositories', search_terms)
|
128
|
+
|
129
|
+
return false unless res
|
130
|
+
|
131
|
+
url = res['html_url']
|
132
|
+
title = res['description'] || res['full_name']
|
133
|
+
[url, title, link_text]
|
134
|
+
end
|
135
|
+
|
136
|
+
def search_user_gists(user, search_terms)
|
137
|
+
best_gist = user_gists(user, search_terms, 1)
|
138
|
+
|
139
|
+
return false unless best_gist
|
140
|
+
|
141
|
+
best_gist
|
142
|
+
end
|
143
|
+
|
144
|
+
def filter_gists(gists, search_terms)
|
145
|
+
score = 0
|
146
|
+
gists.map! do |g|
|
147
|
+
{
|
148
|
+
url: g['html_url'],
|
149
|
+
description: g['description'],
|
150
|
+
files: g['files'].map { |file, info| { filename: file, raw: info['raw_url'] } }
|
151
|
+
}
|
152
|
+
end
|
153
|
+
matches = []
|
154
|
+
gists.each do |g|
|
155
|
+
if g.key?(:files)
|
156
|
+
g[:files].each do |f|
|
157
|
+
next unless f[:filename]
|
158
|
+
|
159
|
+
score = f[:filename].matches_score(search_terms.gsub(/[^a-z0-9]/, ' '))
|
160
|
+
|
161
|
+
if score > 5
|
162
|
+
url = "#{g[:url]}#file-#{f[:filename].gsub(/\./, '-')}"
|
163
|
+
matches << { url: url, title: f[:filename], score: score }
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
score = g[:description].nil? ? 0 : g[:description].matches_score(search_terms.gsub(/[^a-z0-9]/, ' '))
|
169
|
+
matches << { url: g[:url], title: g[:files][0][:filename], score: score } if score > 5
|
170
|
+
end
|
171
|
+
|
172
|
+
return false if matches.empty?
|
173
|
+
|
174
|
+
matches.max_by { |m| m[:score] }
|
175
|
+
end
|
176
|
+
|
177
|
+
def gist(terms, type, link_text)
|
178
|
+
terms.strip!
|
179
|
+
case terms
|
180
|
+
# If an id (and optional file) are given, expand it to include username an generate link
|
181
|
+
when %r{^(?<id>[a-z0-9]{32}|[0-9]{6,10})(?:[#/](?<file>(?:file-)?.*?))?$}
|
182
|
+
m = Regexp.last_match
|
183
|
+
res = Curl::Html.new("https://gist.github.com/#{m['id']}", headers_only: true)
|
184
|
+
url = res.headers['location']
|
185
|
+
title = SL::URL.title(url)
|
186
|
+
|
187
|
+
url = "#{url}##{m['file']}" if m['file']
|
188
|
+
# If a user an id (an o) are given, convert to a link
|
189
|
+
when %r{^(?<u>\w+)/(?<id>[a-z0-9]{32}|[0-9]{6,10})(?:[#/](?<file>(?:file-)?.*?))?$}
|
190
|
+
m = Regexp.last_match
|
191
|
+
url = "https://gist.github.com/#{m['u']}/#{m['id']}"
|
192
|
+
title = SL::URL.title(url)
|
193
|
+
|
194
|
+
url = "#{url}##{m['file']}" if m['file']
|
195
|
+
# if a full gist URL is given, simply clean it up
|
196
|
+
when %r{(?<url>https://gist.github.com/(?:(?<user>\w+)/)?(?<id>[a-z0-9]{32}|[0-9]{6,10}))(?:[#/](?<file>(?:file-)?.*?))?$}
|
197
|
+
m = Regexp.last_match
|
198
|
+
url = m['url']
|
199
|
+
title = SL::URL.title(url)
|
200
|
+
|
201
|
+
url = "#{url}##{m['file']}" if m['file']
|
202
|
+
# Otherwise do a search of gist.github.com for the keywords
|
203
|
+
else
|
204
|
+
if terms.split(/ +/).count > 1
|
205
|
+
parts = terms.split(/ +/)
|
206
|
+
gist = search_user_gists(parts[0], parts[1..].join(' '))
|
207
|
+
|
208
|
+
if gist
|
209
|
+
url = gist[:url]
|
210
|
+
title = gist[:title]
|
211
|
+
else
|
212
|
+
url, title, link_text = SL.ddg("site:gist.github.com #{terms}", link_text)
|
213
|
+
end
|
214
|
+
else
|
215
|
+
url, title, link_text = SL.ddg("site:gist.github.com #{terms}", link_text)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
# Assuming we retrieved a full gist URL
|
220
|
+
if url =~ %r{https://gist.github.com/(?:(?<user>[^/]+)/)?(?<id>[a-z0-9]+?)(?:[#/](?<file>(?:file-)?.*?))?$}
|
221
|
+
m = Regexp.last_match
|
222
|
+
user = m['user']
|
223
|
+
id = m['id']
|
224
|
+
|
225
|
+
# If we're trying to create an embed, convert elements to a JS embed script
|
226
|
+
if type =~ /e$/
|
227
|
+
url = if m['file']
|
228
|
+
"https://gist.github.com/#{user}/#{id}.js?file=#{m['file'].fix_gist_file}"
|
229
|
+
else
|
230
|
+
"https://gist.github.com/#{user}/#{id}.js"
|
231
|
+
end
|
232
|
+
|
233
|
+
['embed', %(<script src="#{url}"></script>), link_text]
|
234
|
+
else
|
235
|
+
[url, title, link_text]
|
236
|
+
end
|
237
|
+
else
|
238
|
+
[false, title, link_text]
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
SL::Searches.register 'github', :search, self
|
244
|
+
end
|
245
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module SL
|
2
|
+
# Google Search
|
3
|
+
class GoogleSearch
|
4
|
+
class << self
|
5
|
+
attr_reader :api_key
|
6
|
+
|
7
|
+
def settings
|
8
|
+
{
|
9
|
+
trigger: '(g(oo)?g(le?)?|img)',
|
10
|
+
searches: [
|
11
|
+
['gg', 'Google Search'],
|
12
|
+
['img', 'First image from result']
|
13
|
+
]
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_for_key
|
18
|
+
return false unless SL.config.key?('google_api_key') && SL.config['google_api_key']
|
19
|
+
|
20
|
+
key = SL.config['google_api_key']
|
21
|
+
return false if key =~ /^(x{4,})?$/i
|
22
|
+
|
23
|
+
@api_key = key
|
24
|
+
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
def search(search_type, search_terms, link_text)
|
29
|
+
image = search_type =~ /img$/ ? true : false
|
30
|
+
|
31
|
+
unless test_for_key
|
32
|
+
SL.add_error('api key', 'Missing Google API Key')
|
33
|
+
return false
|
34
|
+
end
|
35
|
+
|
36
|
+
url = "https://customsearch.googleapis.com/customsearch/v1?cx=338419ee5ac894523&q=#{ERB::Util.url_encode(search_terms)}&num=1&key=#{@api_key}"
|
37
|
+
json = Curl::Json.new(url).json
|
38
|
+
|
39
|
+
if json['error'] && json['error']['code'].to_i == 429
|
40
|
+
SL.notify('api limit', 'Google API limit reached, defaulting to DuckDuckGo')
|
41
|
+
return SL.ddg(terms, link_text, google: false, image: image)
|
42
|
+
end
|
43
|
+
|
44
|
+
unless json['queries']['request'][0]['totalResults'].to_i.positive?
|
45
|
+
SL.notify('no results', 'Google returned no results, defaulting to DuckDuckGo')
|
46
|
+
return SL.ddg(terms, link_text, google: false, image: image)
|
47
|
+
end
|
48
|
+
|
49
|
+
result = json['items'][0]
|
50
|
+
return false if result.nil?
|
51
|
+
|
52
|
+
output_url = result['link']
|
53
|
+
output_title = result['title']
|
54
|
+
output_title.remove_seo!(output_url) if SL.config['remove_seo']
|
55
|
+
|
56
|
+
output_url = SL.first_image if search_type =~ /img$/
|
57
|
+
|
58
|
+
[output_url, output_title, link_text]
|
59
|
+
rescue StandardError
|
60
|
+
SL.notify('Google error', 'Error fetching Google results, switching to DuckDuckGo')
|
61
|
+
SL.ddg(search_terms, link_text, google: false, image: image)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
SL::Searches.register 'google', :search, self
|
66
|
+
end
|
67
|
+
end
|