示例#1
0
 def __init__(self, *args, **kwargs):
     super(HyacinthBot, self).__init__(*args, **kwargs)
     self.password = config.password
     self.karma_store = KarmaStore(config.karma_db_path)
     self.eightball = EightBall(config.eightball_answers_path)
     self.markov = Markov(config.markov_db_path)
     self.roller = Roller()
     self.rate_limiter = RateLimiter()
     self.greeter = Greeter(config.greetings_path,
                            config.greeting_probability)
示例#2
0
def test_user_1_rps():
    class TestTimer(MockTimer):

        def get_times(self) -> []:
            return [0, 500, 1000, 1001, 1500, 2000, 2001]

    rate_limiter = RateLimiter(TestTimer(), bucket_ms_interval=1)
    rate_limiter.configure_limit(user_id=1, rps=1)

    assert rate_limiter.process_request(1)  # for 0 stamp
    assert not rate_limiter.process_request(1)  # 500
    assert rate_limiter.process_request(1)  # 1000 this will pass because exactly 1 sec passed since last request
    assert not rate_limiter.process_request(1)  # 1001
    assert not rate_limiter.process_request(1)  # 1500
    assert rate_limiter.process_request(1)  # 2000 this will pass because exactly 1 sec passed since last request
    assert not rate_limiter.process_request(1)  # 2001
示例#3
0
 def __init__(self, *args, **kwargs):
     super(HyacinthBot, self).__init__(*args, **kwargs)
     self.password = config.password
     self.karma_store = KarmaStore(config.karma_db_path)
     self.eightball = EightBall(config.eightball_answers_path)
     self.markov = Markov(config.markov_db_path)
     self.roller = Roller()
     self.rate_limiter = RateLimiter()
     self.greeter = Greeter(config.greetings_path, config.greeting_probability)
示例#4
0
def test_global_2_rps():
    class TestTimer(MockTimer):

        def get_times(self) -> []:
            return [0, 500, 1000, 1001, 1500, 2000, 2001]

    rate_limiter = RateLimiter(TestTimer(), bucket_ms_interval=1)
    rate_limiter.configure_global_limit(rps=2)

    assert rate_limiter.process_request(1)  # for 0 stamp
    assert rate_limiter.process_request(2)  # 500
    assert rate_limiter.process_request(3)  # 1000 this will pass because exactly 1 sec passed since request at 0
    # and only one request in between
    assert not rate_limiter.process_request(4)  # 1001
    assert rate_limiter.process_request(5)  # 1500 this will pass because exactly 1 sec passed since request at 500
    # and only one request in between
    assert rate_limiter.process_request(6)  # 2000
    assert not rate_limiter.process_request(7)  # 2001
示例#5
0
def test_two_users_1_rps():
    class TestTimer(MockTimer):

        def get_times(self) -> []:
            return [0, 500, 1000, 1001, 1500, 2000, 2001]

    rate_limiter = RateLimiter(TestTimer(), bucket_ms_interval=1)
    rate_limiter.configure_limit(user_id=1, rps=1)
    rate_limiter.configure_limit(user_id=2, rps=1)

    # because users have their own limits configured, they do not overlap
    assert rate_limiter.process_request(1)  # for 0 stamp and user 1
    assert rate_limiter.process_request(2)  # 500 it's OK, because it is user 2
    assert rate_limiter.process_request(1)  # 1000 will pass because exactly 1 sec passed since last request for user 1
    assert not rate_limiter.process_request(1)  # 1001 user 1 just made a request, won't pass
    assert rate_limiter.process_request(2)  # 1500 will pass because exactly 1 sec passed since last request for user 2
    assert not rate_limiter.process_request(2)  # 2000 user 2 made a request 500ms ago
    assert rate_limiter.process_request(1)  # 2001 will pass because more than 1 sec passed since a request for user 1
示例#6
0
def test_user_1_rps_and_unlimited_for_others():
    class TestTimer(MockTimer):

        def get_times(self) -> []:
            return [0, 500, 550, 700, 800, 900, 999, 1001]

    rate_limiter = RateLimiter(TestTimer(), bucket_ms_interval=1)
    rate_limiter.configure_limit(user_id=1, rps=1)

    # user 1 has only limits configured, but others are unlimited
    assert rate_limiter.process_request(1)  # for 0 stamp and user 1
    assert rate_limiter.process_request(2)  # 500 other users unlimited
    assert rate_limiter.process_request(3)  # 550
    assert rate_limiter.process_request(4)  # 700
    assert rate_limiter.process_request(5)  # 800
    assert rate_limiter.process_request(6)  # 900
    assert not rate_limiter.process_request(1)  # 999 but user 1 already exceeded a limit
    assert rate_limiter.process_request(1)  # 1001 user 1 may execute, because 1 sec already passed since last success
