prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  contrib_bots: Prevent runaway bots with rate limiting
  It's possible to create a runaway bot if you have a bot send a message to certain stimuli that in turns becomes a stimulus for the bot to send another message and so on and so forth.
  
  We can prevent that in `contrib_bots/run.py` by adding some logic to prevent runaway bots.
  
  Right now RestrictedClient make self.send_message just be client.send_message, but we can instead have a wrapper like this:
  
  ```
  def send_message(self, *args, **kwargs):
       self.rate_limit()
       self.client.send_message(*args, **kwargs)
  ```
  
  And then have appropriate logic in `rate_limit()` and some state variable to make sure less than N messages have been sent in the last second.  It might make sense to have a small class for rate limiting that RestrictedClient uses an instance of.
  
  You can do a pretty naive rate limiting scheme where you just keep the last N timestamps in a Python list and truncate it off the front as new sends come in and the list grows to size N+1.
  
  </issue>
  <code>
  [start of contrib_bots/run.py]
  1 #!/usr/bin/env python
  2 from __future__ import print_function
  3 
  4 import importlib
  5 import logging
  6 import optparse
  7 import os
  8 import sys
  9 
  10 our_dir = os.path.dirname(os.path.abspath(__file__))
  11 
  12 # For dev setups, we can find the API in the repo itself.
  13 if os.path.exists(os.path.join(our_dir, '../api/zulip')):
  14     sys.path.insert(0, '../api')
  15 
  16 from zulip import Client
  17 
  18 class RestrictedClient(object):
  19     def __init__(self, client):
  20         # Only expose a subset of our Client's functionality
  21         user_profile = client.get_profile()
  22         self.send_message = client.send_message
  23         try:
  24             self.full_name = user_profile['full_name']
  25             self.email = user_profile['email']
  26         except KeyError:
  27             logging.error('Cannot fetch user profile, make sure you have set'
  28                           ' up the zuliprc file correctly.')
  29             sys.exit(1)
  30 
  31 def get_lib_module(lib_fn):
  32     lib_fn = os.path.abspath(lib_fn)
  33     if not os.path.dirname(lib_fn).startswith(os.path.join(our_dir, 'lib')):
  34         print('Sorry, we will only import code from contrib_bots/lib.')
  35         sys.exit(1)
  36 
  37     if not lib_fn.endswith('.py'):
  38         print('Please use a .py extension for library files.')
  39         sys.exit(1)
  40 
  41     sys.path.append('lib')
  42     base_lib_fn = os.path.basename(os.path.splitext(lib_fn)[0])
  43     module_name = 'lib.' + base_lib_fn
  44     module = importlib.import_module(module_name)
  45     return module
  46 
  47 def run_message_handler_for_bot(lib_module, quiet, config_file):
  48     # Make sure you set up your ~/.zuliprc
  49     client = Client(config_file=config_file)
  50     restricted_client = RestrictedClient(client)
  51 
  52     message_handler = lib_module.handler_class()
  53 
  54     class StateHandler(object):
  55         def __init__(self):
  56             self.state = None
  57 
  58         def set_state(self, state):
  59             self.state = state
  60 
  61         def get_state(self):
  62             return self.state
  63 
  64     state_handler = StateHandler()
  65 
  66     if not quiet:
  67         print(message_handler.usage())
  68 
  69     def handle_message(message):
  70         logging.info('waiting for next message')
  71         if message_handler.triage_message(message=message,
  72                                           client=restricted_client):
  73             message_handler.handle_message(
  74                 message=message,
  75                 client=restricted_client,
  76                 state_handler=state_handler
  77                 )
  78 
  79     logging.info('starting message handling...')
  80     client.call_on_each_message(handle_message)
  81 
  82 def run():
  83     usage = '''
  84         ./run.py <lib file>
  85 
  86         Example: ./run.py lib/followup.py
  87 
  88         (This program loads bot-related code from the
  89         library code and then runs a message loop,
  90         feeding messages to the library code to handle.)
  91 
  92         Please make sure you have a current ~/.zuliprc
  93         file with the credentials you want to use for
  94         this bot.
  95 
  96         See lib/readme.md for more context.
  97         '''
  98 
  99     parser = optparse.OptionParser(usage=usage)
  100     parser.add_option('--quiet', '-q',
  101                       action='store_true',
  102                       help='Turn off logging output.')
  103     parser.add_option('--config-file',
  104                       action='store',
  105                       help='(alternate config file to ~/.zuliprc)')
  106     (options, args) = parser.parse_args()
  107 
  108     if len(args) == 0:
  109         print('You must specify a library!')
  110         sys.exit(1)
  111 
  112     lib_module = get_lib_module(lib_fn=args[0])
  113 
  114     if not options.quiet:
  115         logging.basicConfig(stream=sys.stdout, level=logging.INFO)
  116 
  117     run_message_handler_for_bot(
  118         lib_module=lib_module,
  119         config_file=options.config_file,
  120         quiet=options.quiet
  121     )
  122 
  123 if __name__ == '__main__':
  124     run()
  125
  [end of contrib_bots/run.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/contrib_bots/run.py b/contrib_bots/run.py
  --- a/contrib_bots/run.py
  +++ b/contrib_bots/run.py
  @@ -6,6 +6,7 @@
   import optparse
   import os
   import sys
  +import time
   
   our_dir = os.path.dirname(os.path.abspath(__file__))
   
  @@ -15,11 +16,27 @@
   
   from zulip import Client
   
  +class RateLimit(object):
  +    def __init__(self, message_limit, interval_limit):
  +        self.message_limit = message_limit
  +        self.interval_limit = interval_limit
  +        self.message_list = []
  +
  +    def is_legal(self):
  +        self.message_list.append(time.time())
  +        if len(self.message_list) > self.message_limit:
  +            self.message_list.pop(0)
  +            time_diff = self.message_list[-1] - self.message_list[0]
  +            return time_diff >= self.interval_limit
  +        else:
  +            return True
  +
   class RestrictedClient(object):
       def __init__(self, client):
           # Only expose a subset of our Client's functionality
           user_profile = client.get_profile()
  -        self.send_message = client.send_message
  +        self.rate_limit = RateLimit(20, 5)
  +        self.client = client
           try:
               self.full_name = user_profile['full_name']
               self.email = user_profile['email']
  @@ -28,6 +45,15 @@
                             ' up the zuliprc file correctly.')
               sys.exit(1)
   
  +    def send_message(self, *args, **kwargs):
  +        if self.rate_limit.is_legal():
  +            self.client.send_message(*args, **kwargs)
  +        else:
  +            logging.error('-----> !*!*!*MESSAGE RATE LIMIT REACHED, EXITING*!*!*! <-----\n'
  +                  'Is your bot trapped in an infinite loop by reacting to'
  +                  ' its own messages?')
  +            sys.exit(1)
  +
   def get_lib_module(lib_fn):
       lib_fn = os.path.abspath(lib_fn)
       if not os.path.dirname(lib_fn).startswith(os.path.join(our_dir, 'lib')):
  
