searchlink 2.3.59
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 +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
|