示例#7
0
class HyacinthBot(BaseIRCBot):
    """An IRC bot."""

    nickname = config.nickname

    def __init__(self, *args, **kwargs):
        super(HyacinthBot, self).__init__(*args, **kwargs)
        self.password = config.password
        self.karma_store = KarmaStore(config.karma_db_path)
        self.eightball = EightBall(config.eightball_answers_path)
        self.markov = Markov(config.markov_db_path)
        self.roller = Roller()
        self.rate_limiter = RateLimiter()
        self.greeter = Greeter(config.greetings_path, config.greeting_probability)

    # callbacks for events
    def privmsg(self, user, channel, msg):
        """This will get called when the bot receives a message."""
        user = user.split('!', 1)[0]
        self.logger.log(msg)

        # Check to see if they're sending me a private message
        if channel == self.nickname:
            msg = "It isn't nice to whisper!  Play nice with the group."
            self.msg(user, msg)
            return

        # Otherwise check to see if it is a message directed at me
        if msg.startswith(self.nickname + ":"):
            msg = "%s: I am the little death that brings total oblivion" % user
            self.msg(channel, msg)
            self.logger.log("<%s> %s" % (self.nickname, msg))
            return

        self.process_message(user, channel, msg)

    def userJoined(self, user, channel):
        """Greets a user when they join the channel"""
        greeting = self.greeter.greet(user)
        if greeting:
            self.msg(channel, greeting)

    def process_message(self, user, channel, msg):
        self.record_karmas(user, channel, msg)
        self.markov.add_single_line(msg)

        if msg.startswith('!'):
            self.rate_limiter.add_request(user)
            if not self.rate_limiter.is_rate_limited(user):
                self.process_command(user, channel, msg)
        else:
            self.send_markov_sentence(user, channel, msg)

    def process_command(self, user, channel, msg):
        """Decide how to act upon a message, given that it is a command
        i.e. begins with !, e.g. "!karma"
        """

        if msg.startswith('!karma'):
            self.process_karmastring(user, channel, msg)
        elif msg.startswith('!8ball') and msg.endswith('?'):
            self.msg(channel, self.eightball.get_answer())
        elif msg.startswith('!markov'):
            new_msg = msg.replace('!markov', '')
            self.send_markov_sentence(user, channel, new_msg, force=True)
        elif msg.startswith('!roll'):
            new_msg = msg.replace('!roll', '')
            self.roll(user, channel, new_msg)
        elif msg.startswith('!commands'):
            self.msg(channel, '!karma, !8ball, !markov, !roll, !commands')

    def send_markov_sentence(self, user, channel, message, force=False):
        thing_to_say = self.markov.generate_sentence(message)
        if force or (len(thing_to_say.split()) > 5 and random.random() > 0.995):
            self.msg(channel, thing_to_say)

    def process_karmastring(self, user, channel, msg):
        # this is a stupid hack, since if there's not a space at the end
        # it fails strangely
        msg = msg + ' '
        split_msg = msg.split(' ')
        if len(split_msg) == 1:
            requested_user = user
        else:
            requested_user = split_msg[1]
        msg = self.karma_store.get_karma(requested_user)
        self.msg(channel, str(msg))

    def record_karmas(self, user, channel, msg):
        karmas = karma_regex.findall(msg)

        if not karmas:
            return

        for karma in karmas:
            recipient = karma[:-2] # on account of the ++ or --
            if recipient == user:
                self.msg(channel, 'no altering your own karma, %s' % user)
                continue
            if karma.endswith('--'):
                self.karma_store.record_karma(recipient, up=False)
            else:
                self.karma_store.record_karma(recipient, up=True)

    def roll(self, user, channel, msg):
        die, value = self.roller.roll(msg)
        roll_string = '%s rolls %s... %d' % (user, die, value)
        self.msg(channel, roll_string)