verification_info: {"golden_diff": "diff --git a/contrib_bots/run.py b/contrib_bots/run.py\n--- a/contrib_bots/run.py\n+++ b/contrib_bots/run.py\n@@ -6,6 +6,7 @@\n import optparse\n import os\n import sys\n+import time\n \n our_dir = os.path.dirname(os.path.abspath(__file__))\n \n@@ -15,11 +16,27 @@\n \n from zulip import Client\n \n+class RateLimit(object):\n+    def __init__(self, message_limit, interval_limit):\n+        self.message_limit = message_limit\n+        self.interval_limit = interval_limit\n+        self.message_list = []\n+\n+    def is_legal(self):\n+        self.message_list.append(time.time())\n+        if len(self.message_list) > self.message_limit:\n+            self.message_list.pop(0)\n+            time_diff = self.message_list[-1] - self.message_list[0]\n+            return time_diff >= self.interval_limit\n+        else:\n+            return True\n+\n class RestrictedClient(object):\n     def __init__(self, client):\n         # Only expose a subset of our Client's functionality\n         user_profile = client.get_profile()\n-        self.send_message = client.send_message\n+        self.rate_limit = RateLimit(20, 5)\n+        self.client = client\n         try:\n             self.full_name = user_profile['full_name']\n             self.email = user_profile['email']\n@@ -28,6 +45,15 @@\n                           ' up the zuliprc file correctly.')\n             sys.exit(1)\n \n+    def send_message(self, *args, **kwargs):\n+        if self.rate_limit.is_legal():\n+            self.client.send_message(*args, **kwargs)\n+        else:\n+            logging.error('-----> !*!*!*MESSAGE RATE LIMIT REACHED, EXITING*!*!*! <-----\\n'\n+                  'Is your bot trapped in an infinite loop by reacting to'\n+                  ' its own messages?')\n+            sys.exit(1)\n+\n def get_lib_module(lib_fn):\n     lib_fn = os.path.abspath(lib_fn)\n     if not os.path.dirname(lib_fn).startswith(os.path.join(our_dir, 'lib')):\n", "issue": "contrib_bots: Prevent runaway bots with rate limiting\nIt's possible to create a runaway bot if you have a bot send a message to certain stimuli that in turns becomes a stimulus for the bot to send another message and so on and so forth.\r\n\r\nWe can prevent that in `contrib_bots/run.py` by adding some logic to prevent runaway bots.\r\n\r\nRight now RestrictedClient make self.send_message just be client.send_message, but we can instead have a wrapper like this:\r\n\r\n```\r\ndef send_message(self, *args, **kwargs):\r\n     self.rate_limit()\r\n     self.client.send_message(*args, **kwargs)\r\n```\r\n\r\nAnd then have appropriate logic in `rate_limit()` and some state variable to make sure less than N messages have been sent in the last second.  It might make sense to have a small class for rate limiting that RestrictedClient uses an instance of.\r\n\r\nYou can do a pretty naive rate limiting scheme where you just keep the last N timestamps in a Python list and truncate it off the front as new sends come in and the list grows to size N+1.\n", "before_files": [{"content": "#!/usr/bin/env python\nfrom __future__ import print_function\n\nimport importlib\nimport logging\nimport optparse\nimport os\nimport sys\n\nour_dir = os.path.dirname(os.path.abspath(__file__))\n\n# For dev setups, we can find the API in the repo itself.\nif os.path.exists(os.path.join(our_dir, '../api/zulip')):\n    sys.path.insert(0, '../api')\n\nfrom zulip import Client\n\nclass RestrictedClient(object):\n    def __init__(self, client):\n        # Only expose a subset of our Client's functionality\n        user_profile = client.get_profile()\n        self.send_message = client.send_message\n        try:\n            self.full_name = user_profile['full_name']\n            self.email = user_profile['email']\n        except KeyError:\n            logging.error('Cannot fetch user profile, make sure you have set'\n                          ' up the zuliprc file correctly.')\n            sys.exit(1)\n\ndef get_lib_module(lib_fn):\n    lib_fn = os.path.abspath(lib_fn)\n    if not os.path.dirname(lib_fn).startswith(os.path.join(our_dir, 'lib')):\n        print('Sorry, we will only import code from contrib_bots/lib.')\n        sys.exit(1)\n\n    if not lib_fn.endswith('.py'):\n        print('Please use a .py extension for library files.')\n        sys.exit(1)\n\n    sys.path.append('lib')\n    base_lib_fn = os.path.basename(os.path.splitext(lib_fn)[0])\n    module_name = 'lib.' + base_lib_fn\n    module = importlib.import_module(module_name)\n    return module\n\ndef run_message_handler_for_bot(lib_module, quiet, config_file):\n    # Make sure you set up your ~/.zuliprc\n    client = Client(config_file=config_file)\n    restricted_client = RestrictedClient(client)\n\n    message_handler = lib_module.handler_class()\n\n    class StateHandler(object):\n        def __init__(self):\n            self.state = None\n\n        def set_state(self, state):\n            self.state = state\n\n        def get_state(self):\n            return self.state\n\n    state_handler = StateHandler()\n\n    if not quiet:\n        print(message_handler.usage())\n\n    def handle_message(message):\n        logging.info('waiting for next message')\n        if message_handler.triage_message(message=message,\n                                          client=restricted_client):\n            message_handler.handle_message(\n                message=message,\n                client=restricted_client,\n                state_handler=state_handler\n                )\n\n    logging.info('starting message handling...')\n    client.call_on_each_message(handle_message)\n\ndef run():\n    usage = '''\n        ./run.py <lib file>\n\n        Example: ./run.py lib/followup.py\n\n        (This program loads bot-related code from the\n        library code and then runs a message loop,\n        feeding messages to the library code to handle.)\n\n        Please make sure you have a current ~/.zuliprc\n        file with the credentials you want to use for\n        this bot.\n\n        See lib/readme.md for more context.\n        '''\n\n    parser = optparse.OptionParser(usage=usage)\n    parser.add_option('--quiet', '-q',\n                      action='store_true',\n                      help='Turn off logging output.')\n    parser.add_option('--config-file',\n                      action='store',\n                      help='(alternate config file to ~/.zuliprc)')\n    (options, args) = parser.parse_args()\n\n    if len(args) == 0:\n        print('You must specify a library!')\n        sys.exit(1)\n\n    lib_module = get_lib_module(lib_fn=args[0])\n\n    if not options.quiet:\n        logging.basicConfig(stream=sys.stdout, level=logging.INFO)\n\n    run_message_handler_for_bot(\n        lib_module=lib_module,\n        config_file=options.config_file,\n        quiet=options.quiet\n    )\n\nif __name__ == '__main__':\n    run()\n", "path": "contrib_bots/run.py"}]}