Class: SL::HistorySearch

Inherits:
Object
  • Object
show all
Defined in:
lib/searchlink/searches/history.rb,
lib/searchlink/searches/helpers/safari.rb,
lib/searchlink/searches/helpers/firefox.rb,
lib/searchlink/searches/helpers/chromium.rb

Overview

Chromium history search

Class Method Summary collapse

Class Method Details

.extract_chrome_bookmarks(json, urls = [], term = '') ⇒ Array

Extract chromium bookmarks from JSON file

Parameters:

  • json (String)

    The json data

  • urls (Array) (defaults to: [])

    The gathered urls, appended to recursively

  • term (String) (defaults to: '')

    The search term (optional)

Returns:

  • (Array)

    array of bookmarks



269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/searchlink/searches/helpers/chromium.rb', line 269

def extract_chrome_bookmarks(json, urls = [], term = '')
  if json.instance_of?(Array)
    json.each { |item| urls = extract_chrome_bookmarks(item, urls, term) }
  elsif json.instance_of?(Hash)
    if json.key? 'children'
      urls = extract_chrome_bookmarks(json['children'], urls, term)
    elsif json['type'] == 'url'
      date = Time.at(json['date_added'].to_i / 1000000 + (Time.new(1601, 01, 01).strftime('%s').to_i))
      url = { url: json['url'], title: json['name'], date: date }
      score = score_mark(url, term)

      if score > 7
        url[:score] = score
        urls << url
      end
    else
      json.each { |_, v| urls = extract_chrome_bookmarks(v, urls, term) }
    end
  else
    return urls
  end
  urls
end

.get_safari_bookmarks(parent, terms) ⇒ Array

Recursively parse bookmarks hash and score bookmarks

Parameters:

  • parent (Hash, Array)

    The parent bookmark item

  • terms (String)

    The search terms

Returns:

  • (Array)

    array of scored bookmarks



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/searchlink/searches/helpers/safari.rb', line 108

def get_safari_bookmarks(parent, terms)
  results = []

  if parent.is_a?(Array)
    parent.each do |c|
      if c.is_a?(Hash)
        if c.key?('Children')
          results.concat(get_safari_bookmarks(c['Children'], terms))
        elsif c.key?('URIDictionary')
          title = c['URIDictionary']['title']
          url = c['URLString']
          scored = score_bookmark({ url: url, title: title }, terms)

          results.push(scored) if scored[:score] > 7
        end
      end
    end
  elsif parent&.key?('Children')
    results.concat(get_safari_bookmarks(parent['Children'], terms))
  end

  results.sort_by { |h| [h[:score], h[:title].length * -1] }.reverse
end

.score_bookmark(mark, terms) ⇒ Object

Score bookmark for search term matches

Parameters:

  • mark (Hash)

    The bookmark

  • terms (String)

    The search terms



84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/searchlink/searches/helpers/safari.rb', line 84

def score_bookmark(mark, terms)
  score = if mark[:title].matches_exact(terms)
            12 + mark[:url].matches_score(terms, start_word: false)
          elsif mark[:url].matches_exact(terms)
            11
          elsif mark[:title].matches_score(terms) > 5
            mark[:title].matches_score(terms)
          elsif mark[:url].matches_score(terms, start_word: false)
            mark[:url].matches_score(terms, start_word: false)
          end

  { url: mark[:url], title: mark[:title], score: score }
end

.score_mark(mark, terms) ⇒ Object

Score bookmark for search term matches

Parameters:

  • mark (Hash)

    The bookmark

  • terms (String)

    The search terms



299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'lib/searchlink/searches/helpers/chromium.rb', line 299

def score_mark(mark, terms)
  return 0 unless mark[:url]

  score = if mark[:title] && mark[:title].matches_exact(terms)
            12 + mark[:url].matches_score(terms, start_word: false)
          elsif mark[:url].matches_exact(terms)
            11
          elsif mark[:title] && mark[:title].matches_score(terms) > 5
            mark[:title].matches_score(terms)
          elsif mark[:url].matches_score(terms, start_word: false)
            mark[:url].matches_score(terms, start_word: false)
          else
            0
          end

  score
end

.search(search_type, search_terms, link_text) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/searchlink/searches/history.rb', line 47

