Merge remote-tracking branch 'origin/main' into feature/public-instance-sessions
This commit is contained in:
commit
50d592bcec
|
@ -9,8 +9,6 @@ import os
|
||||||
from stem import Signal, SocketError
|
from stem import Signal, SocketError
|
||||||
from stem.control import Controller
|
from stem.control import Controller
|
||||||
|
|
||||||
SEARCH_URL = 'https://www.google.com/search?gbv=1&num=' + str(
|
|
||||||
os.getenv('WHOOGLE_RESULTS_PER_PAGE', 10)) + '&q='
|
|
||||||
MAPS_URL = 'https://maps.google.com/maps'
|
MAPS_URL = 'https://maps.google.com/maps'
|
||||||
AUTOCOMPLETE_URL = ('https://suggestqueries.google.com/'
|
AUTOCOMPLETE_URL = ('https://suggestqueries.google.com/'
|
||||||
'complete/search?client=toolbar&')
|
'complete/search?client=toolbar&')
|
||||||
|
@ -151,6 +149,8 @@ class Request:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, normal_ua, root_path, config: Config):
|
def __init__(self, normal_ua, root_path, config: Config):
|
||||||
|
self.search_url = 'https://www.google.com/search?gbv=1&num=' + str(
|
||||||
|
os.getenv('WHOOGLE_RESULTS_PER_PAGE', 10)) + '&q='
|
||||||
# Send heartbeat to Tor, used in determining if the user can or cannot
|
# Send heartbeat to Tor, used in determining if the user can or cannot
|
||||||
# enable Tor for future requests
|
# enable Tor for future requests
|
||||||
send_tor_signal(Signal.HEARTBEAT)
|
send_tor_signal(Signal.HEARTBEAT)
|
||||||
|
@ -224,7 +224,7 @@ class Request:
|
||||||
return [_.attrib['data'] for _ in
|
return [_.attrib['data'] for _ in
|
||||||
root.findall('.//suggestion/[@data]')]
|
root.findall('.//suggestion/[@data]')]
|
||||||
|
|
||||||
def send(self, base_url=SEARCH_URL, query='', attempt=0,
|
def send(self, base_url='', query='', attempt=0,
|
||||||
force_mobile=False) -> Response:
|
force_mobile=False) -> Response:
|
||||||
"""Sends an outbound request to a URL. Optionally sends the request
|
"""Sends an outbound request to a URL. Optionally sends the request
|
||||||
using Tor, if enabled by the user.
|
using Tor, if enabled by the user.
|
||||||
|
@ -285,7 +285,7 @@ class Request:
|
||||||
disable=True)
|
disable=True)
|
||||||
|
|
||||||
response = requests.get(
|
response = requests.get(
|
||||||
base_url + query,
|
(base_url or self.search_url) + query,
|
||||||
proxies=self.proxies,
|
proxies=self.proxies,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
cookies=cookies)
|
cookies=cookies)
|
||||||
|
@ -295,6 +295,6 @@ class Request:
|
||||||
attempt += 1
|
attempt += 1
|
||||||
if attempt > 10:
|
if attempt > 10:
|
||||||
raise TorError("Tor query failed -- max attempts exceeded 10")
|
raise TorError("Tor query failed -- max attempts exceeded 10")
|
||||||
return self.send(base_url, query, attempt)
|
return self.send((base_url or self.search_url), query, attempt)
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
|
@ -14,7 +14,7 @@ from app import app
|
||||||
from app.models.config import Config
|
from app.models.config import Config
|
||||||
from app.request import Request, TorError
|
from app.request import Request, TorError
|
||||||
from app.utils.bangs import resolve_bang
|
from app.utils.bangs import resolve_bang
|
||||||
from app.utils.misc import read_config_bool
|
from app.utils.misc import read_config_bool, get_client_ip
|
||||||
from app.utils.results import add_ip_card
|
from app.utils.results import add_ip_card
|
||||||
from app.utils.results import bold_search_terms
|
from app.utils.results import bold_search_terms
|
||||||
from app.utils.search import *
|
from app.utils.search import *
|
||||||
|
@ -301,10 +301,11 @@ def search():
|
||||||
# Return 503 if temporarily blocked by captcha
|
# Return 503 if temporarily blocked by captcha
|
||||||
resp_code = 503 if has_captcha(str(response)) else 200
|
resp_code = 503 if has_captcha(str(response)) else 200
|
||||||
response = bold_search_terms(response, query)
|
response = bold_search_terms(response, query)
|
||||||
|
|
||||||
# Feature to display IP address
|
# Feature to display IP address
|
||||||
if search_util.check_kw_ip():
|
if search_util.check_kw_ip():
|
||||||
html_soup = bsoup(response, "html.parser")
|
html_soup = bsoup(str(response), 'html.parser')
|
||||||
response = add_ip_card(html_soup, request.remote_addr)
|
response = add_ip_card(html_soup, get_client_ip(request))
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
'display.html',
|
'display.html',
|
||||||
|
@ -322,7 +323,7 @@ def search():
|
||||||
is_translation=any(
|
is_translation=any(
|
||||||
_ in query.lower() for _ in [translation['translate'], 'translate']
|
_ in query.lower() for _ in [translation['translate'], 'translate']
|
||||||
) and not search_util.search_type, # Standard search queries only
|
) and not search_util.search_type, # Standard search queries only
|
||||||
response=html.unescape(str(response)),
|
response=response,
|
||||||
version_number=app.config['VERSION_NUMBER'],
|
version_number=app.config['VERSION_NUMBER'],
|
||||||
search_header=(render_template(
|
search_header=(render_template(
|
||||||
'header.html',
|
'header.html',
|
||||||
|
@ -434,7 +435,13 @@ def window():
|
||||||
for script in results('script'):
|
for script in results('script'):
|
||||||
script.decompose()
|
script.decompose()
|
||||||
|
|
||||||
return render_template('display.html', response=results)
|
return render_template(
|
||||||
|
'display.html',
|
||||||
|
response=results,
|
||||||
|
translation=app.config['TRANSLATIONS'][
|
||||||
|
g.user_config.get_localization_lang()
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def run_app() -> None:
|
def run_app() -> None:
|
||||||
|
|
|
@ -102,6 +102,7 @@ button::-moz-focus-inner {
|
||||||
}
|
}
|
||||||
|
|
||||||
.open {
|
.open {
|
||||||
|
overflow-y: scroll;
|
||||||
padding-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ const setupConfigLayout = () => {
|
||||||
if (content.style.maxHeight) {
|
if (content.style.maxHeight) {
|
||||||
content.style.maxHeight = null;
|
content.style.maxHeight = null;
|
||||||
} else {
|
} else {
|
||||||
content.style.maxHeight = content.scrollHeight + "px";
|
content.style.maxHeight = "400px";
|
||||||
}
|
}
|
||||||
|
|
||||||
content.classList.toggle("open");
|
content.classList.toggle("open");
|
||||||
|
|
|
@ -1,45 +1,45 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<link rel="shortcut icon" href="static/img/favicon.ico" type="image/x-icon">
|
<link rel="shortcut icon" href="static/img/favicon.ico" type="image/x-icon">
|
||||||
<link rel="icon" href="static/img/favicon.ico" type="image/x-icon">
|
<link rel="icon" href="static/img/favicon.ico" type="image/x-icon">
|
||||||
<link rel="search" href="opensearch.xml" type="application/opensearchdescription+xml" title="Whoogle Search">
|
<link rel="search" href="opensearch.xml" type="application/opensearchdescription+xml" title="Whoogle Search">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta name="referrer" content="no-referrer">
|
<meta name="referrer" content="no-referrer">
|
||||||
<link rel="stylesheet" href="{{ cb_url('input.css') }}">
|
<link rel="stylesheet" href="{{ cb_url('input.css') }}">
|
||||||
<link rel="stylesheet" href="{{ cb_url('search.css') }}">
|
<link rel="stylesheet" href="{{ cb_url('search.css') }}">
|
||||||
<link rel="stylesheet" href="{{ cb_url('header.css') }}">
|
<link rel="stylesheet" href="{{ cb_url('header.css') }}">
|
||||||
{% if config.theme %}
|
{% if config.theme %}
|
||||||
{% if config.theme == 'system' %}
|
{% if config.theme == 'system' %}
|
||||||
<style>
|
<style>
|
||||||
@import "{{ cb_url('light-theme.css') }}" screen;
|
@import "{{ cb_url('light-theme.css') }}" screen;
|
||||||
@import "{{ cb_url('dark-theme.css') }}" screen and (prefers-color-scheme: dark);
|
@import "{{ cb_url('dark-theme.css') }}" screen and (prefers-color-scheme: dark);
|
||||||
</style>
|
</style>
|
||||||
{% else %}
|
|
||||||
<link rel="stylesheet" href="{{ cb_url(config.theme + '-theme.css') }}"/>
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<link rel="stylesheet" href="{{ cb_url(('dark' if config.dark else 'light') + '-theme.css') }}"/>
|
<link rel="stylesheet" href="{{ cb_url(config.theme + '-theme.css') }}"/>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<style>{{ config.style }}</style>
|
{% else %}
|
||||||
<title>{{ clean_query(query) }} - Whoogle Search</title>
|
<link rel="stylesheet" href="{{ cb_url(('dark' if config.dark else 'light') + '-theme.css') }}"/>
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
{{ search_header|safe }}
|
|
||||||
{% if is_translation %}
|
|
||||||
<iframe
|
|
||||||
id="lingva-iframe"
|
|
||||||
src="{{ lingva_url }}/auto/{{ translate_to }}/{{ translate_str }}">
|
|
||||||
</iframe>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ response|safe }}
|
<style>{{ config.style }}</style>
|
||||||
</body>
|
<title>{{ clean_query(query) }} - Whoogle Search</title>
|
||||||
<footer>
|
</head>
|
||||||
<p class="footer">
|
<body>
|
||||||
Whoogle Search v{{ version_number }} ||
|
{{ search_header|safe }}
|
||||||
<a id="gh-link" href="https://github.com/benbusby/whoogle-search">{{ translation['github-link'] }}</a>
|
{% if is_translation %}
|
||||||
</p>
|
<iframe
|
||||||
</footer>
|
id="lingva-iframe"
|
||||||
<script src="{{ cb_url('autocomplete.js') }}"></script>
|
src="{{ lingva_url }}/auto/{{ translate_to }}/{{ translate_str }}">
|
||||||
<script src="{{ cb_url('utils.js') }}"></script>
|
</iframe>
|
||||||
<script src="{{ cb_url('keyboard.js') }}"></script>
|
{% endif %}
|
||||||
|
{{ response|safe }}
|
||||||
|
</body>
|
||||||
|
<footer>
|
||||||
|
<p class="footer">
|
||||||
|
Whoogle Search v{{ version_number }} ||
|
||||||
|
<a id="gh-link" href="https://github.com/benbusby/whoogle-search">{{ translation['github-link'] }}</a>
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
<script src="{{ cb_url('autocomplete.js') }}"></script>
|
||||||
|
<script src="{{ cb_url('utils.js') }}"></script>
|
||||||
|
<script src="{{ cb_url('keyboard.js') }}"></script>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
<header>
|
<header>
|
||||||
<div class="bz1lBb header-div">
|
<div class="bz1lBb header-div">
|
||||||
<form class="search-form Pg70bf"
|
<form class="search-form Pg70bf"
|
||||||
id="search-form"
|
id="search-form"
|
||||||
method="{{ 'GET' if config.get_only else 'POST' }}">
|
method="{{ 'GET' if config.get_only else 'POST' }}">
|
||||||
<a class="logo-link mobile-logo" href="home">
|
<a class="logo-link mobile-logo" href="home">
|
||||||
<div id="mobile-header-logo">
|
<div id="mobile-header-logo">
|
||||||
{{ logo|safe }}
|
{{ logo|safe }}
|
||||||
|
@ -12,17 +12,17 @@
|
||||||
<div class="H0PQec mobile-input-div">
|
<div class="H0PQec mobile-input-div">
|
||||||
<div class="sbc esbc autocomplete">
|
<div class="sbc esbc autocomplete">
|
||||||
<input
|
<input
|
||||||
id="search-bar"
|
id="search-bar"
|
||||||
class="mobile-search-bar"
|
class="mobile-search-bar"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
autocorrect="off"
|
autocorrect="off"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
class="noHIxc"
|
class="noHIxc"
|
||||||
name="q"
|
name="q"
|
||||||
type="text"
|
type="text"
|
||||||
value="{{ clean_query(query) }}"
|
value="{{ clean_query(query) }}"
|
||||||
dir="auto">
|
dir="auto">
|
||||||
<input id="search-reset" type="reset" value="x">
|
<input id="search-reset" type="reset" value="x">
|
||||||
<input name="tbm" value="{{ search_type }}" style="display: none">
|
<input name="tbm" value="{{ search_type }}" style="display: none">
|
||||||
<input type="submit" style="display: none;">
|
<input type="submit" style="display: none;">
|
||||||
|
@ -43,22 +43,22 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="search-div">
|
<div class="search-div">
|
||||||
<form id="search-form"
|
<form id="search-form"
|
||||||
class="search-form"
|
class="search-form"
|
||||||
id="sf"
|
id="sf"
|
||||||
method="{{ 'GET' if config.get_only else 'POST' }}">
|
method="{{ 'GET' if config.get_only else 'POST' }}">
|
||||||
<div class="autocomplete header-autocomplete">
|
<div class="autocomplete header-autocomplete">
|
||||||
<div style="width: 100%; display: flex">
|
<div style="width: 100%; display: flex">
|
||||||
<input
|
<input
|
||||||
id="search-bar"
|
id="search-bar"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
autocorrect="off"
|
autocorrect="off"
|
||||||
class="search-bar-desktop noHIxc"
|
class="search-bar-desktop noHIxc"
|
||||||
name="q"
|
name="q"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
type="text"
|
type="text"
|
||||||
value="{{ clean_query(query) }}"
|
value="{{ clean_query(query) }}"
|
||||||
dir="auto">
|
dir="auto">
|
||||||
<input name="tbm" value="{{ search_type }}" style="display: none">
|
<input name="tbm" value="{{ search_type }}" style="display: none">
|
||||||
<input type="submit" style="display: none;">
|
<input type="submit" style="display: none;">
|
||||||
<div class="sc"></div>
|
<div class="sc"></div>
|
||||||
|
|
|
@ -1,221 +1,242 @@
|
||||||
<html style="background: #000;">
|
<html style="background: #000;">
|
||||||
<head>
|
<head>
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="static/img/favicon/apple-icon-57x57.png">
|
<link rel="apple-touch-icon" sizes="57x57" href="static/img/favicon/apple-icon-57x57.png">
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="static/img/favicon/apple-icon-60x60.png">
|
<link rel="apple-touch-icon" sizes="60x60" href="static/img/favicon/apple-icon-60x60.png">
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="static/img/favicon/apple-icon-72x72.png">
|
<link rel="apple-touch-icon" sizes="72x72" href="static/img/favicon/apple-icon-72x72.png">
|
||||||
<link rel="apple-touch-icon" sizes="76x76" href="static/img/favicon/apple-icon-76x76.png">
|
<link rel="apple-touch-icon" sizes="76x76" href="static/img/favicon/apple-icon-76x76.png">
|
||||||
<link rel="apple-touch-icon" sizes="114x114" href="static/img/favicon/apple-icon-114x114.png">
|
<link rel="apple-touch-icon" sizes="114x114" href="static/img/favicon/apple-icon-114x114.png">
|
||||||
<link rel="apple-touch-icon" sizes="120x120" href="static/img/favicon/apple-icon-120x120.png">
|
<link rel="apple-touch-icon" sizes="120x120" href="static/img/favicon/apple-icon-120x120.png">
|
||||||
<link rel="apple-touch-icon" sizes="144x144" href="static/img/favicon/apple-icon-144x144.png">
|
<link rel="apple-touch-icon" sizes="144x144" href="static/img/favicon/apple-icon-144x144.png">
|
||||||
<link rel="apple-touch-icon" sizes="152x152" href="static/img/favicon/apple-icon-152x152.png">
|
<link rel="apple-touch-icon" sizes="152x152" href="static/img/favicon/apple-icon-152x152.png">
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="static/img/favicon/apple-icon-180x180.png">
|
<link rel="apple-touch-icon" sizes="180x180" href="static/img/favicon/apple-icon-180x180.png">
|
||||||
<link rel="icon" type="image/png" sizes="192x192" href="static/img/favicon/android-icon-192x192.png">
|
<link rel="icon" type="image/png" sizes="192x192" href="static/img/favicon/android-icon-192x192.png">
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="static/img/favicon/favicon-32x32.png">
|
<link rel="icon" type="image/png" sizes="32x32" href="static/img/favicon/favicon-32x32.png">
|
||||||
<link rel="icon" type="image/png" sizes="96x96" href="static/img/favicon/favicon-96x96.png">
|
<link rel="icon" type="image/png" sizes="96x96" href="static/img/favicon/favicon-96x96.png">
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="static/img/favicon/favicon-16x16.png">
|
<link rel="icon" type="image/png" sizes="16x16" href="static/img/favicon/favicon-16x16.png">
|
||||||
<link rel="manifest" href="static/img/favicon/manifest.json">
|
<link rel="manifest" href="static/img/favicon/manifest.json">
|
||||||
<meta name="referrer" content="no-referrer">
|
<meta name="referrer" content="no-referrer">
|
||||||
<meta name="msapplication-TileColor" content="#ffffff">
|
<meta name="msapplication-TileColor" content="#ffffff">
|
||||||
<meta name="msapplication-TileImage" content="static/img/favicon/ms-icon-144x144.png">
|
<meta name="msapplication-TileImage" content="static/img/favicon/ms-icon-144x144.png">
|
||||||
<script type="text/javascript" src="{{ cb_url('autocomplete.js') }}"></script>
|
<script type="text/javascript" src="{{ cb_url('autocomplete.js') }}"></script>
|
||||||
<script type="text/javascript" src="{{ cb_url('controller.js') }}"></script>
|
<script type="text/javascript" src="{{ cb_url('controller.js') }}"></script>
|
||||||
<link rel="search" href="opensearch.xml" type="application/opensearchdescription+xml" title="Whoogle Search">
|
<link rel="search" href="opensearch.xml" type="application/opensearchdescription+xml" title="Whoogle Search">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
{% if config.theme %}
|
{% if config.theme %}
|
||||||
{% if config.theme == 'system' %}
|
{% if config.theme == 'system' %}
|
||||||
<style>
|
<style>
|
||||||
@import "{{ cb_url('light-theme.css') }}" screen;
|
@import "{{ cb_url('light-theme.css') }}" screen;
|
||||||
@import "{{ cb_url('dark-theme.css') }}" screen and (prefers-color-scheme: dark);
|
@import "{{ cb_url('dark-theme.css') }}" screen and (prefers-color-scheme: dark);
|
||||||
</style>
|
</style>
|
||||||
{% else %}
|
|
||||||
<link rel="stylesheet" href="{{ cb_url(config.theme + '-theme.css') }}"/>
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<link rel="stylesheet" href="{{ cb_url(('dark' if config.dark else 'light') + '-theme.css') }}"/>
|
<link rel="stylesheet" href="{{ cb_url(config.theme + '-theme.css') }}"/>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<link rel="stylesheet" href="{{ cb_url('main.css') }}">
|
{% else %}
|
||||||
<noscript>
|
<link rel="stylesheet" href="{{ cb_url(('dark' if config.dark else 'light') + '-theme.css') }}"/>
|
||||||
<style>
|
{% endif %}
|
||||||
#main { display: inherit !important; }
|
<link rel="stylesheet" href="{{ cb_url('main.css') }}">
|
||||||
.content { max-height: 720px; padding: 18px; border-radius: 10px; }
|
<noscript>
|
||||||
.collapsible { display: none; }
|
<style>
|
||||||
</style>
|
#main {
|
||||||
</noscript>
|
display: inherit !important;
|
||||||
<style>{{ config.style }}</style>
|
}
|
||||||
<title>Whoogle Search</title>
|
|
||||||
</head>
|
.content {
|
||||||
<body id="main">
|
max-height: 400px;
|
||||||
<div class="search-container">
|
padding: 18px;
|
||||||
<div class="logo-container">
|
border-radius: 10px;
|
||||||
{{ logo|safe }}
|
overflow-y: scroll;
|
||||||
</div>
|
}
|
||||||
<form id="search-form" action="search" method="{{ 'get' if config.get_only else 'post' }}">
|
|
||||||
<div class="search-fields">
|
.collapsible {
|
||||||
<div class="autocomplete">
|
display: none;
|
||||||
<input
|
}
|
||||||
type="text"
|
</style>
|
||||||
name="q"
|
</noscript>
|
||||||
id="search-bar"
|
<style>{{ config.style }}</style>
|
||||||
class="home-search"
|
<title>Whoogle Search</title>
|
||||||
autofocus="autofocus"
|
</head>
|
||||||
autocapitalize="none"
|
<body id="main">
|
||||||
spellcheck="false"
|
<div class="search-container">
|
||||||
autocorrect="off"
|
<div class="logo-container">
|
||||||
autocomplete="off"
|
{{ logo|safe }}
|
||||||
dir="auto">
|
</div>
|
||||||
</div>
|
<form id="search-form" action="search" method="{{ 'get' if config.get_only else 'post' }}">
|
||||||
<input type="submit" id="search-submit" value="{{ translation['search'] }}">
|
<div class="search-fields">
|
||||||
</div>
|
<div class="autocomplete">
|
||||||
</form>
|
<input
|
||||||
{% if not config_disabled %}
|
type="text"
|
||||||
<br/>
|
name="q"
|
||||||
<button id="config-collapsible" class="collapsible">{{ translation['config'] }}</button>
|
id="search-bar"
|
||||||
<div class="content">
|
class="home-search"
|
||||||
<div class="config-fields">
|
autofocus="autofocus"
|
||||||
<form id="config-form" action="config" method="post">
|
autocapitalize="none"
|
||||||
<div class="config-div config-div-ctry">
|
spellcheck="false"
|
||||||
<label for="config-ctry">{{ translation['config-country'] }}: </label>
|
autocorrect="off"
|
||||||
<select name="ctry" id="config-ctry">
|
autocomplete="off"
|
||||||
{% for ctry in countries %}
|
dir="auto">
|
||||||
<option value="{{ ctry.value }}"
|
</div>
|
||||||
{% if ctry.value in config.ctry %}
|
<input type="submit" id="search-submit" value="{{ translation['search'] }}">
|
||||||
selected
|
</div>
|
||||||
{% endif %}>
|
</form>
|
||||||
{{ ctry.name }}
|
{% if not config_disabled %}
|
||||||
</option>
|
<br/>
|
||||||
{% endfor %}
|
<button id="config-collapsible" class="collapsible">{{ translation['config'] }}</button>
|
||||||
</select>
|
<div class="content">
|
||||||
<div><span class="info-text"> — {{ translation['config-country-help'] }}</span></div>
|
<div class="config-fields">
|
||||||
</div>
|
<form id="config-form" action="config" method="post">
|
||||||
<div class="config-div config-div-lang">
|
<div class="config-div config-div-ctry">
|
||||||
<label for="config-lang-interface">{{ translation['config-lang'] }}: </label>
|
<label for="config-ctry">{{ translation['config-country'] }}: </label>
|
||||||
<select name="lang_interface" id="config-lang-interface">
|
<select name="ctry" id="config-ctry">
|
||||||
{% for lang in languages %}
|
{% for ctry in countries %}
|
||||||
<option value="{{ lang.value }}"
|
<option value="{{ ctry.value }}"
|
||||||
{% if lang.value in config.lang_interface %}
|
{% if ctry.value in config.ctry %}
|
||||||
selected
|
selected
|
||||||
{% endif %}>
|
{% endif %}>
|
||||||
{{ lang.name }}
|
{{ ctry.name }}
|
||||||
</option>
|
</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
<div><span class="info-text"> — {{ translation['config-country-help'] }}</span></div>
|
||||||
<div class="config-div config-div-search-lang">
|
</div>
|
||||||
<label for="config-lang-search">{{ translation['config-lang-search'] }}: </label>
|
<div class="config-div config-div-lang">
|
||||||
<select name="lang_search" id="config-lang-search">
|
<label for="config-lang-interface">{{ translation['config-lang'] }}: </label>
|
||||||
{% for lang in languages %}
|
<select name="lang_interface" id="config-lang-interface">
|
||||||
<option value="{{ lang.value }}"
|
{% for lang in languages %}
|
||||||
{% if lang.value in config.lang_search %}
|
<option value="{{ lang.value }}"
|
||||||
selected
|
{% if lang.value in config.lang_interface %}
|
||||||
{% endif %}>
|
selected
|
||||||
{{ lang.name }}
|
{% endif %}>
|
||||||
</option>
|
{{ lang.name }}
|
||||||
{% endfor %}
|
</option>
|
||||||
</select>
|
{% endfor %}
|
||||||
</div>
|
</select>
|
||||||
<div class="config-div config-div-near">
|
</div>
|
||||||
<label for="config-near">{{ translation['config-near'] }}: </label>
|
<div class="config-div config-div-search-lang">
|
||||||
<input type="text" name="near" id="config-near" placeholder="{{ translation['config-near-help'] }}" value="{{ config.near }}">
|
<label for="config-lang-search">{{ translation['config-lang-search'] }}: </label>
|
||||||
</div>
|
<select name="lang_search" id="config-lang-search">
|
||||||
<div class="config-div config-div-block">
|
{% for lang in languages %}
|
||||||
<label for="config-block">{{ translation['config-block'] }}: </label>
|
<option value="{{ lang.value }}"
|
||||||
<input type="text" name="block" id="config-block" placeholder="{{ translation['config-block-help'] }}" value="{{ config.block }}">
|
{% if lang.value in config.lang_search %}
|
||||||
</div>
|
selected
|
||||||
<div class="config-div config-div-block">
|
{% endif %}>
|
||||||
<label for="config-block-title">{{ translation['config-block-title'] }}: </label>
|
{{ lang.name }}
|
||||||
<input type="text" name="block_title" id="config-block" placeholder="{{ translation['config-block-title-help'] }}" value="{{ config.block_title }}">
|
</option>
|
||||||
</div>
|
{% endfor %}
|
||||||
<div class="config-div config-div-block">
|
</select>
|
||||||
<label for="config-block-url">{{ translation['config-block-url'] }}: </label>
|
</div>
|
||||||
<input type="text" name="block_url" id="config-block" placeholder="{{ translation['config-block-url-help'] }}" value="{{ config.block_url }}">
|
<div class="config-div config-div-near">
|
||||||
</div>
|
<label for="config-near">{{ translation['config-near'] }}: </label>
|
||||||
<div class="config-div config-div-nojs">
|
<input type="text" name="near" id="config-near"
|
||||||
<label for="config-nojs">{{ translation['config-nojs'] }}: </label>
|
placeholder="{{ translation['config-near-help'] }}" value="{{ config.near }}">
|
||||||
<input type="checkbox" name="nojs" id="config-nojs" {{ 'checked' if config.nojs else '' }}>
|
</div>
|
||||||
</div>
|
<div class="config-div config-div-block">
|
||||||
<div class="config-div config-div-theme">
|
<label for="config-block">{{ translation['config-block'] }}: </label>
|
||||||
<label for="config-theme">{{ translation['config-theme'] }}: </label>
|
<input type="text" name="block" id="config-block"
|
||||||
<select name="theme" id="config-theme">
|
placeholder="{{ translation['config-block-help'] }}" value="{{ config.block }}">
|
||||||
{% for theme in themes %}
|
</div>
|
||||||
<option value="{{ theme }}"
|
<div class="config-div config-div-block">
|
||||||
{% if theme in config.theme %}
|
<label for="config-block-title">{{ translation['config-block-title'] }}: </label>
|
||||||
selected
|
<input type="text" name="block_title" id="config-block"
|
||||||
{% endif %}>
|
placeholder="{{ translation['config-block-title-help'] }}"
|
||||||
{{ translation[theme].capitalize() }}
|
value="{{ config.block_title }}">
|
||||||
</option>
|
</div>
|
||||||
{% endfor %}
|
<div class="config-div config-div-block">
|
||||||
</select>
|
<label for="config-block-url">{{ translation['config-block-url'] }}: </label>
|
||||||
</div>
|
<input type="text" name="block_url" id="config-block"
|
||||||
<!-- DEPRECATED -->
|
placeholder="{{ translation['config-block-url-help'] }}" value="{{ config.block_url }}">
|
||||||
<!--<div class="config-div config-div-dark">-->
|
</div>
|
||||||
<!--<label for="config-dark">{{ translation['config-dark'] }}: </label>-->
|
<div class="config-div config-div-nojs">
|
||||||
<!--<input type="checkbox" name="dark" id="config-dark" {{ 'checked' if config.dark else '' }}>-->
|
<label for="config-nojs">{{ translation['config-nojs'] }}: </label>
|
||||||
<!--</div>-->
|
<input type="checkbox" name="nojs" id="config-nojs" {{ 'checked' if config.nojs else '' }}>
|
||||||
<div class="config-div config-div-safe">
|
</div>
|
||||||
<label for="config-safe">{{ translation['config-safe'] }}: </label>
|
<div class="config-div config-div-theme">
|
||||||
<input type="checkbox" name="safe" id="config-safe" {{ 'checked' if config.safe else '' }}>
|
<label for="config-theme">{{ translation['config-theme'] }}: </label>
|
||||||
</div>
|
<select name="theme" id="config-theme">
|
||||||
<div class="config-div config-div-alts">
|
{% for theme in themes %}
|
||||||
<label class="tooltip" for="config-alts">{{ translation['config-alts'] }}: </label>
|
<option value="{{ theme }}"
|
||||||
<input type="checkbox" name="alts" id="config-alts" {{ 'checked' if config.alts else '' }}>
|
{% if theme in config.theme %}
|
||||||
<div><span class="info-text"> — {{ translation['config-alts-help'] }}</span></div>
|
selected
|
||||||
</div>
|
{% endif %}>
|
||||||
<div class="config-div config-div-new-tab">
|
{{ translation[theme].capitalize() }}
|
||||||
<label for="config-new-tab">{{ translation['config-new-tab'] }}: </label>
|
</option>
|
||||||
<input type="checkbox" name="new_tab" id="config-new-tab" {{ 'checked' if config.new_tab else '' }}>
|
{% endfor %}
|
||||||
</div>
|
</select>
|
||||||
<div class="config-div config-div-view-image">
|
</div>
|
||||||
<label for="config-view-image">{{ translation['config-images'] }}: </label>
|
<!-- DEPRECATED -->
|
||||||
<input type="checkbox" name="view_image" id="config-view-image" {{ 'checked' if config.view_image else '' }}>
|
<!--<div class="config-div config-div-dark">-->
|
||||||
<div><span class="info-text"> — {{ translation['config-images-help'] }}</span></div>
|
<!--<label for="config-dark">{{ translation['config-dark'] }}: </label>-->
|
||||||
</div>
|
<!--<input type="checkbox" name="dark" id="config-dark" {{ 'checked' if config.dark else '' }}>-->
|
||||||
<div class="config-div config-div-tor">
|
<!--</div>-->
|
||||||
<label for="config-tor">{{ translation['config-tor'] }}: {{ '' if tor_available else 'Unavailable' }}</label>
|
<div class="config-div config-div-safe">
|
||||||
<input type="checkbox" name="tor" id="config-tor" {{ '' if tor_available else 'hidden' }} {{ 'checked' if config.tor else '' }}>
|
<label for="config-safe">{{ translation['config-safe'] }}: </label>
|
||||||
</div>
|
<input type="checkbox" name="safe" id="config-safe" {{ 'checked' if config.safe else '' }}>
|
||||||
<div class="config-div config-div-get-only">
|
</div>
|
||||||
<label for="config-get-only">{{ translation['config-get-only'] }}: </label>
|
<div class="config-div config-div-alts">
|
||||||
<input type="checkbox" name="get_only" id="config-get-only" {{ 'checked' if config.get_only else '' }}>
|
<label class="tooltip" for="config-alts">{{ translation['config-alts'] }}: </label>
|
||||||
</div>
|
<input type="checkbox" name="alts" id="config-alts" {{ 'checked' if config.alts else '' }}>
|
||||||
<div class="config-div config-div-get-only">
|
<div><span class="info-text"> — {{ translation['config-alts-help'] }}</span></div>
|
||||||
<label for="config-accept-language">Set Accept-Language: </label>
|
</div>
|
||||||
<input type="checkbox" name="accept_language" id="config-accept-language" {{ 'checked' if config.accept_language else '' }}>
|
<div class="config-div config-div-new-tab">
|
||||||
</div>
|
<label for="config-new-tab">{{ translation['config-new-tab'] }}: </label>
|
||||||
<div class="config-div config-div-root-url">
|
<input type="checkbox" name="new_tab"
|
||||||
<label for="config-url">{{ translation['config-url'] }}: </label>
|
id="config-new-tab" {{ 'checked' if config.new_tab else '' }}>
|
||||||
<input type="text" name="url" id="config-url" value="{{ config.url }}">
|
</div>
|
||||||
</div>
|
<div class="config-div config-div-view-image">
|
||||||
<div class="config-div config-div-custom-css">
|
<label for="config-view-image">{{ translation['config-images'] }}: </label>
|
||||||
<a id="css-link"
|
<input type="checkbox" name="view_image"
|
||||||
href="https://github.com/benbusby/whoogle-search/wiki/User-Contributed-CSS-Themes">
|
id="config-view-image" {{ 'checked' if config.view_image else '' }}>
|
||||||
{{ translation['config-css'] }}:
|
<div><span class="info-text"> — {{ translation['config-images-help'] }}</span></div>
|
||||||
</a>
|
</div>
|
||||||
<textarea
|
<div class="config-div config-div-tor">
|
||||||
name="style"
|
<label for="config-tor">{{ translation['config-tor'] }}: {{ '' if tor_available else 'Unavailable' }}</label>
|
||||||
id="config-style"
|
<input type="checkbox" name="tor"
|
||||||
autocapitalize="off"
|
id="config-tor" {{ '' if tor_available else 'hidden' }} {{ 'checked' if config.tor else '' }}>
|
||||||
autocomplete="off"
|
</div>
|
||||||
spellcheck="false"
|
<div class="config-div config-div-get-only">
|
||||||
autocorrect="off"
|
<label for="config-get-only">{{ translation['config-get-only'] }}: </label>
|
||||||
value="">
|
<input type="checkbox" name="get_only"
|
||||||
{{ config.style.replace('\t', '') }}
|
id="config-get-only" {{ 'checked' if config.get_only else '' }}>
|
||||||
</textarea>
|
</div>
|
||||||
</div>
|
<div class="config-div config-div-get-only">
|
||||||
<div class="config-div">
|
<label for="config-accept-language">Set Accept-Language: </label>
|
||||||
<input type="submit" id="config-load" value="{{ translation['load'] }}">
|
<input type="checkbox" name="accept_language"
|
||||||
<input type="submit" id="config-submit" value="{{ translation['apply'] }}">
|
id="config-accept-language" {{ 'checked' if config.accept_language else '' }}>
|
||||||
<input type="submit" id="config-save" value="{{ translation['save-as'] }}">
|
</div>
|
||||||
</div>
|
<div class="config-div config-div-root-url">
|
||||||
</form>
|
<label for="config-url">{{ translation['config-url'] }}: </label>
|
||||||
</div>
|
<input type="text" name="url" id="config-url" value="{{ config.url }}">
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
<div class="config-div config-div-custom-css">
|
||||||
</div>
|
<a id="css-link"
|
||||||
<footer>
|
href="https://github.com/benbusby/whoogle-search/wiki/User-Contributed-CSS-Themes">
|
||||||
<p class="footer">
|
{{ translation['config-css'] }}:
|
||||||
Whoogle Search v{{ version_number }} ||
|
</a>
|
||||||
<a id="gh-link" href="https://github.com/benbusby/whoogle-search">{{ translation['github-link'] }}</a>
|
<textarea
|
||||||
</p>
|
name="style"
|
||||||
</footer>
|
id="config-style"
|
||||||
</body>
|
autocapitalize="off"
|
||||||
|
autocomplete="off"
|
||||||
|
spellcheck="false"
|
||||||
|
autocorrect="off"
|
||||||
|
value="">
|
||||||
|
{{ config.style.replace('\t', '') }}
|
||||||
|
</textarea>
|
||||||
|
</div>
|
||||||
|
<div class="config-div">
|
||||||
|
<input type="submit" id="config-load" value="{{ translation['load'] }}">
|
||||||
|
<input type="submit" id="config-submit" value="{{ translation['apply'] }}">
|
||||||
|
<input type="submit" id="config-save" value="{{ translation['save-as'] }}">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
<p class="footer">
|
||||||
|
Whoogle Search v{{ version_number }} ||
|
||||||
|
<a id="gh-link" href="https://github.com/benbusby/whoogle-search">{{ translation['github-link'] }}</a>
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -55,9 +55,11 @@ def resolve_bang(query: str, bangs_dict: dict) -> str:
|
||||||
query = query.lower()
|
query = query.lower()
|
||||||
split_query = query.split(' ')
|
split_query = query.split(' ')
|
||||||
for operator in bangs_dict.keys():
|
for operator in bangs_dict.keys():
|
||||||
if operator not in split_query:
|
if operator not in split_query \
|
||||||
|
and operator[1:] + operator[0] not in split_query:
|
||||||
continue
|
continue
|
||||||
|
return bangs_dict[operator]['url'].replace(
|
||||||
return bangs_dict[operator]['url'].format(
|
'{}',
|
||||||
query.replace(operator, '').strip())
|
query.replace(operator if operator in split_query
|
||||||
|
else operator[1:] + operator[0], '').strip(), 1)
|
||||||
return ''
|
return ''
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from flask import Request
|
||||||
import hashlib
|
import hashlib
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
@ -15,3 +16,10 @@ def read_config_bool(var: str) -> bool:
|
||||||
if val.isdigit():
|
if val.isdigit():
|
||||||
return bool(int(val))
|
return bool(int(val))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def get_client_ip(r: Request) -> str:
|
||||||
|
if r.environ.get('HTTP_X_FORWARDED_FOR') is None:
|
||||||
|
return r.environ['REMOTE_ADDR']
|
||||||
|
else:
|
||||||
|
return r.environ['HTTP_X_FORWARDED_FOR']
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from bs4 import BeautifulSoup, NavigableString
|
from bs4 import BeautifulSoup, NavigableString
|
||||||
|
import html
|
||||||
import os
|
import os
|
||||||
import urllib.parse as urlparse
|
import urllib.parse as urlparse
|
||||||
from urllib.parse import parse_qs
|
from urllib.parse import parse_qs
|
||||||
|
@ -52,11 +53,15 @@ def bold_search_terms(response: str, query: str) -> BeautifulSoup:
|
||||||
if len(element) == len(target_word):
|
if len(element) == len(target_word):
|
||||||
return
|
return
|
||||||
|
|
||||||
element.replace_with(
|
if not re.match('.*[a-zA-Z0-9].*', target_word) or (
|
||||||
|
element.parent and element.parent.name == 'style'):
|
||||||
|
return
|
||||||
|
|
||||||
|
element.replace_with(BeautifulSoup(
|
||||||
re.sub(fr'\b((?![{{}}<>-]){target_word}(?![{{}}<>-]))\b',
|
re.sub(fr'\b((?![{{}}<>-]){target_word}(?![{{}}<>-]))\b',
|
||||||
r'<b>\1</b>',
|
r'<b>\1</b>',
|
||||||
element,
|
html.escape(element),
|
||||||
flags=re.I)
|
flags=re.I), 'html.parser')
|
||||||
)
|
)
|
||||||
|
|
||||||
# Split all words out of query, grouping the ones wrapped in quotes
|
# Split all words out of query, grouping the ones wrapped in quotes
|
||||||
|
@ -173,9 +178,7 @@ def append_nojs(result: BeautifulSoup) -> None:
|
||||||
"""
|
"""
|
||||||
nojs_link = BeautifulSoup(features='html.parser').new_tag('a')
|
nojs_link = BeautifulSoup(features='html.parser').new_tag('a')
|
||||||
nojs_link['href'] = '/window?location=' + result['href']
|
nojs_link['href'] = '/window?location=' + result['href']
|
||||||
nojs_link['style'] = 'display:block;width:100%;'
|
nojs_link.string = ' NoJS Link'
|
||||||
nojs_link.string = 'NoJS Link: ' + nojs_link['href']
|
|
||||||
result.append(BeautifulSoup('<br><hr><br>', 'html.parser'))
|
|
||||||
result.append(nojs_link)
|
result.append(nojs_link)
|
||||||
|
|
||||||
|
|
||||||
|
|
4
run
4
run
|
@ -12,6 +12,10 @@ SUBDIR="${1:-app}"
|
||||||
export APP_ROOT="$SCRIPT_DIR/$SUBDIR"
|
export APP_ROOT="$SCRIPT_DIR/$SUBDIR"
|
||||||
export STATIC_FOLDER="$APP_ROOT/static"
|
export STATIC_FOLDER="$APP_ROOT/static"
|
||||||
|
|
||||||
|
# Clear out build directory
|
||||||
|
rm -f "$SCRIPT_DIR"/app/static/build/*.js
|
||||||
|
rm -f "$SCRIPT_DIR"/app/static/build/*.css
|
||||||
|
|
||||||
# Check for regular vs test run
|
# Check for regular vs test run
|
||||||
if [[ "$SUBDIR" == "test" ]]; then
|
if [[ "$SUBDIR" == "test" ]]; then
|
||||||
# Set up static files for testing
|
# Set up static files for testing
|
||||||
|
|
|
@ -36,6 +36,11 @@ def test_ddg_bang(client):
|
||||||
assert rv._status_code == 302
|
assert rv._status_code == 302
|
||||||
assert rv.headers.get('Location').startswith('https://www.reddit.com')
|
assert rv.headers.get('Location').startswith('https://www.reddit.com')
|
||||||
|
|
||||||
|
# Move '!' to end of the bang
|
||||||
|
rv = client.get('/search?q=gitlab%20w!')
|
||||||
|
assert rv._status_code == 302
|
||||||
|
assert rv.headers.get('Location').startswith('https://en.wikipedia.org')
|
||||||
|
|
||||||
# Ensure bang is case insensitive
|
# Ensure bang is case insensitive
|
||||||
rv = client.get('/search?q=!GH%20whoogle')
|
rv = client.get('/search?q=!GH%20whoogle')
|
||||||
assert rv._status_code == 302
|
assert rv._status_code == 302
|
||||||
|
|
Loading…
Reference in New Issue
Block a user