.oO SearXNG Developer Documentation Oo.
Loading...
Searching...
No Matches
external_bang.py
Go to the documentation of this file.
1# SPDX-License-Identifier: AGPL-3.0-or-later
2# pylint: disable=missing-module-docstring
3
4__all__ = ["get_bang_url"]
5
6import typing as t
7
8from urllib.parse import quote_plus, urlparse
9from searx.data import EXTERNAL_BANGS
10
11LEAF_KEY = chr(16)
12
13if t.TYPE_CHECKING:
14 from searx.search.models import SearchQuery
15
16
17def get_node(external_bangs_db: dict[str, t.Any], bang: str):
18 node = external_bangs_db['trie']
19 after = ''
20 before = ''
21 for bang_letter in bang:
22 after += bang_letter
23 if after in node and isinstance(node, dict):
24 node = node[after]
25 before += after
26 after = ''
27 return node, before, after
28
29
30def get_bang_definition_and_ac(external_bangs_db: dict[str, t.Any], bang: str):
31 node, before, after = get_node(external_bangs_db, bang)
32
33 bang_definition = None
34 bang_ac_list = []
35 if after != '':
36 for k in node:
37 if k.startswith(after):
38 bang_ac_list.append(before + k)
39 elif isinstance(node, dict):
40 bang_definition = node.get(LEAF_KEY)
41 bang_ac_list = [before + k for k in node.keys() if k != LEAF_KEY]
42 elif isinstance(node, str):
43 bang_definition = node
44 bang_ac_list = []
45
46 return bang_definition, bang_ac_list
47
48
49def resolve_bang_definition(bang_definition: str, query: str) -> tuple[str, int]:
50 url, rank = bang_definition.split(chr(1))
51 if url.startswith('//'):
52 url = 'https:' + url
53 if query:
54 url = url.replace(chr(2), quote_plus(query))
55 else:
56 # go to main instead of search page
57 o = urlparse(url)
58 url = o.scheme + '://' + o.netloc
59
60 rank = int(rank) if len(rank) > 0 else 0
61 return (url, rank)
62
63
65 bang: str, external_bangs_db: dict[str, t.Any] | None = None
66): # pylint: disable=invalid-name
67 if external_bangs_db is None:
68 external_bangs_db = EXTERNAL_BANGS
69
70 bang_definition, bang_ac_list = get_bang_definition_and_ac(external_bangs_db, bang)
71
72 new_autocomplete = []
73 current = [*bang_ac_list]
74 done = set()
75 while len(current) > 0:
76 bang_ac = current.pop(0)
77 done.add(bang_ac)
78
79 current_bang_definition, current_bang_ac_list = get_bang_definition_and_ac(external_bangs_db, bang_ac)
80 if current_bang_definition:
81 _, order = resolve_bang_definition(current_bang_definition, '')
82 new_autocomplete.append((bang_ac, order))
83 for new_bang in current_bang_ac_list:
84 if new_bang not in done and new_bang not in current:
85 current.append(new_bang)
86
87 new_autocomplete.sort(key=lambda t: (-t[1], t[0]))
88 new_autocomplete = list(map(lambda t: t[0], new_autocomplete))
89
90 return bang_definition, new_autocomplete
91
92
93def get_bang_url(search_query: "SearchQuery", external_bangs_db: dict[str, t.Any] | None = None) -> str | None:
94 """
95 Redirects if the user supplied a correct bang search.
96 :param search_query: This is a search_query object which contains preferences and the submitted queries.
97 :return: None if the bang was invalid, else a string of the redirect url.
98 """
99 ret_val = None
100
101 if external_bangs_db is None:
102 external_bangs_db = EXTERNAL_BANGS
103
104 if search_query.external_bang:
105 bang_definition, _ = get_bang_definition_and_ac(external_bangs_db, search_query.external_bang)
106 if bang_definition and isinstance(bang_definition, str):
107 ret_val = resolve_bang_definition(bang_definition, search_query.query)[0]
108
109 return ret_val
str|None get_bang_url("SearchQuery" search_query, dict[str, t.Any]|None external_bangs_db=None)
get_bang_definition_and_ac(dict[str, t.Any] external_bangs_db, str bang)
get_bang_definition_and_autocomplete(str bang, dict[str, t.Any]|None external_bangs_db=None)
tuple[str, int] resolve_bang_definition(str bang_definition, str query)
get_node(dict[str, t.Any] external_bangs_db, str bang)