def search(search_type, search_terms, link_text)
  str = search_type.match(/^h(([scfabe])([hb])?)*$/)[1]

  types = []
  while str && str.length.positive?
    if str =~ /^s([hb]*)/
      t = Regexp.last_match(1)
      if t.length > 1 || t.empty?
        types.push('safari_history')
        types.push('safari_bookmarks')
      elsif t == 'h'
        types.push('safari_history')
      elsif t == 'b'
        types.push('safari_bookmarks')
      end
      str.sub!(/^s([hb]*)/, '')
    end

    if str =~ /^c([hb]*)/
      t = Regexp.last_match(1)
      if t.length > 1 || t.empty?
        types.push('chrome_bookmarks')
        types.push('chrome_history')
      elsif t == 'h'
        types.push('chrome_history')
      elsif t == 'b'
        types.push('chrome_bookmarks')
      end
      str.sub!(/^c([hb]*)/, '')
    end

    if str =~ /^f([hb]*)$/
      t = Regexp.last_match(1)
      if t.length > 1 || t.empty?
        types.push('firefox_bookmarks')
        types.push('firefox_history')
      elsif t == 'h'
        types.push('firefox_history')
      elsif t == 'b'
        types.push('firefox_bookmarks')
      end
      str.sub!(/^f([hb]*)/, '')
    end

    if str =~ /^e([hb]*)$/
      t = Regexp.last_match(1)
      if t.length > 1 || t.empty?
        types.push('edge_bookmarks')
        types.push('edge_history')
      elsif t == 'h'
        types.push('edge_history')
      elsif t == 'b'
        types.push('edge_bookmarks')
      end
      str.sub!(/^e([hb]*)/, '')
    end

    if str =~ /^b([hb]*)$/
      t = Regexp.last_match(1)
      if t.length > 1 || t.empty?
        types.push('brave_bookmarks')
        types.push('brave_history')
      elsif t == 'h'
        types.push('brave_history')
      elsif t == 'b'
        types.push('brave_bookmarks')
      end
      str.sub!(/^b([hb]*)/, '')
    end

    next unless str =~ /^a([hb]*)$/

    t = Regexp.last_match(1)
    if t.length > 1 || t.empty?
      types.push('arc_bookmarks')
      types.push('arc_history')
    elsif t == 'h'
      types.push('arc_history')
    elsif t == 'b'
      types.push('arc_bookmarks')
    end
    str.sub!(/^a([hb]*)/, '')
  end

  url, title = search_history(search_terms, types)
  link_text = title if link_text == '' || link_text == search_terms
  [url, title, link_text]
end

.search_arc_bookmarks(term) ⇒ Array

Search Arc bookmarks

Parameters:

  • term (String)

    The search term

Returns:

  • (Array)

    single bookmark [url, title, date]



138
139
140
141
142
143
144
145
146
147
# File 'lib/searchlink/searches/helpers/chromium.rb', line 138

def search_arc_bookmarks(term)
  bookmarks_file = File.expand_path('~/Library/Application Support/Arc/User Data/Default/Bookmarks')

  if File.exist?(bookmarks_file)
    SL.notify('Searching Brave Bookmarks', term)
    return search_chromium_bookmarks(bookmarks_file, term)
  end

  false
end

.search_arc_history(term) ⇒ Array

Search Arc history

Parameters:

  • term

    The search term

Returns:

  • (Array)

    Single bookmark, [url, title, date]



14
15
16
17
18
19
20
21
22
23
# File 'lib/searchlink/searches/helpers/chromium.rb', line 14

def search_arc_history(term)
  # Google history
  history_file = File.expand_path('~/Library/Application Support/Arc/User Data/Default/History')
  if File.exist?(history_file)
    SL.notify('Searching Arc History', term)
    search_chromium_history(history_file, term)
  else
    false
  end
end

.search_brave_bookmarks(term) ⇒ Array

Search Brave bookmarks

Parameters:

  • term (String)

    The search term

Returns:

  • (Array)

    single bookmark [url, title, date]



156
157
158
159
160
161
162
163
164
165
# File 'lib/searchlink/searches/helpers/chromium.rb', line 156

