searchlink 2.3.68 → 2.3.70
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 +4 -4
- data/lib/searchlink/exceptions.rb +29 -0
- data/lib/searchlink/number.rb +13 -0
- data/lib/searchlink/output.rb +7 -4
- data/lib/searchlink/parse.rb +221 -197
- data/lib/searchlink/script_plugin.rb +84 -0
- data/lib/searchlink/searches/duckduckgo.rb +3 -3
- data/lib/searchlink/searches/google.rb +2 -2
- data/lib/searchlink/searches/helpers/chromium.rb +207 -50
- data/lib/searchlink/searches/helpers/safari.rb +1 -0
- data/lib/searchlink/searches.rb +32 -7
- data/lib/searchlink/semver.rb +7 -7
- data/lib/searchlink/string.rb +8 -0
- data/lib/searchlink/version.rb +1 -1
- data/lib/searchlink.rb +10 -0
- metadata +5 -2
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# title: Script plugin
|
|
4
|
+
# description: Load custom searches from non-ruby scripts
|
|
5
|
+
module SL
|
|
6
|
+
# Script Search
|
|
7
|
+
class ScriptSearch
|
|
8
|
+
def initialize(config)
|
|
9
|
+
@filename = config['filename']
|
|
10
|
+
@path = config['path']
|
|
11
|
+
|
|
12
|
+
%w[trigger searches name script].each do |key|
|
|
13
|
+
raise PluginError.new(%(configuration missing key "#{key}"), plugin: @filename) unless config.key?(key)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
@trigger = config['trigger']
|
|
17
|
+
@searches = config['searches']
|
|
18
|
+
@name = config['name']
|
|
19
|
+
@script = find_script(config['script'])
|
|
20
|
+
|
|
21
|
+
unless File.executable?(@script)
|
|
22
|
+
raise PluginError.new(%(script "#{File.basename(@script)}" not executable\nrun `chmod a+x #{@script.shorten_path}` to correct),
|
|
23
|
+
plugin: @filename)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class << self
|
|
27
|
+
def settings
|
|
28
|
+
{
|
|
29
|
+
trigger: @trigger,
|
|
30
|
+
searches: @searches
|
|
31
|
+
}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def search(search_type, search_terms, link_text)
|
|
35
|
+
type = Shellwords.escape(search_type)
|
|
36
|
+
terms = Shellwords.escape(search_terms)
|
|
37
|
+
text = Shellwords.escape(link_text)
|
|
38
|
+
|
|
39
|
+
stdout = `#{[@script, type, terms, text].join(' ')} 2>&1`
|
|
40
|
+
|
|
41
|
+
unless $CHILD_STATUS.success?
|
|
42
|
+
raise PluginError.new(%("#{File.basename(@script)}" returned error #{$CHILD_STATUS.exitstatus}\n#{stdout}),
|
|
43
|
+
plugin: @filename)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
begin
|
|
47
|
+
res = JSON.parse(stdout)
|
|
48
|
+
rescue JSON::ParserError
|
|
49
|
+
res = YAML.safe_load(stdout)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
unless res.is_a?(Hash)
|
|
53
|
+
raise PluginError.new(%(invalid results from "#{File.basename(@script)}", must be YAML or JSON string),
|
|
54
|
+
plugin: @filename)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
%w[url title link_text].each do |key|
|
|
58
|
+
unless res.key?(key)
|
|
59
|
+
raise PluginError.new(%("#{File.basename(@script)}" output missing key "#{key}"), plugin: @filename)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
[res['url'], res['title'], res['link_text']]
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
SL::Searches.register @name, :search, self
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def find_script(script)
|
|
71
|
+
return script if File.exist?(File.expand_path(script))
|
|
72
|
+
|
|
73
|
+
base = File.expand_path('~/.config/searchlink/plugins')
|
|
74
|
+
first = File.join(base, script)
|
|
75
|
+
return first if File.exist?(first)
|
|
76
|
+
|
|
77
|
+
base = File.expand_path('~/.config/searchlink')
|
|
78
|
+
second = File.join(base, script)
|
|
79
|
+
return second if File.exist?(second)
|
|
80
|
+
|
|
81
|
+
raise PluginError.new(%(Script plugin script "#{script}" not found), plugin: @filename)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -36,7 +36,7 @@ module SL
|
|
|
36
36
|
def search(search_type, search_terms, link_text)
|
|
37
37
|
return zero_click(search_terms, link_text) if search_type =~ /^z$/
|
|
38
38
|
|
|
39
|
-
# return SL.ddg(search_terms, link_text) if search_type == 'g' && SL::GoogleSearch.
|
|
39
|
+
# return SL.ddg(search_terms, link_text) if search_type == 'g' && SL::GoogleSearch.api_key?
|
|
40
40
|
|
|
41
41
|
terms = "%5C#{search_terms.url_encode}"
|
|
42
42
|
page = Curl::Html.new("https://duckduckgo.com/?q=#{terms}", compressed: true)
|
|
@@ -122,7 +122,7 @@ module SL
|
|
|
122
122
|
# @param timeout [Integer] The timeout
|
|
123
123
|
#
|
|
124
124
|
def google(search_terms, link_text = nil, timeout: SL.config['timeout'], image: false)
|
|
125
|
-
if SL::GoogleSearch.
|
|
125
|
+
if SL::GoogleSearch.api_key?
|
|
126
126
|
s_class = 'google'
|
|
127
127
|
s_type = image ? 'img' : 'gg'
|
|
128
128
|
else
|
|
@@ -147,7 +147,7 @@ module SL
|
|
|
147
147
|
# @return [SL::Searches::Result] The search result
|
|
148
148
|
#
|
|
149
149
|
def ddg(search_terms, link_text = nil, timeout: SL.config['timeout'], google: true, image: false)
|
|
150
|
-
if google && SL::GoogleSearch.
|
|
150
|
+
if google && SL::GoogleSearch.api_key?
|
|
151
151
|
s_class = 'google'
|
|
152
152
|
s_type = image ? 'img' : 'gg'
|
|
153
153
|
else
|
|
@@ -16,7 +16,7 @@ module SL
|
|
|
16
16
|
}
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
def
|
|
19
|
+
def api_key?
|
|
20
20
|
return false unless SL.config.key?('google_api_key') && SL.config['google_api_key']
|
|
21
21
|
|
|
22
22
|
key = SL.config['google_api_key']
|
|
@@ -30,7 +30,7 @@ module SL
|
|
|
30
30
|
def search(search_type, search_terms, link_text)
|
|
31
31
|
image = search_type =~ /img$/ ? true : false
|
|
32
32
|
|
|
33
|
-
unless
|
|
33
|
+
unless api_key?
|
|
34
34
|
SL.add_error('api key', 'Missing Google API Key')
|
|
35
35
|
return false
|
|
36
36
|
end
|
|
@@ -31,14 +31,25 @@ module SL
|
|
|
31
31
|
## @return [Array] Single bookmark, [url, title, date]
|
|
32
32
|
##
|
|
33
33
|
def search_brave_history(term)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
base = File.expand_path('~/Library/Application Support/BraveSoftware/Brave-Browser/')
|
|
35
|
+
profiles = Dir.glob('**/History', base: base)
|
|
36
|
+
profiles.delete_if { |p| p =~ /^Snapshots/ }
|
|
37
|
+
profiles.map! { |f| File.join(base, f) }
|
|
38
|
+
|
|
39
|
+
res = false
|
|
40
|
+
|
|
41
|
+
profiles.each do |bookmarks|
|
|
42
|
+
next unless File.exist?(bookmarks)
|
|
43
|
+
|
|
44
|
+
profile = bookmarks.match(%r{Browser/([^/]+)/})[1]
|
|
45
|
+
|
|
46
|
+
SL.notify("Searching Brave History for profile #{profile}", term)
|
|
47
|
+
res = search_chromium_history(bookmarks, term)
|
|
48
|
+
|
|
49
|
+
break if res
|
|
41
50
|
end
|
|
51
|
+
|
|
52
|
+
res
|
|
42
53
|
end
|
|
43
54
|
|
|
44
55
|
## Search Edge history
|
|
@@ -48,14 +59,25 @@ module SL
|
|
|
48
59
|
## @return [Array] Single bookmark, [url, title, date]
|
|
49
60
|
##
|
|
50
61
|
def search_edge_history(term)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
62
|
+
base = File.expand_path('~/Library/Application Support/Microsoft Edge/')
|
|
63
|
+
profiles = Dir.glob('**/History', base: base)
|
|
64
|
+
profiles.delete_if { |p| p =~ /^Snapshots/ }
|
|
65
|
+
profiles.map! { |f| File.join(base, f) }
|
|
66
|
+
|
|
67
|
+
res = false
|
|
68
|
+
|
|
69
|
+
profiles.each do |bookmarks|
|
|
70
|
+
next unless File.exist?(bookmarks)
|
|
71
|
+
|
|
72
|
+
profile = bookmarks.match(%r{Edge/([^/]+)/})[1]
|
|
73
|
+
|
|
74
|
+
SL.notify("Searching Chrome History for profile #{profile}", term)
|
|
75
|
+
res = search_chromium_history(bookmarks, term)
|
|
76
|
+
|
|
77
|
+
break if res
|
|
58
78
|
end
|
|
79
|
+
|
|
80
|
+
res
|
|
59
81
|
end
|
|
60
82
|
|
|
61
83
|
## Search Chrome history
|
|
@@ -66,13 +88,27 @@ module SL
|
|
|
66
88
|
##
|
|
67
89
|
def search_chrome_history(term)
|
|
68
90
|
# Google history
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
91
|
+
base = File.expand_path('~/Library/Application Support/Google/Chrome/')
|
|
92
|
+
profiles = Dir.glob('**/History', base: base)
|
|
93
|
+
profiles.delete_if { |p| p =~ /^Snapshots/ }
|
|
94
|
+
profiles.map! { |f| File.join(base, f) }
|
|
95
|
+
|
|
96
|
+
# bookmarks_file = File.expand_path('~/Library/Application Support/Google/Chrome/Default/Bookmarks')
|
|
97
|
+
|
|
98
|
+
res = false
|
|
99
|
+
|
|
100
|
+
profiles.each do |bookmarks|
|
|
101
|
+
next unless File.exist?(bookmarks)
|
|
102
|
+
|
|
103
|
+
profile = bookmarks.match(%r{Chrome/([^/]+)/})[1]
|
|
104
|
+
|
|
105
|
+
SL.notify("Searching Chrome History for profile #{profile}", term)
|
|
106
|
+
res = search_chromium_history(bookmarks, term)
|
|
107
|
+
|
|
108
|
+
break if res
|
|
75
109
|
end
|
|
110
|
+
|
|
111
|
+
res
|
|
76
112
|
end
|
|
77
113
|
|
|
78
114
|
##
|
|
@@ -105,6 +141,7 @@ module SL
|
|
|
105
141
|
terms.push("(url NOT LIKE '%search/?%'
|
|
106
142
|
AND url NOT LIKE '%?q=%'
|
|
107
143
|
AND url NOT LIKE '%?s=%'
|
|
144
|
+
AND url NOT LIKE '%www.bing.com%'
|
|
108
145
|
AND url NOT LIKE '%duckduckgo.com/?t%')")
|
|
109
146
|
if exact_match
|
|
110
147
|
terms.push("(url LIKE '%#{term.strip.downcase}%' OR title LIKE '%#{term.strip.downcase}%')")
|
|
@@ -138,11 +175,11 @@ module SL
|
|
|
138
175
|
## @return [Array] single bookmark [url, title, date]
|
|
139
176
|
##
|
|
140
177
|
def search_arc_bookmarks(term)
|
|
141
|
-
bookmarks_file = File.expand_path('~/Library/Application Support/Arc/
|
|
178
|
+
bookmarks_file = File.expand_path('~/Library/Application Support/Arc/StorableSidebar.json')
|
|
142
179
|
|
|
143
180
|
if File.exist?(bookmarks_file)
|
|
144
|
-
SL.notify('Searching
|
|
145
|
-
return
|
|
181
|
+
SL.notify('Searching Arc Bookmarks', term)
|
|
182
|
+
return search_arc_json(bookmarks_file, term)
|
|
146
183
|
end
|
|
147
184
|
|
|
148
185
|
false
|
|
@@ -156,14 +193,25 @@ module SL
|
|
|
156
193
|
## @return [Array] single bookmark [url, title, date]
|
|
157
194
|
##
|
|
158
195
|
def search_brave_bookmarks(term)
|
|
159
|
-
|
|
196
|
+
base = File.expand_path('~/Library/Application Support/BraveSoftware/Brave-Browser/')
|
|
197
|
+
profiles = Dir.glob('**/Bookmarks', base: base)
|
|
198
|
+
profiles.delete_if { |p| p =~ /^Snapshots/ }
|
|
199
|
+
profiles.map! { |f| File.join(base, f) }
|
|
160
200
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
201
|
+
res = false
|
|
202
|
+
|
|
203
|
+
profiles.each do |bookmarks|
|
|
204
|
+
next unless File.exist?(bookmarks)
|
|
205
|
+
|
|
206
|
+
profile = bookmarks.match(%r{Browser/([^/]+)/})[1]
|
|
207
|
+
|
|
208
|
+
SL.notify("Searching Brave Bookmarks for profile #{profile}", term)
|
|
209
|
+
res = search_chromium_bookmarks(bookmarks, term)
|
|
210
|
+
|
|
211
|
+
break if res
|
|
164
212
|
end
|
|
165
213
|
|
|
166
|
-
|
|
214
|
+
res
|
|
167
215
|
end
|
|
168
216
|
|
|
169
217
|
##
|
|
@@ -174,14 +222,24 @@ module SL
|
|
|
174
222
|
## @return [Array] single bookmark [url, title, date]
|
|
175
223
|
##
|
|
176
224
|
def search_edge_bookmarks(term)
|
|
177
|
-
|
|
225
|
+
base = File.expand_path('~/Library/Application Support/Microsoft Edge')
|
|
226
|
+
profiles = Dir.glob('**/Bookmarks', base: base)
|
|
227
|
+
profiles.delete_if { |p| p =~ /^Snapshots/ }
|
|
228
|
+
profiles.map! { |f| File.join(base, f) }
|
|
178
229
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
230
|
+
res = false
|
|
231
|
+
|
|
232
|
+
profiles.each do |bookmarks|
|
|
233
|
+
next unless File.exist?(bookmarks)
|
|
234
|
+
|
|
235
|
+
profile = bookmarks.match(%r{Edge/([^/]+)/})[1]
|
|
236
|
+
|
|
237
|
+
SL.notify("Searching Edge Bookmarks for profile #{profile}", term)
|
|
238
|
+
res = search_chromium_bookmarks(bookmarks, term)
|
|
239
|
+
break if res
|
|
182
240
|
end
|
|
183
241
|
|
|
184
|
-
|
|
242
|
+
res
|
|
185
243
|
end
|
|
186
244
|
|
|
187
245
|
##
|
|
@@ -192,11 +250,102 @@ module SL
|
|
|
192
250
|
## @return [Array] single bookmark [url, title, date]
|
|
193
251
|
##
|
|
194
252
|
def search_chrome_bookmarks(term)
|
|
195
|
-
|
|
253
|
+
base = File.expand_path('~/Library/Application Support/Google/Chrome/')
|
|
254
|
+
profiles = Dir.glob('**/Bookmarks', base: base)
|
|
255
|
+
profiles.delete_if { |p| p =~ /^Snapshots/ }
|
|
256
|
+
profiles.map! { |f| File.join(base, f) }
|
|
196
257
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
258
|
+
res = false
|
|
259
|
+
|
|
260
|
+
profiles.each do |bookmarks|
|
|
261
|
+
if File.exist?(bookmarks)
|
|
262
|
+
profile = bookmarks.match(%r{Chrome/([^/]+)/})[1]
|
|
263
|
+
|
|
264
|
+
SL.notify("Searching Chrome Bookmarks for profile #{profile}", term)
|
|
265
|
+
res = search_chromium_bookmarks(bookmarks, term)
|
|
266
|
+
break if res
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
res
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
##
|
|
274
|
+
## Search Arc/JSON bookmarks
|
|
275
|
+
##
|
|
276
|
+
## @param bookmarks_file [String] path to bookmarks file
|
|
277
|
+
## @param term [String] the string to search for
|
|
278
|
+
##
|
|
279
|
+
## @return [Array] single bookmark [url, title, date]
|
|
280
|
+
##
|
|
281
|
+
def search_arc_json(bookmarks_file, term)
|
|
282
|
+
arc_bookmarks = JSON.parse(IO.read(bookmarks_file))
|
|
283
|
+
|
|
284
|
+
exact_match = false
|
|
285
|
+
match_phrases = []
|
|
286
|
+
|
|
287
|
+
# If search terms start with ''term, only search for exact string matches
|
|
288
|
+
if term =~ /^ *'/
|
|
289
|
+
exact_match = true
|
|
290
|
+
term.gsub!(/(^ *'+|'+ *$)/, '')
|
|
291
|
+
elsif term =~ /%22(.*?)%22/
|
|
292
|
+
match_phrases = term.scan(/%22(\S.*?\S)%22/)
|
|
293
|
+
term.gsub!(/%22(\S.*?\S)%22/, '')
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
if arc_bookmarks
|
|
297
|
+
bookmarks = []
|
|
298
|
+
arc_bookmarks['sidebarSyncState']['items'].each do |mark|
|
|
299
|
+
next if mark.is_a?(String)
|
|
300
|
+
|
|
301
|
+
next unless mark['value']['childrenIds'].empty?
|
|
302
|
+
|
|
303
|
+
next unless mark['value']['data']['tab']
|
|
304
|
+
|
|
305
|
+
url = {
|
|
306
|
+
url: mark['value']['data']['tab']['savedURL'],
|
|
307
|
+
saved_title: mark['value']['data']['tab']['savedTitle'],
|
|
308
|
+
title: mark['value']['title'],
|
|
309
|
+
created: mark['value']['createdAt'].to_datetime,
|
|
310
|
+
active: mark['value']['data']['tab']['timeLastActiveAt']&.to_datetime
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
score = score_mark(url, term)
|
|
314
|
+
|
|
315
|
+
if score > 7
|
|
316
|
+
url[:score] = score
|
|
317
|
+
bookmarks << url
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
unless bookmarks.empty?
|
|
322
|
+
if exact_match
|
|
323
|
+
bookmarks.delete_if do |bm|
|
|
324
|
+
!(bm[:url].matches_exact(term) ||
|
|
325
|
+
bm[:title].matches_exact(term) ||
|
|
326
|
+
bm[:saved_title].matches_exact(term))
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
if match_phrases
|
|
331
|
+
match_phrases.map! { |phrase| phrase[0] }
|
|
332
|
+
bookmarks.delete_if do |bm|
|
|
333
|
+
matched = true
|
|
334
|
+
match_phrases.each do |phrase|
|
|
335
|
+
matched = false unless bm[:url].matches_exact(phrase) ||
|
|
336
|
+
bm[:title].matches_exact(phrase) ||
|
|
337
|
+
bm[:saved_title].matches_exact(phrase)
|
|
338
|
+
end
|
|
339
|
+
!matched
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
return false if bookmarks.empty?
|
|
344
|
+
|
|
345
|
+
lastest_bookmark = bookmarks.min_by { |u| u[:created] }
|
|
346
|
+
|
|
347
|
+
return [lastest_bookmark[:url], lastest_bookmark[:title], lastest_bookmark[:date]]
|
|
348
|
+
end
|
|
200
349
|
end
|
|
201
350
|
|
|
202
351
|
false
|
|
@@ -284,7 +433,7 @@ module SL
|
|
|
284
433
|
urls << url
|
|
285
434
|
end
|
|
286
435
|
else
|
|
287
|
-
json.
|
|
436
|
+
json.each_value { |v| urls = extract_chrome_bookmarks(v, urls, term) }
|
|
288
437
|
end
|
|
289
438
|
else
|
|
290
439
|
return urls
|
|
@@ -301,19 +450,27 @@ module SL
|
|
|
301
450
|
def score_mark(mark, terms)
|
|
302
451
|
return 0 unless mark[:url]
|
|
303
452
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
453
|
+
if mark[:title]&.matches_exact(terms)
|
|
454
|
+
12 + mark[:url].matches_score(terms, start_word: false)
|
|
455
|
+
elsif mark[:url].matches_exact(terms)
|
|
456
|
+
11
|
|
457
|
+
elsif (mark[:title] && mark[:title].matches_score(terms) > 5) ||
|
|
458
|
+
(mark[:saved_title] && mark[:saved_title].matches_score(terms) > 5)
|
|
459
|
+
|
|
460
|
+
if mark[:saved_title]
|
|
461
|
+
t = mark[:title]&.matches_score(terms)
|
|
462
|
+
s = mark[:saved_title].matches_score(terms)
|
|
463
|
+
return s if t.nil?
|
|
315
464
|
|
|
316
|
-
|
|
465
|
+
[t, s].max
|
|
466
|
+
else
|
|
467
|
+
mark[:title]&.matches_score(terms)
|
|
468
|
+
end
|
|
469
|
+
elsif mark[:url].matches_score(terms, start_word: false)
|
|
470
|
+
mark[:url].matches_score(terms, start_word: false)
|
|
471
|
+
else
|
|
472
|
+
0
|
|
473
|
+
end
|
|
317
474
|
end
|
|
318
475
|
end
|
|
319
476
|
end
|
|
@@ -28,6 +28,7 @@ module SL
|
|
|
28
28
|
terms = []
|
|
29
29
|
terms.push("(url NOT LIKE '%search/?%'
|
|
30
30
|
AND url NOT LIKE '%?q=%' AND url NOT LIKE '%?s=%'
|
|
31
|
+
AND url NOT LIKE '%/search?%'
|
|
31
32
|
AND url NOT LIKE '%duckduckgo.com/?t%')")
|
|
32
33
|
if exact_match
|
|
33
34
|
terms.push("(url LIKE '%#{term.strip.downcase}%' OR title LIKE '%#{term.strip.downcase}%')")
|
data/lib/searchlink/searches.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module SL
|
|
4
|
+
# Searches and plugin registration
|
|
4
5
|
module Searches
|
|
5
6
|
class << self
|
|
6
7
|
def plugins
|
|
@@ -25,7 +26,7 @@ module SL
|
|
|
25
26
|
|
|
26
27
|
def description_for_search(search_type)
|
|
27
28
|
description = "#{search_type} search"
|
|
28
|
-
plugins[:search].
|
|
29
|
+
plugins[:search].each_value do |plugin|
|
|
29
30
|
search = plugin[:searches].select { |s| s[0].is_a?(Array) ? s[0].include?(search_type) : s[0] == search_type }
|
|
30
31
|
unless search.empty?
|
|
31
32
|
description = search[0][1]
|
|
@@ -50,7 +51,11 @@ module SL
|
|
|
50
51
|
'<tbody>']
|
|
51
52
|
|
|
52
53
|
searches.each do |s|
|
|
53
|
-
out << "<tr
|
|
54
|
+
out << "<tr>
|
|
55
|
+
<td>
|
|
56
|
+
<code>!#{s[0].is_a?(Array) ? "#{s[0][0]} (#{s[0][1..-1].join(',')})" : s[0]}
|
|
57
|
+
</code>
|
|
58
|
+
</td><td>#{s[1]}</td></tr>"
|
|
54
59
|
end
|
|
55
60
|
out.concat(['</tbody>', '</table>']).join("\n")
|
|
56
61
|
end
|
|
@@ -62,7 +67,7 @@ module SL
|
|
|
62
67
|
#
|
|
63
68
|
def available_searches
|
|
64
69
|
searches = []
|
|
65
|
-
plugins[:search].
|
|
70
|
+
plugins[:search].each_value { |plugin| searches.concat(plugin[:searches].delete_if { |s| s[1].nil? }) }
|
|
66
71
|
out = []
|
|
67
72
|
searches.each do |s|
|
|
68
73
|
out += "!#{s[0].is_a?(Array) ? "#{s[0][0]} (#{s[0][1..-1].join(',')})" : s[0]}#{s[0].spacer}#{s[1]}"
|
|
@@ -77,8 +82,8 @@ module SL
|
|
|
77
82
|
|
|
78
83
|
def all_possible_searches
|
|
79
84
|
searches = []
|
|
80
|
-
plugins[:search].
|
|
81
|
-
searches.concat(SL.config['custom_site_searches'].keys)
|
|
85
|
+
plugins[:search].each_value { |plugin| plugin[:searches].each { |s| searches.push(s[0]) } }
|
|
86
|
+
searches.concat(SL.config['custom_site_searches'].keys.sort)
|
|
82
87
|
end
|
|
83
88
|
|
|
84
89
|
def did_you_mean(term)
|
|
@@ -101,11 +106,11 @@ module SL
|
|
|
101
106
|
end
|
|
102
107
|
|
|
103
108
|
def register_plugin(title, type, klass)
|
|
104
|
-
raise
|
|
109
|
+
raise PluginError.new("Plugin has no settings method", plugin: title) unless klass.respond_to? :settings
|
|
105
110
|
|
|
106
111
|
settings = klass.settings
|
|
107
112
|
|
|
108
|
-
raise
|
|
113
|
+
raise PluginError.new("Plugin has no search method", plugin: title) unless klass.respond_to? :search
|
|
109
114
|
|
|
110
115
|
plugins[type] ||= {}
|
|
111
116
|
plugins[type][title] = {
|
|
@@ -123,6 +128,8 @@ module SL
|
|
|
123
128
|
Dir.glob(File.join(plugins_folder, '**/*.rb')).sort.each do |plugin|
|
|
124
129
|
require plugin
|
|
125
130
|
end
|
|
131
|
+
|
|
132
|
+
load_custom_scripts(plugins_folder)
|
|
126
133
|
end
|
|
127
134
|
|
|
128
135
|
return unless File.directory?(new_plugins_folder)
|
|
@@ -130,6 +137,24 @@ module SL
|
|
|
130
137
|
Dir.glob(File.join(new_plugins_folder, '**/*.rb')).sort.each do |plugin|
|
|
131
138
|
require plugin
|
|
132
139
|
end
|
|
140
|
+
|
|
141
|
+
load_custom_scripts(new_plugins_folder)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def load_custom_scripts(plugins_folder)
|
|
145
|
+
Dir.glob(File.join(plugins_folder, '**/*.{json,yml,yaml}')).each do |file|
|
|
146
|
+
ext = File.extname(file).sub(/^\./, '')
|
|
147
|
+
config = IO.read(file)
|
|
148
|
+
cfg = case ext
|
|
149
|
+
when /^y/i
|
|
150
|
+
YAML.safe_load(config)
|
|
151
|
+
else
|
|
152
|
+
JSON.parse(config)
|
|
153
|
+
end
|
|
154
|
+
cfg['filename'] = File.basename(file)
|
|
155
|
+
cfg['path'] = file.shorten_path
|
|
156
|
+
SL::ScriptSearch.new(cfg)
|
|
157
|
+
end
|
|
133
158
|
end
|
|
134
159
|
|
|
135
160
|
def do_search(search_type, search_terms, link_text, timeout: SL.config['timeout'])
|
data/lib/searchlink/semver.rb
CHANGED
|
@@ -5,14 +5,14 @@ module SL
|
|
|
5
5
|
class SemVer
|
|
6
6
|
attr_accessor :maj, :min, :patch, :pre
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
## Initialize a Semantic Version object
|
|
9
|
+
##
|
|
10
|
+
## @param version_string [String] a semantic version number
|
|
11
|
+
##
|
|
12
|
+
## @return [SemVer] SemVer object
|
|
13
|
+
##
|
|
14
14
|
def initialize(version_string)
|
|
15
|
-
raise "Invalid semantic version number: #{version_string}" unless version_string.valid_version?
|
|
15
|
+
raise VersionError.new("Invalid semantic version number: #{version_string}") unless version_string.valid_version?
|
|
16
16
|
|
|
17
17
|
@maj, @min, @patch = version_string.split(/\./)
|
|
18
18
|
@pre = nil
|
data/lib/searchlink/string.rb
CHANGED
|
@@ -488,5 +488,13 @@ module SL
|
|
|
488
488
|
def code_indent
|
|
489
489
|
split(/\n/).map { |l| " #{l}" }.join("\n")
|
|
490
490
|
end
|
|
491
|
+
|
|
492
|
+
##
|
|
493
|
+
## Shorten path by adding ~ for home directory
|
|
494
|
+
##
|
|
495
|
+
def shorten_path
|
|
496
|
+
home_directory = ENV['HOME']
|
|
497
|
+
sub(home_directory, '~')
|
|
498
|
+
end
|
|
491
499
|
end
|
|
492
500
|
end
|
data/lib/searchlink/version.rb
CHANGED
data/lib/searchlink.rb
CHANGED
|
@@ -13,10 +13,17 @@ require 'zlib'
|
|
|
13
13
|
require 'time'
|
|
14
14
|
require 'json'
|
|
15
15
|
require 'erb'
|
|
16
|
+
require 'english'
|
|
16
17
|
|
|
17
18
|
# import
|
|
18
19
|
require 'tokens' if File.exist?('lib/tokens.rb')
|
|
19
20
|
|
|
21
|
+
# import
|
|
22
|
+
require 'searchlink/exceptions'
|
|
23
|
+
|
|
24
|
+
# import
|
|
25
|
+
require 'searchlink/number'
|
|
26
|
+
|
|
20
27
|
# import
|
|
21
28
|
require 'searchlink/util'
|
|
22
29
|
|
|
@@ -62,4 +69,7 @@ require 'searchlink/output'
|
|
|
62
69
|
# import
|
|
63
70
|
require 'searchlink/which'
|
|
64
71
|
|
|
72
|
+
# import
|
|
73
|
+
require 'searchlink/script_plugin'
|
|
74
|
+
|
|
65
75
|
module Secrets; end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: searchlink
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.3.
|
|
4
|
+
version: 2.3.70
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Brett Terpstra
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2024-08-
|
|
11
|
+
date: 2024-08-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -235,10 +235,13 @@ files:
|
|
|
235
235
|
- lib/searchlink/curl.rb
|
|
236
236
|
- lib/searchlink/curl/html.rb
|
|
237
237
|
- lib/searchlink/curl/json.rb
|
|
238
|
+
- lib/searchlink/exceptions.rb
|
|
238
239
|
- lib/searchlink/help.rb
|
|
240
|
+
- lib/searchlink/number.rb
|
|
239
241
|
- lib/searchlink/output.rb
|
|
240
242
|
- lib/searchlink/parse.rb
|
|
241
243
|
- lib/searchlink/plist.rb
|
|
244
|
+
- lib/searchlink/script_plugin.rb
|
|
242
245
|
- lib/searchlink/search.rb
|
|
243
246
|
- lib/searchlink/searches.rb
|
|
244
247
|
- lib/searchlink/searches/amazon.rb
|