prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Results from currency conversion should be rounded to 2 decimals
  At the moment, the results of a currency conversion query is reported as a float:
  
      < mynick> .cur 100 SEK to USD
      < mybot> 100.0 SEK (Swedish krona) = 12.202017114914426 USD (US dollar)
  
  As far as I know, no currency reports more than 2 decimals, and some even less (JPY comes to mind). Rounding the result to 2 decimals will result in more readable output.
  
      < mynick> .cur 100 SEK to USD
      < mybot> 100.0 SEK (Swedish krona) = 12.20 USD (US dollar)
  
  
  --- 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 re
  7 
  8 from requests import get
  9 from sopel.module import commands, example, NOLIMIT
  10 
  11 # The Canadian central bank has better exchange rate data than the Fed, the
  12 # Bank of England, or the European Central Bank. Who knew?
  13 base_url = 'http://www.bankofcanada.ca/stats/assets/rates_rss/noon/en_{}.xml'
  14 regex = re.compile(r'''
  15     (\d+(?:\.\d+)?)        # Decimal number
  16     \s*([a-zA-Z]{3})       # 3-letter currency code
  17     \s+(?:in|as|of|to)\s+  # preposition
  18     ([a-zA-Z]{3})          # 3-letter currency code
  19     ''', re.VERBOSE)
  20 
  21 
  22 def get_rate(code):
  23     code = code.upper()
  24     if code == 'CAD':
  25         return 1, 'Canadian Dollar'
  26     elif code == 'BTC':
  27         btc_rate = get('https://apiv2.bitcoinaverage.com/indices/global/ticker/BTCCAD')
  28         rates = btc_rate.json()
  29         return 1 / rates['averages']['day'], 'Bitcoin—24hr average'
  30 
  31     data = get("http://www.bankofcanada.ca/valet/observations/FX{}CAD/json".format(code))
  32     name = data.json()['seriesDetail']['FX{}CAD'.format(code)]['description']
  33     name = name.split(" to Canadian")[0]
  34     json = data.json()['observations']
  35     for element in reversed(json):
  36         if 'v' in element['FX{}CAD'.format(code)]:
  37             return 1 / float(element['FX{}CAD'.format(code)]['v']), name
  38 
  39 
  40 @commands('cur', 'currency', 'exchange')
  41 @example('.cur 20 EUR in USD')
  42 def exchange(bot, trigger):
  43     """Show the exchange rate between two currencies"""
  44     if not trigger.group(2):
  45         return bot.reply("No search term. An example: .cur 20 EUR in USD")
  46     match = regex.match(trigger.group(2))
  47     if not match:
  48         # It's apologetic, because it's using Canadian data.
  49         bot.reply("Sorry, I didn't understand the input.")
  50         return NOLIMIT
  51 
  52     amount, of, to = match.groups()
  53     try:
  54         amount = float(amount)
  55     except:
  56         bot.reply("Sorry, I didn't understand the input.")
  57     display(bot, amount, of, to)
  58 
  59 
  60 def display(bot, amount, of, to):
  61     if not amount:
  62         bot.reply("Zero is zero, no matter what country you're in.")
  63     try:
  64         of_rate, of_name = get_rate(of)
  65         if not of_name:
  66             bot.reply("Unknown currency: %s" % of)
  67             return
  68         to_rate, to_name = get_rate(to)
  69         if not to_name:
  70             bot.reply("Unknown currency: %s" % to)
  71             return
  72     except Exception:
  73         bot.reply("Something went wrong while I was getting the exchange rate.")
  74         return NOLIMIT
  75 
  76     result = amount / of_rate * to_rate
  77     bot.say("{} {} ({}) = {} {} ({})".format(amount, of.upper(), of_name,
  78                                              result, to.upper(), to_name))
  79 
  80 
  81 @commands('btc', 'bitcoin')
  82 @example('.btc 20 EUR')
  83 def bitcoin(bot, trigger):
  84     #if 2 args, 1st is number and 2nd is currency. If 1 arg, it's either the number or the currency.
  85     to = trigger.group(4)
  86     amount = trigger.group(3)
  87     if not to:
  88         to = trigger.group(3) or 'USD'
  89         amount = 1
  90 
  91     try:
  92         amount = float(amount)
  93     except:
  94         bot.reply("Sorry, I didn't understand the input.")
  95         return NOLIMIT
  96 
  97     display(bot, amount, 'BTC', to)
  98
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/sopel/modules/currency.py b/sopel/modules/currency.py
  --- a/sopel/modules/currency.py
  +++ b/sopel/modules/currency.py
  @@ -74,7 +74,7 @@
           return NOLIMIT
   
       result = amount / of_rate * to_rate
  -    bot.say("{} {} ({}) = {} {} ({})".format(amount, of.upper(), of_name,
  +    bot.say("{:.2f} {} ({}) = {:.2f} {} ({})".format(amount, of.upper(), of_name,
                                                result, to.upper(), to_name))
  
verification_info: {"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@@ -74,7 +74,7 @@\n         return NOLIMIT\n \n     result = amount / of_rate * to_rate\n-    bot.say(\"{} {} ({}) = {} {} ({})\".format(amount, of.upper(), of_name,\n+    bot.say(\"{:.2f} {} ({}) = {:.2f} {} ({})\".format(amount, of.upper(), of_name,\n                                              result, to.upper(), to_name))\n", "issue": "Results from currency conversion should be rounded to 2 decimals\nAt the moment, the results of a currency conversion query is reported as a float:\r\n\r\n    < mynick> .cur 100 SEK to USD\r\n    < mybot> 100.0 SEK (Swedish krona) = 12.202017114914426 USD (US dollar)\r\n\r\nAs far as I know, no currency reports more than 2 decimals, and some even less (JPY comes to mind). Rounding the result to 2 decimals will result in more readable output.\r\n\r\n    < mynick> .cur 100 SEK to USD\r\n    < mybot> 100.0 SEK (Swedish krona) = 12.20 USD (US dollar)\r\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 re\n\nfrom requests import get\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        btc_rate = get('https://apiv2.bitcoinaverage.com/indices/global/ticker/BTCCAD')\n        rates = btc_rate.json()\n        return 1 / rates['averages']['day'], 'Bitcoin\u201424hr average'\n\n    data = get(\"http://www.bankofcanada.ca/valet/observations/FX{}CAD/json\".format(code))\n    name = data.json()['seriesDetail']['FX{}CAD'.format(code)]['description']\n    name = name.split(\" to Canadian\")[0]\n    json = data.json()['observations']\n    for element in reversed(json):\n        if 'v' in element['FX{}CAD'.format(code)]:\n            return 1 / float(element['FX{}CAD'.format(code)]['v']), 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:\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"}], "after_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 re\n\nfrom requests import get\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        btc_rate = get('https://apiv2.bitcoinaverage.com/indices/global/ticker/BTCCAD')\n        rates = btc_rate.json()\n        return 1 / rates['averages']['day'], 'Bitcoin\u201424hr average'\n\n    data = get(\"http://www.bankofcanada.ca/valet/observations/FX{}CAD/json\".format(code))\n    name = data.json()['seriesDetail']['FX{}CAD'.format(code)]['description']\n    name = name.split(\" to Canadian\")[0]\n    json = data.json()['observations']\n    for element in reversed(json):\n        if 'v' in element['FX{}CAD'.format(code)]:\n            return 1 / float(element['FX{}CAD'.format(code)]['v']), 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:\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(\"{:.2f} {} ({}) = {:.2f} {} ({})\".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"}]}