def search_brave_bookmarks(term)
  bookmarks_file = File.expand_path('~/Library/Application Support/BraveSoftware/Brave-Browser/Default/Bookmarks')

  if File.exist?(bookmarks_file)
    SL.notify('Searching Brave Bookmarks', term)
    return search_chromium_bookmarks(bookmarks_file, term)
  end

  false
end

.search_brave_history(term) ⇒ Array

Search Brave history

Parameters:

  • term

    The search term

Returns:

  • (Array)

    Single bookmark, [url, title, date]



31
32
33
34
35
36
37
38
39
40
# File 'lib/searchlink/searches/helpers/chromium.rb', line 31

def search_brave_history(term)
  # Google history
  history_file = File.expand_path('~/Library/Application Support/BraveSoftware/Brave-Browser/Default/History')
  if File.exist?(history_file)
    SL.notify('Searching Brave History', term)
    search_chromium_history(history_file, term)
  else
    false
  end
end

.search_chrome_bookmarks(term) ⇒ Array

Search Chrome bookmarks

Parameters:

  • term (String)

    The search term

Returns:

  • (Array)

    single bookmark [url, title, date]



192
193
194
195
196
197
198
199
200
201
# File 'lib/searchlink/searches/helpers/chromium.rb', line 192

def search_chrome_bookmarks(term)
  bookmarks_file = File.expand_path('~/Library/Application Support/Google/Chrome/Default/Bookmarks')

  if File.exist?(bookmarks_file)
    SL.notify('Searching Chrome Bookmarks', term)
    return search_chromium_bookmarks(bookmarks_file, term)
  end

  false
end

.search_chrome_history(term) ⇒ Array

Search Chrome history

Parameters:

  • term

    The search term

Returns:

  • (Array)

    Single bookmark, [url, title, date]



65
66
67
68
69
70
71
72
73
74
# File 'lib/searchlink/searches/helpers/chromium.rb', line 65

def search_chrome_history(term)
  # Google history
  history_file = File.expand_path('~/Library/Application Support/Google/Chrome/Default/History')
  if File.exist?(history_file)
    SL.notify('Searching Chrome History', term)
    search_chromium_history(history_file, term)
  else
    false
  end
end

.search_chromium_bookmarks(bookmarks_file, term) ⇒ Array

Generic chromium bookmark search

Parameters:

  • bookmarks_file (String)

    The path to bookmarks file for selected browser

  • term (String)

    The term

Returns:

  • (Array)

    single bookmark [url, title, date]



213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/searchlink/searches/helpers/chromium.rb', line 213

def search_chromium_bookmarks(bookmarks_file, term)
  chrome_bookmarks = JSON.parse(IO.read(bookmarks_file))

  exact_match = false
  match_phrases = []

  # If search terms start with ''term, only search for exact string matches
  if term =~ /^ *'/
    exact_match = true
    term.gsub!(/(^ *'+|'+ *$)/, '')
  elsif term =~ /%22(.*?)%22/
    match_phrases = term.scan(/%22(\S.*?\S)%22/)
    term.gsub!(/%22(\S.*?\S)%22/, '')
  end

  if chrome_bookmarks
    roots = chrome_bookmarks['roots']

    urls = extract_chrome_bookmarks(roots, [], term)

    unless urls.empty?
      urls.delete_if { |bm| !(bm[:url].matches_exact(term) || bm[:title].matches_exact(term)) } if exact_match

      if match_phrases
        match_phrases.map! { |phrase| phrase[0] }
        urls.delete_if do |bm|
          matched = true
          match_phrases.each do |phrase|
            matched = false unless bm[:url].matches_exact(phrase) || bm[:title].matches_exact(phrase)
          end
          !matched
        end
      end

      return false if urls.empty?

      lastest_bookmark = urls.max_by { |u| u[:score] }

      return [lastest_bookmark[:url], lastest_bookmark[:title], lastest_bookmark[:date]]
    end
  end

  false
end

.search_chromium_history(history_file, term) ⇒ Array

Generic chromium history search

Parameters:

  • history_file (String)

    The history file path for the selected browser

  • term (String)

    The search term

Returns:

  • (Array)

    Single bookmark, [url, title, date]



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/searchlink/searches/helpers/chromium.rb', line 86

