searchlink 2.3.68 → 2.3.70
Sign up to get free protection for your applications and to get access to all the features.
- 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
|