File size: 3,710 Bytes
4606755
 
98b1c51
0c4cf03
 
e3af794
7c22da3
0c4cf03
4606755
 
98b1c51
7c22da3
98b1c51
7c22da3
 
 
 
4606755
 
0c4cf03
 
98b1c51
e3af794
 
98b1c51
7c22da3
 
98b1c51
6f7b315
 
98b1c51
4606755
 
98b1c51
6f7b315
 
98b1c51
f02ffb2
 
4606755
6f7b315
 
 
 
 
 
 
 
 
 
 
 
 
98b1c51
6f7b315
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0c4cf03
 
 
 
 
98b1c51
0c4cf03
 
 
 
 
 
 
4606755
 
 
 
 
98b1c51
4606755
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f02ffb2
 
 
98b1c51
f02ffb2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
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
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import type { YouWebSearch } from "../../types/WebSearch";
import { WebSearchProvider } from "../../types/WebSearch";
import { env } from "$env/dynamic/private";
import { getJson } from "serpapi";
import type { GoogleParameters } from "serpapi";
import { searchWebLocal } from "./searchWebLocal";
import { searchSearxng } from "./searchSearxng";

// get which SERP api is providing web results
export function getWebSearchProvider() {
	if (env.YDC_API_KEY) {
		return WebSearchProvider.YOU;
	} else if (env.SEARXNG_QUERY_URL) {
		return WebSearchProvider.SEARXNG;
	} else {
		return WebSearchProvider.GOOGLE;
	}
}

// Show result as JSON
export async function searchWeb(query: string) {
	if (env.USE_LOCAL_WEBSEARCH) {
		return await searchWebLocal(query);
	}
	if (env.SEARXNG_QUERY_URL) {
		return await searchSearxng(query);
	}
	if (env.SERPER_API_KEY) {
		return await searchWebSerper(query);
	}
	if (env.YDC_API_KEY) {
		return await searchWebYouApi(query);
	}
	if (env.SERPAPI_KEY) {
		return await searchWebSerpApi(query);
	}
	if (env.SERPSTACK_API_KEY) {
		return await searchSerpStack(query);
	}
	throw new Error("No You.com or Serper.dev or SerpAPI key found");
}

export async function searchWebSerper(query: string) {
	const params = {
		q: query,
		hl: "en",
		gl: "us",
	};

	const response = await fetch("https://google.serper.dev/search", {
		method: "POST",
		body: JSON.stringify(params),
		headers: {
			"x-api-key": env.SERPER_API_KEY,
			"Content-type": "application/json; charset=UTF-8",
		},
	});

	/* eslint-disable @typescript-eslint/no-explicit-any */
	const data = (await response.json()) as Record<string, any>;

	if (!response.ok) {
		throw new Error(
			data["message"] ??
				`Serper API returned error code ${response.status} - ${response.statusText}`
		);
	}

	return {
		organic_results: data["organic"] ?? [],
	};
}

export async function searchWebSerpApi(query: string) {
	const params = {
		q: query,
		hl: "en",
		gl: "us",
		google_domain: "google.com",
		api_key: env.SERPAPI_KEY,
	} satisfies GoogleParameters;

	// Show result as JSON
	const response = await getJson("google", params);

	return response;
}

export async function searchWebYouApi(query: string) {
	const response = await fetch(`https://api.ydc-index.io/search?query=${query}`, {
		method: "GET",
		headers: {
			"X-API-Key": env.YDC_API_KEY,
			"Content-type": "application/json; charset=UTF-8",
		},
	});

	if (!response.ok) {
		throw new Error(`You.com API returned error code ${response.status} - ${response.statusText}`);
	}

	const data = (await response.json()) as YouWebSearch;
	const formattedResultsWithSnippets = data.hits
		.map(({ title, url, snippets }) => ({
			title,
			link: url,
			text: snippets?.join("\n") || "",
			hostname: new URL(url).hostname,
		}))
		.sort((a, b) => b.text.length - a.text.length); // desc order by text length

	return {
		organic_results: formattedResultsWithSnippets,
	};
}

export async function searchSerpStack(query: string) {
	const response = await fetch(
		`http://api.serpstack.com/search?access_key=${env.SERPSTACK_API_KEY}&query=${query}&hl=en&gl=us`,
		{
			method: "GET",
			headers: {
				"Content-type": "application/json; charset=UTF-8",
			},
		}
	);

	const data = (await response.json()) as Record<string, any>;

	if (!response.ok) {
		throw new Error(
			data["error"] ??
				`SerpStack API returned error code ${response.status} - ${response.statusText}`
		);
	}

	const resultsWithSnippets = data["organic_results"].map(
		({ title, url, snippet }: { title: string; url: string; snippet: string | undefined }) => ({
			title,
			link: url,
			text: snippet || "",
		})
	);

	return {
		organic_results: resultsWithSnippets ?? [],
	};
}