searchlink 2.3.83 → 2.3.84

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9d9d44b58260a556b6834758e6782fe1b0b0d6ebb43ed79c88b3ecee9d53d124
4
- data.tar.gz: a10d673938b4a58c907b2588fedaf4e71f021d5f85d392cbc17c144dea91c3b8
3
+ metadata.gz: 78ae4255b0a3a287ce3ccf4b84db34597f8c2a566bf4e7cb0b4939a28f62cca1
4
+ data.tar.gz: f654c706f574f6c23dca1a6714b2b52177944379fb1df00d34d9bac22e14104b
5
5
  SHA512:
6
- metadata.gz: e1a3d23ee5be311f69772b5f46a72a9c18b9684a22d62af561d6c1421ab13343df8b6f233e00ba6811748d5b2cf31c91886d45fae4fff94e797ecca10a285b65
7
- data.tar.gz: 8100ed794563d9747bce2be4b1b2a13e6475331769ee5411a7c4c186089b01bde9536c17002bffdd8c4821db11d185590b9de3d6b8c42b27175540e2cdae945c
6
+ metadata.gz: ecccb8b8f87a8aa891c9bbafffd7e40f9ecee5ee8042326fe8e0151d52e225797bcff745c7ec67b7f7dd8010fd5e32330673b3fe062c378873a74407b666205a
7
+ data.tar.gz: 6a4ada563e8488646f0cc4fe5ab8c5414c906ead7560cafaf3e6b68fcdf9292a37c04ab8261970de6d5a3a3224dc135a3dba7bc2b2abc60160ca6181dbec512c
@@ -111,7 +111,7 @@ module Curl
111
111
  def extract_tag_contents(tag, source: false)
