Merge branch 'develop' into nord_dark_theme
This commit is contained in:
commit
c0bf85902f
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -9,6 +9,7 @@ test/static
|
|||
flask_session/
|
||||
app/static/config
|
||||
app/static/custom_config
|
||||
app/static/bangs
|
||||
|
||||
# pip stuff
|
||||
build/
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from app.utils.session_utils import generate_user_keys
|
||||
from app.utils.gen_ddg_bangs import gen_bangs_json
|
||||
from flask import Flask
|
||||
from flask_session import Session
|
||||
import os
|
||||
|
@ -15,6 +16,8 @@ app.config['STATIC_FOLDER'] = os.getenv('STATIC_FOLDER', os.path.join(app.config
|
|||
app.config['CONFIG_PATH'] = os.getenv('CONFIG_VOLUME', os.path.join(app.config['STATIC_FOLDER'], 'config'))
|
||||
app.config['DEFAULT_CONFIG'] = os.path.join(app.config['CONFIG_PATH'], 'config.json')
|
||||
app.config['SESSION_FILE_DIR'] = os.path.join(app.config['CONFIG_PATH'], 'session')
|
||||
app.config['BANG_PATH'] = os.getenv('CONFIG_VOLUME', os.path.join(app.config['STATIC_FOLDER'], 'bangs'))
|
||||
app.config['BANG_FILE'] = os.path.join(app.config['BANG_PATH'], 'bangs.json')
|
||||
|
||||
if not os.path.exists(app.config['CONFIG_PATH']):
|
||||
os.makedirs(app.config['CONFIG_PATH'])
|
||||
|
@ -22,6 +25,11 @@ if not os.path.exists(app.config['CONFIG_PATH']):
|
|||
if not os.path.exists(app.config['SESSION_FILE_DIR']):
|
||||
os.makedirs(app.config['SESSION_FILE_DIR'])
|
||||
|
||||
# (Re)generate DDG bang filter, and create path if it doesn't exist yet
|
||||
if not os.path.exists(app.config['BANG_PATH']):
|
||||
os.makedirs(app.config['BANG_PATH'])
|
||||
gen_bangs_json(app.config['BANG_FILE'])
|
||||
|
||||
Session(app)
|
||||
|
||||
from app import routes
|
||||
|
|
|
@ -18,6 +18,9 @@ from app.request import Request
|
|||
from app.utils.session_utils import valid_user_session
|
||||
from app.utils.routing_utils import *
|
||||
|
||||
# Load DDG bang json files only on init
|
||||
bang_json = json.load(open(app.config['BANG_FILE']))
|
||||
|
||||
|
||||
def auth_required(f):
|
||||
@wraps(f)
|
||||
|
@ -127,6 +130,10 @@ def opensearch():
|
|||
def autocomplete():
|
||||
q = g.request_params.get('q')
|
||||
|
||||
# Search bangs if the query begins with "!", but not "! " (feeling lucky)
|
||||
if q.startswith('!') and len(q) > 1 and not q.startswith('! '):
|
||||
return jsonify([q, [bang_json[_]['suggestion'] for _ in bang_json if _.startswith(q)]])
|
||||
|
||||
if not q and not request.data:
|
||||
return jsonify({'?': []})
|
||||
elif request.data:
|
||||
|
@ -144,6 +151,10 @@ def search():
|
|||
search_util = RoutingUtils(request, g.user_config, session, cookies_disabled=g.cookies_disabled)
|
||||
query = search_util.new_search_query()
|
||||
|
||||
resolved_bangs = search_util.bang_operator(bang_json)
|
||||
if resolved_bangs != '':
|
||||
return redirect(resolved_bangs)
|
||||
|
||||
# Redirect to home if invalid/blank search
|
||||
if not query:
|
||||
return redirect('/')
|
||||
|
|
|
@ -93,8 +93,14 @@ const autocomplete = (searchInput, autocompleteResults) => {
|
|||
removeActive(suggestion);
|
||||
suggestion[currentFocus].classList.add("autocomplete-active");
|
||||
|
||||
// Autofill search bar with suggestion content
|
||||
searchBar.value = suggestion[currentFocus].textContent;
|
||||
// Autofill search bar with suggestion content (minus the "bang name" if using a bang operator)
|
||||
let searchContent = suggestion[currentFocus].textContent;
|
||||
if (searchContent.indexOf('(') > 0) {
|
||||
searchBar.value = searchContent.substring(0, searchContent.indexOf('('));
|
||||
} else {
|
||||
searchBar.value = searchContent;
|
||||
}
|
||||
|
||||
searchBar.focus();
|
||||
};
|
||||
|
||||
|
|
26
app/utils/gen_ddg_bangs.py
Normal file
26
app/utils/gen_ddg_bangs.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
import json
|
||||
import requests
|
||||
|
||||
|
||||
def gen_bangs_json(bangs_file):
|
||||
# Request list
|
||||
try:
|
||||
r = requests.get('https://duckduckgo.com/bang.v255.js')
|
||||
r.raise_for_status()
|
||||
except requests.exceptions.HTTPError as err:
|
||||
raise SystemExit(err)
|
||||
|
||||
# Convert to json
|
||||
data = json.loads(r.text)
|
||||
|
||||
# Set up a json object (with better formatting) for all available bangs
|
||||
bangs_data = {}
|
||||
|
||||
for row in data:
|
||||
bang_command = '!' + row['t']
|
||||
bangs_data[bang_command] = {
|
||||
'url': row['u'].replace('{{{s}}}', '{}'),
|
||||
'suggestion': bang_command + ' (' + row['s'] + ')'
|
||||
}
|
||||
|
||||
json.dump(bangs_data, open(bangs_file, 'w'))
|
|
@ -55,6 +55,12 @@ class RoutingUtils:
|
|||
self.query = q[2:] if self.feeling_lucky else q
|
||||
return self.query
|
||||
|
||||
def bang_operator(self, bangs_dict: dict) -> str:
|
||||
for operator in bangs_dict.keys():
|
||||
if self.query.split(' ')[0] == operator:
|
||||
return bangs_dict[operator]['url'].format(self.query.replace(operator, '').strip())
|
||||
return ''
|
||||
|
||||
def generate_response(self) -> Tuple[Any, int]:
|
||||
mobile = 'Android' in self.user_agent or 'iPhone' in self.user_agent
|
||||
|
||||
|
|
|
@ -27,6 +27,16 @@ def test_feeling_lucky(client):
|
|||
assert rv._status_code == 303
|
||||
|
||||
|
||||
def test_ddg_bang(client):
|
||||
rv = client.get('/search?q=!gh%20whoogle')
|
||||
assert rv._status_code == 302
|
||||
assert rv.headers.get('Location').startswith('https://github.com')
|
||||
|
||||
rv = client.get('/search?q=!w%20github')
|
||||
assert rv._status_code == 302
|
||||
assert rv.headers.get('Location').startswith('https://en.wikipedia.org')
|
||||
|
||||
|
||||
def test_config(client):
|
||||
rv = client.post('/config', data=demo_config)
|
||||
assert rv._status_code == 302
|
||||
|
|
Loading…
Reference in New Issue
Block a user