def search_chromium_history(history_file, term)
  tmpfile = "#{history_file}.tmp"
  FileUtils.cp(history_file, tmpfile)

  exact_match = false
  match_phrases = []

  # If search terms start with ''term, only search for exact string matches
  if term =~ /^ *'/
    exact_match = true
    term.gsub!(/(^ *'+|'+ *$)/, '')
  elsif term =~ /%22(.*?)%22/
    match_phrases = term.scan(/%22(\S.*?\S)%22/)
    term.gsub!(/%22(\S.*?\S)%22/, '')
  end

  terms = []
  terms.push("(url NOT LIKE '%search/?%'
             AND url NOT LIKE '%?q=%'
             AND url NOT LIKE '%?s=%'
             AND url NOT LIKE '%duckduckgo.com/?t%')")
  if exact_match
    terms.push("(url LIKE '%#{term.strip.downcase}%' OR title LIKE '%#{term.strip.downcase}%')")
  else
    terms.concat(term.split(/\s+/).map do |t|
      "(url LIKE '%#{t.strip.downcase}%' OR title LIKE '%#{t.strip.downcase}%')"
    end)
    terms.concat(match_phrases.map do |t|
      "(url LIKE '%#{t[0].strip.downcase}%' OR title LIKE '%#{t[0].strip.downcase}%')"
    end)
  end

  query = terms.join(' AND ')
  most_recent = `sqlite3 -json '#{tmpfile}' "select title, url,
  datetime(last_visit_time / 1000000 + (strftime('%s', '1601-01-01')), 'unixepoch') as datum
  from urls where #{query} order by datum desc limit 1 COLLATE NOCASE;"`.strip
  FileUtils.rm_f(tmpfile)
  return false if most_recent.strip.empty?

  bm = JSON.parse(most_recent)[0]

  date = Time.parse(bm['datum'])
  [bm['url'], bm['title'], date]
end

.search_edge_bookmarks(term) ⇒ Array

Search Ege bookmarks

Parameters:

  • term (String)

    The search term

Returns:

  • (Array)

    single bookmark [url, title, date]



174
175
176
177
178
179
180
181
182
183
# File 'lib/searchlink/searches/helpers/chromium.rb', line 174

def search_edge_bookmarks(term)
  bookmarks_file = File.expand_path('~/Library/Application Support/Microsoft Edge/Default/Bookmarks')

  if File.exist?(bookmarks_file)
    SL.notify('Searching Edge Bookmarks', term)
    return search_chromium_bookmarks(bookmarks_file, term)
  end

  false
end

.search_edge_history(term) ⇒ Array

Search Edge history

Parameters:

  • term

    The search term

Returns:

  • (Array)

    Single bookmark, [url, title, date]



48
49
50
51
52
53
54
55
56
57
# File 'lib/searchlink/searches/helpers/chromium.rb', line 48

def search_edge_history(term)
  # Google history
  history_file = File.expand_path('~/Library/Application Support/Microsoft Edge/Default/History')
  if File.exist?(history_file)
    SL.notify('Searching Edge History', term)
    search_chromium_history(history_file, term)
  else
    false
  end
end

.search_firefox_bookmarks(term) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/searchlink/searches/helpers/firefox.rb', line 72

def search_firefox_bookmarks(term)
  # Firefox history
  base = File.expand_path('~/Library/Application Support/Firefox/Profiles')
  Dir.chdir(base)
  profile = Dir.glob('*default-release')
  return false unless profile

  src = File.join(base, profile[0], 'places.sqlite')

  exact_match = false
  match_phrases = []

  # If search terms start with ''term, only search for exact string matches
  if term =~ /^ *'/
    exact_match = true
    term.gsub!(/(^ *'+|'+ *$)/, '')
  elsif term =~ /%22(.*?)%22/
    match_phrases = term.scan(/%22(\S.*?\S)%22/)
    term.gsub!(/%22(\S.*?\S)%22/, '')
  end

  if File.exist?(src)
    SL.notify('Searching Firefox Bookmarks', term)
    tmpfile = "#{src}.tmp"
    FileUtils.cp(src, tmpfile)

    terms = []
    terms.push("(h.url NOT LIKE '%search/?%'
               AND h.url NOT LIKE '%?q=%'
               AND h.url NOT LIKE '%?s=%'
               AND h.url NOT LIKE '%duckduckgo.com/?t%')")
    if exact_match
      terms.push("(h.url LIKE '%#{term.strip.downcase}%' OR h.title LIKE '%#{term.strip.downcase}%')")
    else
      terms.concat(term.split(/\s+/).map do |t|
        "(h.url LIKE '%#{t.strip.downcase}%' OR h.title LIKE '%#{t.strip.downcase}%')"
      end)
      terms.concat(match_phrases.map do |t|
        "(h.url LIKE '%#{t[0].strip.downcase}%' OR h.title LIKE '%#{t[0].strip.downcase}%')"
      end)
    end

    query = terms.join(' AND ')

    most_recent = `sqlite3 -json '#{tmpfile}' "select h.url, b.title,
    datetime(b.dateAdded/1000000, 'unixepoch', 'localtime') as datum
    FROM moz_places h JOIN moz_bookmarks b ON h.id = b.fk
    where #{query} order by datum desc limit 1 COLLATE NOCASE;"`.strip
    FileUtils.rm_f(tmpfile)

    return false if most_recent.strip.empty?

    bm = JSON.parse(most_recent)[0]

    date = Time.parse(bm['datum'])
    score = score_mark({url: bm['url'], title: bm['title']}, term)
    [bm['url'], bm['title'], date, score]
  else
    false
  end
