prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Double bangs without query redirect to search URL instead of main page
  <!-- PLEASE FILL THESE FIELDS, IT REALLY HELPS THE MAINTAINERS OF SearXNG -->
  
  **Version of SearXNG, commit number if you are using on master branch and stipulate if you forked SearXNG**
  `2023.4.21+98387e29`
  
  **How did you install SearXNG?**
  `searxng-docker`
  
  **What happened?**
  Double bangs without query redirect to search URL instead of main page
  
  **How To Reproduce**
  Use external bang without any search items e.g. `!!gh` and we land on `https://github.com/search?utf8=%E2%9C%93&q=` 
  
  **Expected behavior**
  `!!gh` should redirect to `https://github.com` if no search items
  
  </issue>
  <code>
  [start of searx/external_bang.py]
  1 # SPDX-License-Identifier: AGPL-3.0-or-later
  2 
  3 from urllib.parse import quote_plus
  4 from searx.data import EXTERNAL_BANGS
  5 
  6 LEAF_KEY = chr(16)
  7 
  8 
  9 def get_node(external_bangs_db, bang):
  10     node = external_bangs_db['trie']
  11     after = ''
  12     before = ''
  13     for bang_letter in bang:
  14         after += bang_letter
  15         if after in node and isinstance(node, dict):
  16             node = node[after]
  17             before += after
  18             after = ''
  19     return node, before, after
  20 
  21 
  22 def get_bang_definition_and_ac(external_bangs_db, bang):
  23     node, before, after = get_node(external_bangs_db, bang)
  24 
  25     bang_definition = None
  26     bang_ac_list = []
  27     if after != '':
  28         for k in node:
  29             if k.startswith(after):
  30                 bang_ac_list.append(before + k)
  31     elif isinstance(node, dict):
  32         bang_definition = node.get(LEAF_KEY)
  33         bang_ac_list = [before + k for k in node.keys() if k != LEAF_KEY]
  34     elif isinstance(node, str):
  35         bang_definition = node
  36         bang_ac_list = []
  37 
  38     return bang_definition, bang_ac_list
  39 
  40 
  41 def resolve_bang_definition(bang_definition, query):
  42     url, rank = bang_definition.split(chr(1))
  43     url = url.replace(chr(2), quote_plus(query))
  44     if url.startswith('//'):
  45         url = 'https:' + url
  46     rank = int(rank) if len(rank) > 0 else 0
  47     return (url, rank)
  48 
  49 
  50 def get_bang_definition_and_autocomplete(bang, external_bangs_db=None):
  51     if external_bangs_db is None:
  52         external_bangs_db = EXTERNAL_BANGS
  53 
  54     bang_definition, bang_ac_list = get_bang_definition_and_ac(external_bangs_db, bang)
  55 
  56     new_autocomplete = []
  57     current = [*bang_ac_list]
  58     done = set()
  59     while len(current) > 0:
  60         bang_ac = current.pop(0)
  61         done.add(bang_ac)
  62 
  63         current_bang_definition, current_bang_ac_list = get_bang_definition_and_ac(external_bangs_db, bang_ac)
  64         if current_bang_definition:
  65             _, order = resolve_bang_definition(current_bang_definition, '')
  66             new_autocomplete.append((bang_ac, order))
  67         for new_bang in current_bang_ac_list:
  68             if new_bang not in done and new_bang not in current:
  69                 current.append(new_bang)
  70 
  71     new_autocomplete.sort(key=lambda t: (-t[1], t[0]))
  72     new_autocomplete = list(map(lambda t: t[0], new_autocomplete))
  73 
  74     return bang_definition, new_autocomplete
  75 
  76 
  77 def get_bang_url(search_query, external_bangs_db=None):
  78     """
  79     Redirects if the user supplied a correct bang search.
  80     :param search_query: This is a search_query object which contains preferences and the submitted queries.
  81     :return: None if the bang was invalid, else a string of the redirect url.
  82     """
  83     ret_val = None
  84 
  85     if external_bangs_db is None:
  86         external_bangs_db = EXTERNAL_BANGS
  87 
  88     if search_query.external_bang:
  89         bang_definition, _ = get_bang_definition_and_ac(external_bangs_db, search_query.external_bang)
  90         if bang_definition and isinstance(bang_definition, str):
  91             ret_val = resolve_bang_definition(bang_definition, search_query.query)[0]
  92 
  93     return ret_val
  94
  [end of searx/external_bang.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/searx/external_bang.py b/searx/external_bang.py
  --- a/searx/external_bang.py
  +++ b/searx/external_bang.py
  @@ -1,6 +1,6 @@
   # SPDX-License-Identifier: AGPL-3.0-or-later
   
  -from urllib.parse import quote_plus
  +from urllib.parse import quote_plus, urlparse
   from searx.data import EXTERNAL_BANGS
   
   LEAF_KEY = chr(16)
  @@ -40,9 +40,15 @@
   
   def resolve_bang_definition(bang_definition, query):
       url, rank = bang_definition.split(chr(1))
  -    url = url.replace(chr(2), quote_plus(query))
       if url.startswith('//'):
           url = 'https:' + url
  +    if query:
  +        url = url.replace(chr(2), quote_plus(query))
  +    else:
  +        # go to main instead of search page
  +        o = urlparse(url)
  +        url = o.scheme + '://' + o.netloc
  +
       rank = int(rank) if len(rank) > 0 else 0
       return (url, rank)
  
verification_info: {"golden_diff": "diff --git a/searx/external_bang.py b/searx/external_bang.py\n--- a/searx/external_bang.py\n+++ b/searx/external_bang.py\n@@ -1,6 +1,6 @@\n # SPDX-License-Identifier: AGPL-3.0-or-later\n \n-from urllib.parse import quote_plus\n+from urllib.parse import quote_plus, urlparse\n from searx.data import EXTERNAL_BANGS\n \n LEAF_KEY = chr(16)\n@@ -40,9 +40,15 @@\n \n def resolve_bang_definition(bang_definition, query):\n     url, rank = bang_definition.split(chr(1))\n-    url = url.replace(chr(2), quote_plus(query))\n     if url.startswith('//'):\n         url = 'https:' + url\n+    if query:\n+        url = url.replace(chr(2), quote_plus(query))\n+    else:\n+        # go to main instead of search page\n+        o = urlparse(url)\n+        url = o.scheme + '://' + o.netloc\n+\n     rank = int(rank) if len(rank) > 0 else 0\n     return (url, rank)\n", "issue": "Double bangs without query redirect to search URL instead of main page\n<!-- PLEASE FILL THESE FIELDS, IT REALLY HELPS THE MAINTAINERS OF SearXNG -->\r\n\r\n**Version of SearXNG, commit number if you are using on master branch and stipulate if you forked SearXNG**\r\n`2023.4.21+98387e29`\r\n\r\n**How did you install SearXNG?**\r\n`searxng-docker`\r\n\r\n**What happened?**\r\nDouble bangs without query redirect to search URL instead of main page\r\n\r\n**How To Reproduce**\r\nUse external bang without any search items e.g. `!!gh` and we land on `https://github.com/search?utf8=%E2%9C%93&q=` \r\n\r\n**Expected behavior**\r\n`!!gh` should redirect to `https://github.com` if no search items\n", "before_files": [{"content": "# SPDX-License-Identifier: AGPL-3.0-or-later\n\nfrom urllib.parse import quote_plus\nfrom searx.data import EXTERNAL_BANGS\n\nLEAF_KEY = chr(16)\n\n\ndef get_node(external_bangs_db, bang):\n    node = external_bangs_db['trie']\n    after = ''\n    before = ''\n    for bang_letter in bang:\n        after += bang_letter\n        if after in node and isinstance(node, dict):\n            node = node[after]\n            before += after\n            after = ''\n    return node, before, after\n\n\ndef get_bang_definition_and_ac(external_bangs_db, bang):\n    node, before, after = get_node(external_bangs_db, bang)\n\n    bang_definition = None\n    bang_ac_list = []\n    if after != '':\n        for k in node:\n            if k.startswith(after):\n                bang_ac_list.append(before + k)\n    elif isinstance(node, dict):\n        bang_definition = node.get(LEAF_KEY)\n        bang_ac_list = [before + k for k in node.keys() if k != LEAF_KEY]\n    elif isinstance(node, str):\n        bang_definition = node\n        bang_ac_list = []\n\n    return bang_definition, bang_ac_list\n\n\ndef resolve_bang_definition(bang_definition, query):\n    url, rank = bang_definition.split(chr(1))\n    url = url.replace(chr(2), quote_plus(query))\n    if url.startswith('//'):\n        url = 'https:' + url\n    rank = int(rank) if len(rank) > 0 else 0\n    return (url, rank)\n\n\ndef get_bang_definition_and_autocomplete(bang, external_bangs_db=None):\n    if external_bangs_db is None:\n        external_bangs_db = EXTERNAL_BANGS\n\n    bang_definition, bang_ac_list = get_bang_definition_and_ac(external_bangs_db, bang)\n\n    new_autocomplete = []\n    current = [*bang_ac_list]\n    done = set()\n    while len(current) > 0:\n        bang_ac = current.pop(0)\n        done.add(bang_ac)\n\n        current_bang_definition, current_bang_ac_list = get_bang_definition_and_ac(external_bangs_db, bang_ac)\n        if current_bang_definition:\n            _, order = resolve_bang_definition(current_bang_definition, '')\n            new_autocomplete.append((bang_ac, order))\n        for new_bang in current_bang_ac_list:\n            if new_bang not in done and new_bang not in current:\n                current.append(new_bang)\n\n    new_autocomplete.sort(key=lambda t: (-t[1], t[0]))\n    new_autocomplete = list(map(lambda t: t[0], new_autocomplete))\n\n    return bang_definition, new_autocomplete\n\n\ndef get_bang_url(search_query, external_bangs_db=None):\n    \"\"\"\n    Redirects if the user supplied a correct bang search.\n    :param search_query: This is a search_query object which contains preferences and the submitted queries.\n    :return: None if the bang was invalid, else a string of the redirect url.\n    \"\"\"\n    ret_val = None\n\n    if external_bangs_db is None:\n        external_bangs_db = EXTERNAL_BANGS\n\n    if search_query.external_bang:\n        bang_definition, _ = get_bang_definition_and_ac(external_bangs_db, search_query.external_bang)\n        if bang_definition and isinstance(bang_definition, str):\n            ret_val = resolve_bang_definition(bang_definition, search_query.query)[0]\n\n    return ret_val\n", "path": "searx/external_bang.py"}]}