示例#8
0
class HyacinthBot(BaseIRCBot):
    """An IRC bot."""

    nickname = config.nickname

    def __init__(self, *args, **kwargs):
        super(HyacinthBot, self).__init__(*args, **kwargs)
        self.password = config.password
        self.karma_store = KarmaStore(config.karma_db_path)
        self.eightball = EightBall(config.eightball_answers_path)
        self.markov = Markov(config.markov_db_path)
        self.roller = Roller()
        self.rate_limiter = RateLimiter()
        self.greeter = Greeter(config.greetings_path,
                               config.greeting_probability)

    # callbacks for events
    def privmsg(self, user, channel, msg):
        """This will get called when the bot receives a message."""
        user = user.split('!', 1)[0]
        self.logger.log(msg)

        # Check to see if they're sending me a private message
        if channel == self.nickname:
            msg = "It isn't nice to whisper!  Play nice with the group."
            self.msg(user, msg)
            return

        # Otherwise check to see if it is a message directed at me
        if msg.startswith(self.nickname + ":"):
            msg = "%s: I am the little death that brings total oblivion" % user
            self.msg(channel, msg)
            self.logger.log("<%s> %s" % (self.nickname, msg))
            return

        self.process_message(user, channel, msg)

    def userJoined(self, user, channel):
        """Greets a user when they join the channel"""
        greeting = self.greeter.greet(user)
        if greeting:
            self.msg(channel, greeting)

    def process_message(self, user, channel, msg):
        self.record_karmas(user, channel, msg)
        self.markov.add_single_line(msg)

        if msg.startswith('!'):
            self.rate_limiter.add_request(user)
            if not self.rate_limiter.is_rate_limited(user):
                self.process_command(user, channel, msg)
        else:
            self.send_markov_sentence(user, channel, msg)

    def process_command(self, user, channel, msg):
        """Decide how to act upon a message, given that it is a command
        i.e. begins with !, e.g. "!karma"
        """

        if msg.startswith('!karma'):
            self.process_karmastring(user, channel, msg)
        elif msg.startswith('!8ball') and msg.endswith('?'):
            self.msg(channel, self.eightball.get_answer())
        elif msg.startswith('!markov'):
            new_msg = msg.replace('!markov', '')
            self.send_markov_sentence(user, channel, new_msg, force=True)
        elif msg.startswith('!roll'):
            new_msg = msg.replace('!roll', '')
            self.roll(user, channel, new_msg)
        elif msg.startswith('!commands'):
            self.msg(channel, '!karma, !8ball, !markov, !roll, !commands')

    def send_markov_sentence(self, user, channel, message, force=False):
        thing_to_say = self.markov.generate_sentence(message)
        if force or (len(thing_to_say.split()) > 5
                     and random.random() > 0.995):
            self.msg(channel, thing_to_say)

    def process_karmastring(self, user, channel, msg):
        # this is a stupid hack, since if there's not a space at the end
        # it fails strangely
        msg = msg + ' '
        split_msg = msg.split(' ')
        if len(split_msg) == 1:
            requested_user = user
        else:
            requested_user = split_msg[1]
        msg = self.karma_store.get_karma(requested_user)
        self.msg(channel, str(msg))

    def record_karmas(self, user, channel, msg):
        karmas = karma_regex.findall(msg)

        if not karmas:
            return

        for karma in karmas:
            recipient = karma[:-2]  # on account of the ++ or --
            if recipient == user:
                self.msg(channel, 'no altering your own karma, %s' % user)
                continue
            if karma.endswith('--'):
                self.karma_store.record_karma(recipient, up=False)
            else:
                self.karma_store.record_karma(recipient, up=True)

    def roll(self, user, channel, msg):
        die, value = self.roller.roll(msg)
        roll_string = '%s rolls %s... %d' % (user, die, value)
        self.msg(channel, roll_string)
示例#9
0
def test_10_ms_bucket_interval():
    """
    This test expresses a drawback of designed rate limiter. Even the time interval between two requests with timestamps
    1007, 2001 is shorter than 1 second it still passes. The reason is we split our tracker by 10 milliseconds interval,
    so once we performed a bucketing the distance between two buckets would be exactly 1 second which is valid.
    The case won't pass for 1ms buckets. So, with 10ms wide bucket we produce 100 buckets, we may incorrectly identify
    a distance for half timestamps within a bucket, that gives us 0.5% error. With 1ms the error is 0.05%. We may
    decrease the error by decreasing the width of the bucket, but this will impact space and time complexity. Overall
    the number of buckets will not exceed 1000/[bucket width], so we can talk about effective constant time and space
    complexity.
    :return:
    """

    class TestTimer(MockTimer):

        def get_times(self) -> []:
            return [1007, 2001]

    rate_limiter = RateLimiter(TestTimer(), bucket_ms_interval=10)
    rate_limiter.configure_limit(user_id=1, rps=1)

    assert rate_limiter.process_request(1)  # for 1007 stamp
    assert rate_limiter.process_request(1)  # 2001

    rate_limiter = RateLimiter(TestTimer(), bucket_ms_interval=1)
    rate_limiter.configure_limit(user_id=1, rps=1)

    assert rate_limiter.process_request(1)  # for 1007 stamp
    assert not rate_limiter.process_request(1)  # 2001
示例#10
0
def test_user_contributes_to_global_limit():
    class TestTimer(MockTimer):

        def get_times(self) -> []:
            return [0, 500, 1000, 1001, 1500, 2000, 2001]

    rate_limiter = RateLimiter(TestTimer(), bucket_ms_interval=1)
    rate_limiter.configure_limit(user_id=1, rps=1)
    rate_limiter.configure_global_limit(rps=1)

    # user 1 makes 1 request for in 1 second interval and contribute to global limit, second user can't execute during
    # the same interval
    assert rate_limiter.process_request(1)  # for 0 stamp user 1
    assert not rate_limiter.process_request(2)  # 500 since user 1 already made a request
    assert rate_limiter.process_request(1)  # 1000 this will pass because exactly 1 sec passed since last request
    assert not rate_limiter.process_request(2)  # 1001 since user 1 just made a request
    assert not rate_limiter.process_request(1)  # 1500
    assert rate_limiter.process_request(1)  # 2000 this will pass because exactly 1 sec passed since last request
    assert not rate_limiter.process_request(2)  # 2001