end

.search_firefox_history(term) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/searchlink/searches/helpers/firefox.rb', line 4

def search_firefox_history(term)
  # Firefox history
  base = File.expand_path('~/Library/Application Support/Firefox/Profiles')
  Dir.chdir(base)
  profile = Dir.glob('*default-release')
  return false unless profile

  src = File.join(base, profile[0], 'places.sqlite')

  exact_match = false
  match_phrases = []

  # If search terms start with ''term, only search for exact string matches
  case term
  when /^ *'/
    exact_match = true
    term.gsub!(/(^ *'+|'+ *$)/, '')
  when /%22(.*?)%22/
    match_phrases = term.scan(/%22(\S.*?\S)%22/)
    term.gsub!(/%22(\S.*?\S)%22/, '')
  end

  if File.exist?(src)
    SL.notify('Searching Firefox History', term)
    tmpfile = "#{src}.tmp"
    FileUtils.cp(src, tmpfile)

    terms = []
    terms.push("(moz_places.url NOT LIKE '%search/?%'
               AND moz_places.url NOT LIKE '%?q=%'
               AND moz_places.url NOT LIKE '%?s=%'
               AND moz_places.url NOT LIKE '%duckduckgo.com/?t%')")
    if exact_match
      terms.push("(moz_places.url LIKE '%#{term.strip.downcase}%' OR moz_places.title LIKE '%#{term.strip.downcase}%')")
    else
      terms.concat(term.split(/\s+/).map do |t|
        "(moz_places.url LIKE '%#{t.strip.downcase}%' OR moz_places.title LIKE '%#{t.strip.downcase}%')"
      end)
      terms.concat(match_phrases.map do |t|
        "(moz_places.url LIKE '%#{t[0].strip.downcase}%' OR moz_places.title LIKE '%#{t[0].strip.downcase}%')"
      end)
    end
    query = terms.join(' AND ')
    most_recent = `sqlite3 -json '#{tmpfile}' "select moz_places.title, moz_places.url,
    datetime(moz_historyvisits.visit_date/1000000, 'unixepoch', 'localtime') as datum
    from moz_places, moz_historyvisits where moz_places.id = moz_historyvisits.place_id
    and #{query} order by datum desc limit 1 COLLATE NOCASE;"`.strip
    FileUtils.rm_f(tmpfile)

    return false if most_recent.strip.empty?

    marks = JSON.parse(most_recent)

    marks.map! do |bm|
      date = Time.parse(bm['datum'])
      score = score_mark({url: bm['url'], title: bm['title']}, term)
      { url: bm['url'], title: bm['title'], date: date, score: score }
    end


    m = marks.sort_by { |m| [m[:url].length * -1, m[:score]] }.last

    [m[:url], m[:title], m[:date]]
  else
    false
  end
end

.search_history(term, types = []) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/searchlink/searches/history.rb', line 136

