We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[currency] Currency codes are needlessly case-sensitive
There's no reason that the currency codes should have to be capitalized by the user, is there?

```
[06:03:23] <dgw> ;cur 1 USD in CAD
[06:03:24] <Sopel> 1.0 USD (U.S. dollar) = 1.386001386 CAD (Canadian Dollar)
[06:03:28] <dgw> ;cur 1 usd in CAD
[06:03:29] <Sopel> 1.0 usd (U.S. dollar) = 1.386001386 CAD (Canadian Dollar)
[06:03:33] <dgw> ;cur 1 USD in cad
[06:03:34] <Sopel> dgw: Unknown currency: cad
```

If no reason not to do so arises, I'll PR this one myself in a couple days.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `sopel/modules/currency.py`
Content:
```
1 # coding=utf-8
2 # Copyright 2013 Elsie Powell, embolalia.com
3 # Licensed under the Eiffel Forum License 2
4 from __future__ import unicode_literals, absolute_import, print_function, division
5 
6 import json
7 import xmltodict
8 import re
9 
10 from sopel import web
11 from sopel.module import commands, example, NOLIMIT
12 
13 # The Canadian central bank has better exchange rate data than the Fed, the
14 # Bank of England, or the European Central Bank. Who knew?
15 base_url = 'http://www.bankofcanada.ca/stats/assets/rates_rss/noon/en_{}.xml'
16 regex = re.compile(r'''
17     (\d+(?:\.\d+)?)        # Decimal number
18     \s*([a-zA-Z]{3})       # 3-letter currency code
19     \s+(?:in|as|of|to)\s+  # preposition
20     ([a-zA-Z]{3})          # 3-letter currency code
21     ''', re.VERBOSE)
22 
23 
24 def get_rate(code):
25     if code.upper() == 'CAD':
26         return 1, 'Canadian Dollar'
27     elif code.upper() == 'BTC':
28         rates = json.loads(web.get('https://api.bitcoinaverage.com/ticker/all'))
29         return 1 / rates['CAD']['24h_avg'], 'Bitcoin—24hr average'
30 
31     data, headers = web.get(base_url.format(code), dont_decode=True, return_headers=True)
32     if headers['_http_status'] == 404:
33         return False, False
34     namespaces = {
35         'http://www.cbwiki.net/wiki/index.php/Specification_1.1': 'cb',
36         'http://purl.org/rss/1.0/': None,
37         'http://www.w3.org/1999/02/22-rdf-syntax-ns#': 'rdf' }
38     xml = xmltodict.parse(data, process_namespaces=True, namespaces=namespaces).get('rdf:RDF')
39     namestring = xml.get('channel').get('title').get('#text')
40     name = namestring[len('Bank of Canada noon rate: '):]
41     name = re.sub(r'\s*\(noon\)\s*', '', name)
42     rate = xml.get('item').get('cb:statistics').get('cb:exchangeRate').get('cb:value').get('#text')
43     return float(rate), name
44 
45 
46 @commands('cur', 'currency', 'exchange')
47 @example('.cur 20 EUR in USD')
48 def exchange(bot, trigger):
49     """Show the exchange rate between two currencies"""
50     if not trigger.group(2):
51         return bot.reply("No search term. An example: .cur 20 EUR in USD")
52     match = regex.match(trigger.group(2))
53     if not match:
54         # It's apologetic, because it's using Canadian data.
55         bot.reply("Sorry, I didn't understand the input.")
56         return NOLIMIT
57 
58     amount, of, to = match.groups()
59     try:
60         amount = float(amount)
61     except:
62         bot.reply("Sorry, I didn't understand the input.")
63     display(bot, amount, of, to)
64 
65 
66 def display(bot, amount, of, to):
67     if not amount:
68         bot.reply("Zero is zero, no matter what country you're in.")
69     try:
70         of_rate, of_name = get_rate(of)
71         if not of_name:
72             bot.reply("Unknown currency: %s" % of)
73             return
74         to_rate, to_name = get_rate(to)
75         if not to_name:
76             bot.reply("Unknown currency: %s" % to)
77             return
78     except Exception as e:
79         raise
80         bot.reply("Something went wrong while I was getting the exchange rate.")
81         return NOLIMIT
82 
83     result = amount / of_rate * to_rate
84     bot.say("{} {} ({}) = {} {} ({})".format(amount, of, of_name,
85                                              result, to, to_name))
86 
87 
88 @commands('btc', 'bitcoin')
89 @example('.btc 20 EUR')
90 def bitcoin(bot, trigger):
91     #if 2 args, 1st is number and 2nd is currency. If 1 arg, it's either the number or the currency.
92     to = trigger.group(4)
93     amount = trigger.group(3)
94     if not to:
95         to = trigger.group(3) or 'USD'
96         amount = 1
97 
98     try:
99         amount = float(amount)
100     except:
101         bot.reply("Sorry, I didn't understand the input.")
102         return NOLIMIT
103 
104     display(bot, amount, 'BTC', to)
105
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/sopel/modules/currency.py b/sopel/modules/currency.py
--- a/sopel/modules/currency.py
+++ b/sopel/modules/currency.py
@@ -26,9 +26,10 @@
 
 
 def get_rate(code):
-    if code.upper() == 'CAD':
+    code = code.upper()
+    if code == 'CAD':
         return 1, 'Canadian Dollar'
-    elif code.upper() == 'BTC':
+    elif code == 'BTC':
         rates = json.loads(web.get('https://api.bitcoinaverage.com/ticker/all'))
         return 1 / rates['CAD']['24h_avg'], 'Bitcoin—24hr average'
 
@@ -85,8 +86,8 @@
         return NOLIMIT
 
     result = amount / of_rate * to_rate
-    bot.say("{} {} ({}) = {} {} ({})".format(amount, of, of_name,
-                                             result, to, to_name))
+    bot.say("{} {} ({}) = {} {} ({})".format(amount, of.upper(), of_name,
+                                             result, to.upper(), to_name))
 
 
 @commands('btc', 'bitcoin')

{"golden_diff": "diff --git a/sopel/modules/currency.py b/sopel/modules/currency.py\n--- a/sopel/modules/currency.py\n+++ b/sopel/modules/currency.py\n@@ -26,9 +26,10 @@\n \n \n def get_rate(code):\n-    if code.upper() == 'CAD':\n+    code = code.upper()\n+    if code == 'CAD':\n         return 1, 'Canadian Dollar'\n-    elif code.upper() == 'BTC':\n+    elif code == 'BTC':\n         rates = json.loads(web.get('https://api.bitcoinaverage.com/ticker/all'))\n         return 1 / rates['CAD']['24h_avg'], 'Bitcoin\u201424hr average'\n \n@@ -85,8 +86,8 @@\n         return NOLIMIT\n \n     result = amount / of_rate * to_rate\n-    bot.say(\"{} {} ({}) = {} {} ({})\".format(amount, of, of_name,\n-                                             result, to, to_name))\n+    bot.say(\"{} {} ({}) = {} {} ({})\".format(amount, of.upper(), of_name,\n+                                             result, to.upper(), to_name))\n \n \n @commands('btc', 'bitcoin')\n", "issue": "[currency] Currency codes are needlessly case-sensitive\nThere's no reason that the currency codes should have to be capitalized by the user, is there?\n\n```\n[06:03:23] <dgw> ;cur 1 USD in CAD\n[06:03:24] <Sopel> 1.0 USD (U.S. dollar) = 1.386001386 CAD (Canadian Dollar)\n[06:03:28] <dgw> ;cur 1 usd in CAD\n[06:03:29] <Sopel> 1.0 usd (U.S. dollar) = 1.386001386 CAD (Canadian Dollar)\n[06:03:33] <dgw> ;cur 1 USD in cad\n[06:03:34] <Sopel> dgw: Unknown currency: cad\n```\n\nIf no reason not to do so arises, I'll PR this one myself in a couple days.\n\n", "before_files": [{"content": "# coding=utf-8\n# Copyright 2013 Elsie Powell, embolalia.com\n# Licensed under the Eiffel Forum License 2\nfrom __future__ import unicode_literals, absolute_import, print_function, division\n\nimport json\nimport xmltodict\nimport re\n\nfrom sopel import web\nfrom sopel.module import commands, example, NOLIMIT\n\n# The Canadian central bank has better exchange rate data than the Fed, the\n# Bank of England, or the European Central Bank. Who knew?\nbase_url = 'http://www.bankofcanada.ca/stats/assets/rates_rss/noon/en_{}.xml'\nregex = re.compile(r'''\n    (\\d+(?:\\.\\d+)?)        # Decimal number\n    \\s*([a-zA-Z]{3})       # 3-letter currency code\n    \\s+(?:in|as|of|to)\\s+  # preposition\n    ([a-zA-Z]{3})          # 3-letter currency code\n    ''', re.VERBOSE)\n\n\ndef get_rate(code):\n    if code.upper() == 'CAD':\n        return 1, 'Canadian Dollar'\n    elif code.upper() == 'BTC':\n        rates = json.loads(web.get('https://api.bitcoinaverage.com/ticker/all'))\n        return 1 / rates['CAD']['24h_avg'], 'Bitcoin\u201424hr average'\n\n    data, headers = web.get(base_url.format(code), dont_decode=True, return_headers=True)\n    if headers['_http_status'] == 404:\n        return False, False\n    namespaces = {\n        'http://www.cbwiki.net/wiki/index.php/Specification_1.1': 'cb',\n        'http://purl.org/rss/1.0/': None,\n        'http://www.w3.org/1999/02/22-rdf-syntax-ns#': 'rdf' }\n    xml = xmltodict.parse(data, process_namespaces=True, namespaces=namespaces).get('rdf:RDF')\n    namestring = xml.get('channel').get('title').get('#text')\n    name = namestring[len('Bank of Canada noon rate: '):]\n    name = re.sub(r'\\s*\\(noon\\)\\s*', '', name)\n    rate = xml.get('item').get('cb:statistics').get('cb:exchangeRate').get('cb:value').get('#text')\n    return float(rate), name\n\n\n@commands('cur', 'currency', 'exchange')\n@example('.cur 20 EUR in USD')\ndef exchange(bot, trigger):\n    \"\"\"Show the exchange rate between two currencies\"\"\"\n    if not trigger.group(2):\n        return bot.reply(\"No search term. An example: .cur 20 EUR in USD\")\n    match = regex.match(trigger.group(2))\n    if not match:\n        # It's apologetic, because it's using Canadian data.\n        bot.reply(\"Sorry, I didn't understand the input.\")\n        return NOLIMIT\n\n    amount, of, to = match.groups()\n    try:\n        amount = float(amount)\n    except:\n        bot.reply(\"Sorry, I didn't understand the input.\")\n    display(bot, amount, of, to)\n\n\ndef display(bot, amount, of, to):\n    if not amount:\n        bot.reply(\"Zero is zero, no matter what country you're in.\")\n    try:\n        of_rate, of_name = get_rate(of)\n        if not of_name:\n            bot.reply(\"Unknown currency: %s\" % of)\n            return\n        to_rate, to_name = get_rate(to)\n        if not to_name:\n            bot.reply(\"Unknown currency: %s\" % to)\n            return\n    except Exception as e:\n        raise\n        bot.reply(\"Something went wrong while I was getting the exchange rate.\")\n        return NOLIMIT\n\n    result = amount / of_rate * to_rate\n    bot.say(\"{} {} ({}) = {} {} ({})\".format(amount, of, of_name,\n                                             result, to, to_name))\n\n\n@commands('btc', 'bitcoin')\n@example('.btc 20 EUR')\ndef bitcoin(bot, trigger):\n    #if 2 args, 1st is number and 2nd is currency. If 1 arg, it's either the number or the currency.\n    to = trigger.group(4)\n    amount = trigger.group(3)\n    if not to:\n        to = trigger.group(3) or 'USD'\n        amount = 1\n\n    try:\n        amount = float(amount)\n    except:\n        bot.reply(\"Sorry, I didn't understand the input.\")\n        return NOLIMIT\n\n    display(bot, amount, 'BTC', to)\n", "path": "sopel/modules/currency.py"}], "after_files": [{"content": "# coding=utf-8\n\"\"\"currency.py - Sopel Exchange Rate Module\nCopyright 2013 Edward Powell, embolalia.com\nLicensed under the Eiffel Forum License 2\n\nhttp://sopel.chat\n\"\"\"\nfrom __future__ import unicode_literals, absolute_import, print_function, division\n\nimport json\nimport xmltodict\nimport re\n\nfrom sopel import web\nfrom sopel.module import commands, example, NOLIMIT\n\n# The Canadian central bank has better exchange rate data than the Fed, the\n# Bank of England, or the European Central Bank. Who knew?\nbase_url = 'http://www.bankofcanada.ca/stats/assets/rates_rss/noon/en_{}.xml'\nregex = re.compile(r'''\n    (\\d+(?:\\.\\d+)?)        # Decimal number\n    \\s*([a-zA-Z]{3})       # 3-letter currency code\n    \\s+(?:in|as|of|to)\\s+  # preposition\n    ([a-zA-Z]{3})          # 3-letter currency code\n    ''', re.VERBOSE)\n\n\ndef get_rate(code):\n    code = code.upper()\n    if code == 'CAD':\n        return 1, 'Canadian Dollar'\n    elif code == 'BTC':\n        rates = json.loads(web.get('https://api.bitcoinaverage.com/ticker/all'))\n        return 1 / rates['CAD']['24h_avg'], 'Bitcoin\u201424hr average'\n\n    data, headers = web.get(base_url.format(code), dont_decode=True, return_headers=True)\n    if headers['_http_status'] == 404:\n        return False, False\n    namespaces = {\n        'http://www.cbwiki.net/wiki/index.php/Specification_1.1': 'cb', \n        'http://purl.org/rss/1.0/': None, \n        'http://www.w3.org/1999/02/22-rdf-syntax-ns#': 'rdf' }\n    xml = xmltodict.parse(data, process_namespaces=True, namespaces=namespaces).get('rdf:RDF')\n    namestring = xml.get('channel').get('title').get('#text')\n    name = namestring[len('Bank of Canada noon rate: '):]\n    name = re.sub(r'\\s*\\(noon\\)\\s*', '', name)\n    rate = xml.get('item').get('cb:statistics').get('cb:exchangeRate').get('cb:value').get('#text')\n    return float(rate), name\n\n\n@commands('cur', 'currency', 'exchange')\n@example('.cur 20 EUR in USD')\ndef exchange(bot, trigger):\n    \"\"\"Show the exchange rate between two currencies\"\"\"\n    if not trigger.group(2):\n        return bot.reply(\"No search term. An example: .cur 20 EUR in USD\")\n    match = regex.match(trigger.group(2))\n    if not match:\n        # It's apologetic, because it's using Canadian data.\n        bot.reply(\"Sorry, I didn't understand the input.\")\n        return NOLIMIT\n\n    amount, of, to = match.groups()\n    try:\n        amount = float(amount)\n    except:\n        bot.reply(\"Sorry, I didn't understand the input.\")\n    display(bot, amount, of, to)\n\n\ndef display(bot, amount, of, to):\n    if not amount:\n        bot.reply(\"Zero is zero, no matter what country you're in.\")\n    try:\n        of_rate, of_name = get_rate(of)\n        if not of_name:\n            bot.reply(\"Unknown currency: %s\" % of)\n            return\n        to_rate, to_name = get_rate(to)\n        if not to_name:\n            bot.reply(\"Unknown currency: %s\" % to)\n            return\n    except Exception as e:\n        raise\n        bot.reply(\"Something went wrong while I was getting the exchange rate.\")\n        return NOLIMIT\n\n    result = amount / of_rate * to_rate\n    bot.say(\"{} {} ({}) = {} {} ({})\".format(amount, of.upper(), of_name,\n                                             result, to.upper(), to_name))\n\n\n@commands('btc', 'bitcoin')\n@example('.btc 20 EUR')\ndef bitcoin(bot, trigger):\n    #if 2 args, 1st is number and 2nd is currency. If 1 arg, it's either the number or the currency.\n    to = trigger.group(4)\n    amount = trigger.group(3)\n    if not to:\n        to = trigger.group(3) or 'USD'\n        amount = 1\n\n    try:\n        amount = float(amount)\n    except:\n        bot.reply(\"Sorry, I didn't understand the input.\")\n        return NOLIMIT\n\n    display(bot, amount, 'BTC', to)\n", "path": "sopel/modules/currency.py"}]}