112
112
  return @body.scan(%r{<#{tag}.*?>(?:.*?</#{tag}>)?}) if source
113
113
 
114
- @body.scan(/<#{tag}.*?>(.*?)</).map { |t| t[0] }
114
+ @body.scan(/<#{tag}.*?>(.*?)</).map { |t| t[1] }
115
115
  end
116
116
 
117
117
  ##
@@ -386,10 +386,10 @@ module SL
386
386
  m = Regexp.last_match
387
387
 
388
388
  search_type = if m[1].nil?
389
- SL::GoogleSearch.api_key? ? "gg" : "g"
390
- else
391
- m[1]
392
- end
389
+ SL::GoogleSearch.api_key? ? "gg" : "g"
390
+ else
391
+ m[1]
392
+ end
393
393
 
394
394
  search_terms = m[2].gsub(/(^["']|["']$)/, "")
395
395
  search_terms.strip!
@@ -414,6 +414,12 @@ module SL
414
414
  link_only = true if search_terms =~ /!!\^?$/
415
415
 
416
416
  search_terms = search_terms.sub(/(!!)?\^?(!!)?$/, "")
417
+
418
+ if search_type =~ /^(\S+\.)+\S+$/
419
+ search_type = "g"
420
+ search_terms = "site:#{m[1]} #{search_terms}"
421
+ end
422
+
417
423
  elsif link_info =~ /^!/
418
424
  search_word = link_info.match(/^!(\S+)/)
419
425
 
@@ -14,11 +14,12 @@ module SL
14
14
  config: [
15
15
  {
16
16
  key: "bitly_access_token",
17
- value: "xxxx",
18
- required: false,
17
+ value: "",
18
+ required: true,
19
19
  description: "Generate an access token at https://app.bitly.com/settings/api/"
20
20
  },
21
21
  {
22
+ description: "Bit.ly domain (optional).",
22
23
  key: "bitly_domain",
23
24
  value: "bit.ly",
24
25
  required: false
@@ -31,32 +32,56 @@ module SL
31
32
  if SL::URL.url?(search_terms)
32
33
  link = search_terms
33
34
  else
34
- link, rtitle = SL.ddg(search_terms, link_text)
35
+ link, title, link_text = SL.ddg(search_terms, link_text)
35
36
  end
36
37
 
37
- url, title = bitly_shorten(link, rtitle)
38
- link_text = title || url
39
- [url, title, link_text]
40
- end
38
+ url = shorten(link)
41
39
 
42
- def bitly_shorten(url, title = nil)
43
- unless SL.config.key?("bitly_access_token") && !SL.config["bitly_access_token"].empty?
44
- SL.add_error("Bit.ly not configured", "Missing access token")
45
- return [false, title]
40
+ unless url
41
+ SL.add_error("Result is not a valid URL", "URL error")
42
+ return [false, title, link_text]
46
43
  end
47
44
 
45
+ format_response(url, link, link_text)
46
+ end
47
+
48
+ def shorten(url)
49
+ return false unless bitly_config?
50
+
48
51
  domain = SL.config.key?("bitly_domain") ? SL.config["bitly_domain"] : "bit.ly"
49
- long_url = url.dup
50
- curl = TTY::Which.which("curl")
51
- cmd = [
52
- %(#{curl} -SsL -H 'Authorization: Bearer #{SL.config['bitly_access_token']}'),
53
- %(-H 'Content-Type: application/json'),
54
- "-X POST", %(-d '{ "long_url": "#{url}", "domain": "#{domain}" }'), "https://api-ssl.bitly.com/v4/shorten"
55
- ]
56
- data = JSON.parse(`#{cmd.join(" ")}`.strip)
57
- link = data["link"]
58
- title ||= SL::URL.title(long_url)
59
- [link, title]
52
+ url.dup
53
+
54
+ headers = {
55
+ "Content-Type" => "application/json",
56
+ "Authorization" => "Bearer #{SL.config['bitly_access_token']}"
57
+ }
58
+ data_obj = {
59
+ "long_url" => url,
60
+ "domain" => domain
61
+ }
62
+ data = Curl::Json.new("https://api-ssl.bitly.com/v4/shorten", data: data_obj.to_json, headers: headers, symbolize_names: true)
63
+
64
+ return false unless data.json.key?(:link)
65
+
66
+ link = data.json[:link]
67
+
68
+ return false unless SL::URL.valid_link?(link)
69
+
70
+ link
71
+ end
72
+
73
+ private
74
+
75
+ def bitly_config?
76
+ return true if SL.config["bitly_access_token"] && !SL.config["bitly_access_token"].empty?
77
+
78
+ SL.add_error("Bit.ly not configured", "Missing access token")
79
+ false
80
+ end
81
+
82
+ def format_response(link, original_url, link_text)
83
+ rtitle = SL::URL.title(original_url)
84
+ [link, rtitle, link_text == "" && !SL.titleize ? rtitle : link_text]
60
85
  end
61
86
  end
62
87
 
@@ -25,6 +25,9 @@ module SL
25
25
  end
26
26
 
27
27
  def search(search_type, search_terms, link_text)
28
+ @auth_token = Secrets::GH_AUTH_TOKEN if defined? Secrets::GH_AUTH_TOKEN
29
+ @auth_token = SL.config["github_token"] if SL.config.key?("github_token") && !SL.config["github_token"].empty?
30
+
28
31
  case search_type
29
32
  when /^gist/
30
33
  url, title, link_text = gist(search_terms, search_type, link_text)
@@ -46,7 +49,7 @@ module SL
46
49
  "Accept" => "application/vnd.github+json",
47
50
  "X-GitHub-Api-Version" => "2022-11-28"
48
51
  }
49
- headers["Authorization"] = "Bearer #{Secrets::GH_AUTH_TOKEN}" if defined? Secrets::GH_AUTH_TOKEN
52
+ headers["Authorization"] = "Bearer #{@auth_token}" if defined? @auth_token
50
53
 
51
54
  url = "https://api.github.com/search/#{endpoint}?q=#{query.url_encode}&per_page=1&page=1&order=desc"
52
55
  res = Curl::Json.new(url, headers: headers)
@@ -163,6 +166,8 @@ module SL
163
166
  end
164
167
 
165
168
  def filter_gists(gists, search_terms)
169
+ return false if gists.is_a?(Hash)
170
+
166
171
  score = 0
167
172
  gists.map! do |g|
168
173
  {
@@ -238,7 +243,7 @@ module SL
238
243
  end
239
244
 
240
245
  # Assuming we retrieved a full gist URL
241
- if url =~ %r{https://gist.github.com/(?:(?<user>[^/]+)/)?(?<id>[a-z0-9]+?)(?:[#/](?<file>(?:file-)?.*?))?$}
246
+ if url && url =~ %r{https://gist.github.com/(?:(?<user>[^/]+)/)?(?<id>[a-z0-9]+?)(?:[#/](?<file>(?:file-)?.*?))?$}
242
247
  m = Regexp.last_match
243
248
  user = m["user"]
244
249
  id = m["id"]
@@ -43,7 +43,7 @@ module SL
43
43
  return false
44
44
  end
45
45
 
46
- url = "https://customsearch.googleapis.com/customsearch/v1?cx=338419ee5ac894523&q=#{ERB::Util.url_encode(search_terms)}&num=1&key=#{@api_key}"
46
+ url = "https://customsearch.googleapis.com/customsearch/v1?cx=338419ee5ac894523&q=#{ERB::Util.url_encode(search_terms.gsub(/%22/, '"'))}&num=1&key=#{@api_key}"
47
47
  json = Curl::Json.new(url).json
48
48
 
49
49
  if json["error"] && json["error"]["code"].to_i == 429
@@ -17,29 +17,26 @@ module SL
17
17
  if SL::URL.url?(search_terms)
18
18
  link = search_terms
19
19
  else
20
- link, rtitle = SL.ddg(search_terms, link_text)
20
+ link, title, link_text = SL.ddg(search_terms, link_text)
21
21
  end
22
22
 
23
- url, title = isgd_shorten(link, rtitle)
24
- link_text = title || url
23
+ url = shorten(link)
24
+ title = SL::URL.title(link) if title.nil? || title.empty?
25
+ link_text = title if (link_text.nil? || link_text.empty?) && !SL.titleize
25
26
  [url, title, link_text]
26
27
  end
27
28
 
28
- def isgd_shorten(url, title = nil)
29
+ def shorten(url)
29
30
  long_url = url.dup
30
31
 
31
32
  data = Curl::Json.new("https://is.gd/create.php?format=json&url=#{CGI.escape(long_url)}", symbolize_names: true)
32
33
 
33
34
  if data.json.key?("errorcode")
34
35
  SL.add_error("Error creating is.gd url", data.json[:errorcode])
35
- return [false, title, link_text]
36
+ return false
36
37
  end
37
38
 
38
- link = data.json[:shorturl]
39
- rtitle = SL::URL.title(long_url)
40
- title = rtitle
41
- link_text = rtitle if link_text == "" && !SL.titleize
42
- [link, title, link_text]
39
+ data.json[:shorturl]
43
40
  end
44
41
  end
45
42
 
@@ -10,6 +10,20 @@ module SL
10
10
  trigger: "(ld|ding)",
11
11
  searches: [
12
12
  [%w[ld ding], "Linkding Bookmark Search"]
13
+ ],
14
+ config: [
15
+ {
16
+ description: "Linkding server URL.",
17
+ key: "linkding_server",
18
+ value: "''",
19
+ required: true
20
+ },
21
+ {
22
+ description: "Linkding API key.\nYou can find your api key here: https://your_server/settings/integrations",
23
+ key: "linkding_api_key",
24
+ value: "''",
25
+ required: true
26
+ }
13
27
  ]
14
28
  }
15
29
  end
@@ -108,7 +122,7 @@ module SL
108
122
  cache
109
123
  end
110
124
 
111
- # Search pinboard bookmarks
125
+ # Search linkding bookmarks
112
126
  # Begin query with '' to force exact matching (including description text)
113
127
  # Regular matching searches for each word of query and scores the bookmarks
114
128
  # exact matches in title get highest score
@@ -120,13 +134,13 @@ module SL
120
134
  #
121
135
  # Exact matching is case and punctuation insensitive
122
136
  def search(_, search_terms, link_text)
123
- unless SL.config["linkding_server"]
137
+ unless SL.config["linkding_server"] && !SL.config["linkding_server"].empty?
124
138
  SL.add_error("Missing Linkding server",
125
139
  "add it to your configuration (linkding_server: https://YOUR_SERVER)")
126
140
  return false
127
141
  end
128
142
 
129
- unless SL.config["linkding_api_key"]
143
+ unless SL.config["linkding_api_key"] && !SL.config["linkding_api_key"].empty?
130
144
  SL.add_error("Missing Linkding API token",
131
145
  "Find your api key at https://your_server/settings/integrations and add it
132
146
  to your configuration (linkding_api_key: YOURKEY)")
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Setapp Plugin
4
+ # Takes an app name or Setapp URL, adds an affiliate string,
5
+ # and outputs a URL, optionally shortened with is.gd or bit.ly
6
+ #
7
+ # Optional config:
8
+ #
9
+ # ```yaml
10
+ # setapp_affiliate_string: xxxxxxxxx # see below
11
+ # bitly_domain: bit.ly # or custom domain
12
+ # bitly_access_token: xxxxxxxxxxxx # see below
13
+ # ```
14
+ #
15
+ # To get your Setapp affiliate string
16
+ #
17
+ # 1. You must have a Setapp affiliate account through impact.com
18
+ # 2. Generate a campaign url for an app landing page
19
+ # 3. Follow the short link provided
20
+ # 4. The browser URL bar will now show the expanded link
21
+ # 5. Copy everything after the & symbol in the url to
22
+ # the `setapp_affiliate_string` config line
23
+ #
24
+ # Run a search with !set, !seti, or !setb. The input can either be
25
+ # a Setapp app landing page url, or an app name, e.g. `!seti marked`,
26
+ # `[jump desktop](!seti)`, or `!setb https://setapp.com/apps/marked`
27
+ #
28
+ module SL
29
+ # is.gd link shortening
30
+ class SetappSearch
31
+ class << self
32
+ def settings
33
+ {
34
+ trigger: "set[ib]?",
35
+ searches: [
36
+ ["set", "Setapp Link with optional affiliate string"],
37
+ ["seti", "Shorten Setapp Affiliate Link with is.gd"],
38
+ ["setb", "Shorten Setapp Affiliate Link with bit.ly"]
39
+ ],
40
+ config: [
41
+ {
42
+ description: "Setapp affiliate string (optional).\nYou can find your affiliate string here: https://www.impact.com/affiliate/links",
43
+ key: "setapp_affiliate_string",
44
+ value: "''",
45
+ required: false
46
+ }
47
+ ]
48
+ }
49
+ end
50
+
51
+ def search(search_type, search_terms, link_text)
52
+ link = build_link(search_terms)
53
+ link, rtitle, link_text = process_link(link, search_terms, link_text)
54
+ return [false, rtitle, link_text] unless valid_setapp_url?(link)
55
+
56
+ process_search_type(search_type, link, rtitle, link_text)
57
+ end
58
+
59
+ private
60
+
61
+ def build_link(search_terms)
62
+ return search_terms if SL::URL.url?(search_terms)
63
+
64
+ "https://setapp.com/apps/#{CGI.escape(search_terms.gsub(/ \d+$/, '').gsub(/ +/, '-').downcase)}"
65
+ end
66
+
67
+ def process_link(link, search_terms, link_text)
68
+ SL::URL.valid_link?(link, 2) && [link, search_terms, link_text] || SL.ddg("site:setapp.com #{search_terms}", link_text)
69
+ end
70
+
71
+ def process_search_type(search_type, link, title, link_text)
72
+ link_text = link_text.nil? || link_text.empty? ? Curl::Html.new(link).title.sub(/ on Setapp.*/, "") : link_text
73
+ link = build_affiliate_url(link)
74
+ case search_type
75
+ when "set"
76
+ format_response(link, link_text)
77
+ when "seti"
78
+ shorten(:isgd, link, title, link_text)
79
+ when "setb"
80
+ shorten(:bitly, link, title, link_text)
81
+ else
82
+ SL.add_error("Invalid search type", "Search error")
83
+ [false, title, link_text]
84
+ end
85
+ end
86
+
87
+ def shorten(type, url, title = nil, link_text = nil)
88
+ return [false, title, link_text] unless valid_setapp_url?(url)
89
+
90
+ shortened_url = case type
91
+ when :isgd
92
+ SL::IsgdSearch.shorten(url)
93
+ when :bitly
94
+ SL::BitlySearch.shorten(url)
95
+ end
96
+ return [false, title, link_text] unless shortened_url
97
+
98
+ format_response(shortened_url, link_text)
99
+ end
100
+
101
+ def valid_setapp_url?(url)
102
+ return true if SL::URL.valid_link?(url, 2) && url =~ %r{^https://setapp.com}
103
+
104
+ SL.add_error("URL is not a valid Setapp link", "URL error")
105
+ false
106
+ end
107
+
108
+ def valid_affiliate_config?
109
+ return true if SL.config.key?("setapp_affiliate_string") && !SL.config["setapp_affiliate_string"].empty?
110
+
111
+ # SL.add_error("Setapp affiliate string not configured", "Missing affiliate string")
112
+ false
113
+ end
114
+
115
+ def build_affiliate_url(url)
116
+ return url unless valid_affiliate_config?
117
+
118
+ separator = url =~ /\?/ ? "&" : "?"
119
+ "#{url}#{SL.config['setapp_affiliate_string'].sub(/^[?&]?/, separator)}"
120
+ end
121
+
122
+ def format_response(link, link_text)
123
+ title = SL::URL.title(link)
124
+
125
+ [link, title, link_text.nil? || link_text.empty? && !SL.titleize ? title : link_text]
126
+ end
127
+ end
128
+
129
+ SL::Searches.register "setapp", :search, self
130
+ end
131
+ end
@@ -218,6 +218,9 @@ require_relative "searches/lastfm"
218
218
  # import
219
219
  require_relative "searches/pinboard"
220
220
 
221
+ # import
222
+ require_relative "searches/setapp"
223
+
221
224
  # import
222
225
  require_relative "searches/social"
223
226
 
@@ -157,6 +157,30 @@ module SL
157
157
  input + append
158
158
  end
159
159
 
160
+ ##
161
+ ## Append an affiliate string to a URL
162
+ ##
163
+ ## @param aff_string [String] The affiliate string
164
+ ## @return [String] The URL with the affiliate string
165
+ ##
166
+ ## @see #append_affiliate_string!
167
+ ##
168
+ def append_affiliate_string(aff_string)
169
+ separator = self =~ /\?/ ? "&" : "?"
170
+ "#{self}#{aff_string.sub(/^[?&]?/, separator)}"
171
+ end
172
+
173
+ ## Destructively append an affiliate string to a URL
174
+ ##
175
+ ## @param aff_string [String] The affiliate string
176
+ ## @return [String] The URL with the affiliate string
177
+ ##
178
+ ## @see #append_affiliate_string
179
+ ##
180
+ def append_affiliate_string!(aff_string)
181
+ replace append_affiliate_string(aff_string)
182
+ end
183
+
160
184
  ##
161
185
  ## Remove the protocol from a URL
162
186
  ##
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SL
4
- VERSION = '2.3.83'
4
+ VERSION = "2.3.84"
5
5
  end
6
6
 
7
7
  # Main module
@@ -47,7 +47,7 @@ module SL
47
47
  if defined? Secrets::GH_AUTH_TOKEN
48
48
  headers["Authorization"] = "Bearer #{Secrets::GH_AUTH_TOKEN}"
49
49
  elsif SL.config["github_token"]
50
- headers["Authorization"] = "Bearer #{SL.settings["github_token"]}"
50
+ headers["Authorization"] = "Bearer #{SL.config["github_token"]}"
51
51
  end
52
52
 
53
53
  url = "https://api.github.com/repos/ttscoff/searchlink/releases/latest"
data/lib/tokens.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Secrets
2
- GH_AUTH_TOKEN = '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'
4
+ GH_AUTH_TOKEN = "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"
3
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: searchlink
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.83
4
+ version: 2.3.84
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-03-26 00:00:00.000000000 Z
10
+ date: 2025-03-27 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: base64
@@ -303,6 +303,7 @@ files:
303
303
  - lib/searchlink/searches/linkding.rb
304
304
  - lib/searchlink/searches/lyrics.rb
305
305
  - lib/searchlink/searches/pinboard.rb
306
+ - lib/searchlink/searches/setapp.rb
306
307
  - lib/searchlink/searches/social.rb
307
308
  - lib/searchlink/searches/software.rb
308
309
  - lib/searchlink/searches/spelling.rb