def search_history(term, types = [])
  if types.empty?
    return false unless SL.config['history_types']

    types = SL.config['history_types']
  end

  results = []

  if !types.empty?
    types.each do |type|
      url, title, date = send("search_#{type}", term)

      results << { 'url' => url, 'title' => title, 'date' => date } if url
    end

    if results.empty?
      false
    else
      out = results.sort_by! { |r| r['date'] }.last
      [out['url'], out['title']]
    end
  else
    false
  end
end

.search_safari_bookmarks(terms) ⇒ Array

Search Safari bookmarks for relevant search terms

Parameters:

  • terms (String)

    The search terms

Returns:

  • (Array)
    url, title, date


67
68
69
70
71
72
73
74
75
76
# File 'lib/searchlink/searches/helpers/safari.rb', line 67

def search_safari_bookmarks(terms)
  data = `plutil -convert xml1 -o - ~/Library/Safari/Bookmarks.plist`.strip
  parent = Plist.parse_xml(data)
  results = get_safari_bookmarks(parent, terms)
  return false if results.empty?

  result = results.max_by { |res| [res[:score], res[:title].length] }

  [result[:url], result[:title], Time.now]
end

.search_safari_history(term) ⇒ Object

Search Safari history for terms

Parameters:

  • term

    The search term



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/searchlink/searches/helpers/safari.rb', line 8

def search_safari_history(term)
  # Safari
  src = File.expand_path('~/Library/Safari/History.db')
  if File.exist?(src)
    SL.notify('Searching Safari History', term)

    exact_match = false
    match_phrases = []

    # If search terms start with ''term, only search for exact string matches
    if term =~ /^ *'/
      exact_match = true
      term.gsub!(/(^ *'+|'+ *$)/, '')
    elsif term =~ /%22(.*?)%22/
      match_phrases = term.scan(/%22(\S.*?\S)%22/)
      term.gsub!(/%22(\S.*?\S)%22/, '')
    end

    terms = []
    terms.push("(url NOT LIKE '%search/?%'
               AND url NOT LIKE '%?q=%' AND url NOT LIKE '%?s=%'
               AND url NOT LIKE '%duckduckgo.com/?t%')")
    if exact_match
      terms.push("(url LIKE '%#{term.strip.downcase}%' OR title LIKE '%#{term.strip.downcase}%')")
    else
      terms.concat(term.split(/\s+/).map do |t|
        "(url LIKE '%#{t.strip.downcase}%' OR title LIKE '%#{t.strip.downcase}%')"
      end)
      terms.concat(match_phrases.map do |t|
        "(url LIKE '%#{t[0].strip.downcase}%' OR title LIKE '%#{t[0].strip.downcase}%')"
      end)
    end

    query = terms.join(' AND ')

    cmd = %(sqlite3 -json '#{src}' "select title, url,
    datetime(visit_time/1000000, 'unixepoch', 'localtime') as datum
    from history_visits INNER JOIN history_items ON history_items.id = history_visits.history_item
    where #{query} order by datum desc limit 1 COLLATE NOCASE;")

    most_recent = `#{cmd}`.strip

    return false if most_recent.strip.empty?

    bm = JSON.parse(most_recent)[0]
    date = Time.parse(bm['datum'])
    [bm['url'], bm['title'], date]
  else
    false
  end
end

.settingsObject



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/searchlink/searches/history.rb', line 14

def settings
  {
    trigger: 'h(([scfabe])([hb])?)*',
    searches: [
      ['h', 'Browser History/Bookmark Search'],
      ['hsh', 'Safari History Search'],
      ['hsb', 'Safari Bookmark Search'],
      ['hshb', nil],
      ['hsbh', nil],
      ['hch', 'Chrome History Search'],
      ['hcb', 'Chrome Bookmark Search'],
      ['hchb', nil],
      ['hcbh', nil],
      ['hfh', 'Firefox History Search'],
      ['hfb', 'Firefox Bookmark Search'],
      ['hfhb', nil],
      ['hfbh', nil],
      ['hah', 'Arc History Search'],
      ['hab', 'Arc Bookmark Search'],
      ['hahb', nil],
      ['habh', nil],
      ['hbh', 'Brave History Search'],
      ['hbb', 'Brave Bookmark Search'],
      ['hbhb', nil],
      ['hbbh', nil],
      ['heh', 'Edge History Search'],
      ['heb', 'Edge Bookmark Search'],
      ['hehb', nil],
      ['hebh', nil]
    ]
  }
end