import json
import os
import sys
import urllib.parse
import urllib.request
ENGINES = {
"google": "https://www.google.com/search?q={query}",
"duckduckgo": "https://duckduckgo.com/?q={query}",
"bing": "https://www.bing.com/search?q={query}",
"brave": "https://search.brave.com/search?q={query}",
}
DDG_AC_URL = "https://duckduckgo.com/ac/?q={}&type=list"
DEBUG = os.getenv("DEBUG", "").lower() in ("1", "true", "yes")
def debug(msg: str) -> None:
if DEBUG:
print(f"[DEBUG] {msg}", file=sys.stderr)
def build_url(engine: str, query: str) -> str:
encoded = urllib.parse.quote_plus(query)
return ENGINES[engine].replace("{query}", encoded)
def fetch_suggestions(query: str) -> list[str]:
url = DDG_AC_URL.format(urllib.parse.quote_plus(query))
debug(f"Fetching suggestions from: {url}")
req = urllib.request.Request(url, headers={"User-Agent": "raffi-search/1.0"})
with urllib.request.urlopen(req, timeout=2) as resp:
data = json.loads(resp.read().decode())
if isinstance(data, list) and len(data) >= 2 and isinstance(data[1], list):
debug(f"Got {len(data[1])} suggestions")
return data[1]
debug("No suggestions found in response")
return []
def make_item(title: str, engine: str) -> dict:
url = build_url(engine, title)
engine_label = engine.capitalize()
return {
"title": title,
"subtitle": f'Search {engine_label} for "{title}"',
"arg": url,
}
def main() -> None:
if len(sys.argv) < 2:
print(json.dumps({"items": [], "error": "Usage: search.py <engine> [query]"}))
sys.exit(1)
engine = sys.argv[1].lower()
debug(f"Engine: {engine}")
if engine not in ENGINES:
known = ", ".join(sorted(ENGINES.keys()))
print(
json.dumps(
{"items": [], "error": f"Unknown engine '{engine}'. Known: {known}"}
)
)
sys.exit(1)
query = sys.argv[2] if len(sys.argv) > 2 else ""
debug(f"Query: '{query}'")
if not query.strip():
print(json.dumps({"items": []}))
return
items = [make_item(query, engine)]
try:
suggestions = fetch_suggestions(query)
for suggestion in suggestions:
if suggestion != query:
items.append(make_item(suggestion, engine))
except Exception as e:
debug(f"Error fetching suggestions: {e}")
debug(f"Returning {len(items)} items")
print(json.dumps({"items": items}))
if __name__ == "__main__":
main()