#!/usr/bin/env python3
import json
import re
import urllib.parse
import urllib.request
import sys
import os
import datetime
import gzip
import subprocess as sp
import html
import requests
################################################################################
##### C O N F I G U R A T I O N ######
################################################################################
SEARCH_ENGINE = 'google' # or 'duckduckgo'
BROWSER = 'firefox' # or 'firefox', 'chromium', 'brave', 'lynx'
TERMINAL = ['gnome-terminal', '--'] # or ['st', '-e'] or something like that
################################################################################
CONFIG = {
'BROWSER_PATH' : {
'brave' : ['brave'],
'firefox' : ['firefox'],
'chrome' : ['google-chrome-stable'],
'chromium' : ['chromium-browser'],
'lynx' : TERMINAL + ['lynx']
},
'USER_AGENT' : {
'chrome' : 'Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36',
'firefox' : 'Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:69.0) Gecko/20100101 Firefox/69.0',
'chromium' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/76.0.3809.100 Chrome/76.0.3809.100 Safari/537.36',
'brave' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36',
'lynx' : 'Lynx/2.8.9rel.1 libwww-FM/2.14 SSL-MM/1.4.1 OpenSSL/1.1.1d'
},
'SEARCH_ENGINE_NAME' : {
'google' : 'Google',
'duckduckgo' : 'DuckDuckGo'
},
'SEARCH_URL' : {
'google' : 'https://www.google.com/search?q=',
'duckduckgo' : 'https://duckduckgo.com/?q='
},
'SUGGESTION_URL' : {
'google' : 'https://www.google.com/complete/search?',
'duckduckgo' : 'https://duckduckgo.com/ac/?'
}
}
USER_AGENT = 'Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36'
def cleanhtml(txt):
"""Clean HTML tags from a string."""
return re.sub(r'<.*?>', '', txt)
def clean_html(text):
"""Membuang tag HTML dan karakter yang tidak diinginkan."""
clean_text = re.sub(r'<.*?>', '', text) # Menghapus tag HTML
clean_text = re.sub(r'https?://\S+', '', clean_text) # Menghapus URL
return clean_text.strip()
def fetch_suggestions(search_string):
if search_string.startswith('!'):
return [] # DuckDuckGo tidak beri saran untuk pencarian dengan tanda '!'
try:
# Mencari saran dari DuckDuckGo
url = f'https://duckduckgo.com/ac/?q={search_string}&kl=wt-wt'
headers = {
'User-Agent': USER_AGENT
}
response = requests.get(url, headers=headers)
response.raise_for_status() # Pastikan tidak ada error pada request
suggestions = response.json()
return [clean_html(res['phrase']) for res in suggestions]
except Exception as e:
print(f'DuckDuckGo Error: {e}', file=sys.stderr)
# Jika gagal, coba ambil saran dari Google
try:
google_url = f'https://www.google.com/complete/search?q={search_string}&client=psy-ab'
response = requests.get(google_url, headers=headers)
response.raise_for_status()
google_suggestions = response.json()
return [clean_html(sug[0]) for sug in google_suggestions[1]] # Ambil saran dari Google
except Exception as e:
print(f'Google Error: {e}', file=sys.stderr)
return [] # Jika kedua sumber gagal, kembalikan daftar kosong
def main():
search_string = html.unescape((' '.join(sys.argv[1:])).strip())
if search_string.endswith('!'):
search_string = search_string.rstrip('!').strip()
results = fetch_suggestions(search_string)
for r in results:
print(html.unescape(r))
elif search_string == '':
print('!!-- Type something and search it with %s' % CONFIG['SEARCH_ENGINE_NAME'][SEARCH_ENGINE])
print('!!-- Close your search string with "!" to get search suggestions')
else:
url = CONFIG['SEARCH_URL'][SEARCH_ENGINE] + urllib.parse.quote_plus(search_string)
sp.Popen(CONFIG['BROWSER_PATH'][BROWSER] + [url], stdout=sp.DEVNULL, stderr=sp.DEVNULL, shell=False)
def validate_config(c):
if type(c) != dict:
print('Configuration file must be a JSON object', file=sys.stderr)
sys.exit(1)
for k in ('SEARCH_ENGINE', 'BROWSER', 'TERMINAL'):
if k not in c:
print('Configuration file is missing %s' % k, file=sys.stderr)
sys.exit(1)
for k in ('SEARCH_ENGINE', 'BROWSER'):
if type(c[k]) != str:
print('Configuration Error: The value of %s must be a string' % k, file=sys.stderr)
if type(c['TERMINAL']) != list:
print('Configuration Error: The value of TERMINAL must be a list of strings', file=sys.stderr)
sys.exit(1)
for x in c['TERMINAL']:
if type(x) != str:
print('Configuration Error: The value of TERMINAL must be a list of strings', file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
try:
fname = os.path.expanduser('~/.config/rofi-web-search/config.json')
if os.path.exists(fname):
try:
config = json.loads(open(fname, 'r').read())
except json.JSONDecodeError:
print('Configuration file %s is not a valid JSON' % fname, file=sys.stderr)
sys.exit(1)
validate_config(config)
SEARCH_ENGINE = config['SEARCH_ENGINE']
BROWSER = config['BROWSER']
TERMINAL = config['TERMINAL']
else:
# Create default config
config = {
'SEARCH_ENGINE' : SEARCH_ENGINE,
'BROWSER' : BROWSER,
'TERMINAL' : TERMINAL
}
os.makedirs(os.path.dirname(fname))
f = open(fname, 'w')
f.write(json.dumps(config, indent=4))
f.write('\n')
f.close()
main()
except:
sys.exit(1)