<commit_before>"""
Tests for when endpoints are called with unexpected header data.
"""

from datetime import datetime, timedelta

import pytest
import requests
from freezegun import freeze_time
from requests import codes

from mock_vws._constants import ResultCodes
from tests.mock_vws.utils import (
    TargetAPIEndpoint,
    VuforiaDatabaseKeys,
    assert_vws_failure,
    assert_vws_response,
    authorization_header,
    rfc_1123_date,
)

# @pytest.mark.usefixtures('verify_mock_vuforia')
# class TestHeaders:
#     """
#     Tests for what happens when the headers are not as expected.
#     """
#
#     def test_empty(self, endpoint: TargetAPIEndpoint) -> None:
#         """
#         When no headers are given, an `UNAUTHORIZED` response is returned.
#         """
#         endpoint.prepared_request.prepare_headers(headers={})
#         session = requests.Session()
#         response = session.send(  # type: ignore
#             request=endpoint.prepared_request,
#         )
#         assert_vws_failure(
#             response=response,
#             status_code=codes.UNAUTHORIZED,
#             result_code=ResultCodes.AUTHENTICATION_FAILURE,
#         )
#
#
# @pytest.mark.usefixtures('verify_mock_vuforia')
# class TestAuthorizationHeader:
#     """
#     Tests for what happens when the `Authorization` header isn't as expected.
#     """
#
#     def test_missing(self, endpoint: TargetAPIEndpoint) -> None:
#         """
#         An `UNAUTHORIZED` response is returned when no `Authorization` header
#         is given.
#         """
#         date = rfc_1123_date()
#
#         headers = {
#             **endpoint.prepared_request.headers,
#             'Date': date,
#         }
#
#         headers.pop('Authorization', None)
#
#         endpoint.prepared_request.prepare_headers(headers=headers)
#         session = requests.Session()
#         response = session.send(  # type: ignore
#             request=endpoint.prepared_request,
#         )
#
#         assert_vws_failure(
#             response=response,
#             status_code=codes.UNAUTHORIZED,
#             result_code=ResultCodes.AUTHENTICATION_FAILURE,
#         )
#
#     def test_incorrect(self, endpoint: TargetAPIEndpoint) -> None:
#         """
#         If an incorrect `Authorization` header is given, a `BAD_REQUEST`
#         response is given.
#         """
#         date = rfc_1123_date()
#
#         headers = {
#             **endpoint.prepared_request.headers,
#             'Authorization': 'gibberish',
#             'Date': date,
#         }
#
#         endpoint.prepared_request.prepare_headers(headers=headers)
#         session = requests.Session()
#         response = session.send(  # type: ignore
#             request=endpoint.prepared_request,
#         )
#
#         assert_vws_failure(
#             response=response,
#             status_code=codes.BAD_REQUEST,
#             result_code=ResultCodes.FAIL,
#         )
#
#
@pytest.mark.usefixtures('verify_mock_vuforia')
class TestDateHeader:
    """
    Tests for what happens when the `Date` header isn't as expected.
    """

    # def test_no_date_header(
    #     self,
    #     vuforia_database_keys: VuforiaDatabaseKeys,
    #     endpoint: TargetAPIEndpoint,
    # ) -> None:
    #     """
    #     A `BAD_REQUEST` response is returned when no `Date` header is given.
    #     """
    #     content_type = str(endpoint.prepared_request.headers.get(
    #         key='Content-Type',
    #         default='',
    #     ))
    #
    #     content = endpoint.prepared_request.body or b''
    #
    #     authorization_string = authorization_header(
    #         access_key=vuforia_database_keys.server_access_key,
    #         secret_key=vuforia_database_keys.server_secret_key,
    #         method=endpoint.prepared_request.method,
    #         content=content,
    #         content_type=content_type,
    #         date='',
    #         request_path=endpoint.prepared_request.path_url,
    #     )
    #
    #     headers = {
    #         **endpoint.prepared_request.headers,
    #         'Authorization': authorization_string,
    #     }
    #
    #     headers.pop('Date', None)
    #
    #     endpoint.prepared_request.prepare_headers(headers=headers)
    #     session = requests.Session()
    #     response = session.send(  # type: ignore
    #         request=endpoint.prepared_request,
    #     )
    #
    #     assert_vws_failure(
    #         response=response,
    #         status_code=codes.BAD_REQUEST,
    #         result_code=ResultCodes.FAIL,
    #     )
    #
    def test_incorrect_date_format(
        self,
        vuforia_database_keys: VuforiaDatabaseKeys,
        endpoint: TargetAPIEndpoint,
    ) -> None:
        """
        A `BAD_REQUEST` response is returned when the date given in the date
        header is not in the expected format (RFC 1123).
        """
        with freeze_time(datetime.now()):
            now = datetime.now()
            date_incorrect_format = now.strftime('%a %b %d %H:%M:%S %Y')

        endpoint_headers = dict(endpoint.prepared_request.headers)
        content_type = endpoint_headers.get('Content-Type', '')
        assert isinstance(content_type, str)
        content = endpoint.prepared_request.body or b''
        assert isinstance(content, bytes)

        authorization_string = authorization_header(
            access_key=vuforia_database_keys.server_access_key,
            secret_key=vuforia_database_keys.server_secret_key,
            method=str(endpoint.prepared_request.method),
            content=content,
            content_type=content_type,
            date=date_incorrect_format,
            request_path=endpoint.prepared_request.path_url,
        )

        headers = {
            **endpoint_headers,
            'Authorization': authorization_string,
            'Date': date_incorrect_format,
        }

        endpoint.prepared_request.prepare_headers(  # type: ignore
            headers=headers,
        )
        session = requests.Session()
        response = session.send(  # type: ignore
            request=endpoint.prepared_request,
        )

        assert_vws_failure(
            response=response,
            status_code=codes.BAD_REQUEST,
            result_code=ResultCodes.FAIL,
        )

    @pytest.mark.parametrize(
        'time_multiplier',
        [1, -1],
        ids=(['After', 'Before']),
    )
    def test_date_out_of_range(
        self,
        vuforia_database_keys: VuforiaDatabaseKeys,
        time_multiplier: int,
        endpoint: TargetAPIEndpoint,
    ) -> None:
        """
        If the date header is more than five minutes before or after the
        request is sent, a `FORBIDDEN` response is returned.

        Because there is a small delay in sending requests and Vuforia isn't
        consistent, some leeway is given.
        """
        time_difference_from_now = timedelta(minutes=5, seconds=10)
        time_difference_from_now *= time_multiplier
        with freeze_time(datetime.now() + time_difference_from_now):
            date = rfc_1123_date()

        endpoint_headers = dict(endpoint.prepared_request.headers)
        content_type = endpoint_headers.get('Content-Type', '')
        assert isinstance(content_type, str)
        content = endpoint.prepared_request.body or b''
        assert isinstance(content, bytes)

        authorization_string = authorization_header(
            access_key=vuforia_database_keys.server_access_key,
            secret_key=vuforia_database_keys.server_secret_key,
            method=str(endpoint.prepared_request.method),
            content=content,
            content_type=content_type,
            date=date,
            request_path=endpoint.prepared_request.path_url,
        )

        headers = {
            **endpoint_headers,
            'Authorization': authorization_string,
            'Date': date,
        }

        endpoint.prepared_request.prepare_headers(  # type: ignore
            headers=headers,
        )
        session = requests.Session()
        response = session.send(  # type: ignore
            request=endpoint.prepared_request,
        )

        assert_vws_failure(
            response=response,
            status_code=codes.FORBIDDEN,
            result_code=ResultCodes.REQUEST_TIME_TOO_SKEWED,
        )

    @pytest.mark.parametrize(
        'time_multiplier',
        [1, -1],
        ids=(['After', 'Before']),
    )
    def test_date_in_range(
        self,
        vuforia_database_keys: VuforiaDatabaseKeys,
        time_multiplier: int,
        endpoint: TargetAPIEndpoint,
    ) -> None:
        """
        If a date header is within five minutes before or after the request
        is sent, no error is returned.

        Because there is a small delay in sending requests and Vuforia isn't
        consistent, some leeway is given.
        """
        time_difference_from_now = timedelta(minutes=4, seconds=50)
        time_difference_from_now *= time_multiplier
        with freeze_time(datetime.now() + time_difference_from_now):
            date = rfc_1123_date()
        time_difference_from_now *= time_multiplier
        with freeze_time(datetime.now() + time_difference_from_now):
            date = rfc_1123_date()

        content_type = endpoint.prepared_request.headers.get(
            key='Content-Type',
            default='',
        )
        content = endpoint.prepared_request.body or b''

        authorization_string = authorization_header(
            access_key=vuforia_database_keys.server_access_key,
            secret_key=vuforia_database_keys.server_secret_key,
            method=endpoint.prepared_request.method,
            content=content,
            content_type=content_type,
            date=date,
            request_path=endpoint.prepared_request.path_url,
        )

        headers = {
            **endpoint.prepared_request.headers,
            'Authorization': authorization_string,
            'Date': date,
        }

        endpoint.prepared_request.prepare_headers(headers=headers)
        session = requests.Session()
        response = session.send(  # type: ignore
            request=endpoint.prepared_request,
        )

        assert_vws_response(
            response=response,
            status_code=endpoint.successful_headers_status_code,
            result_code=endpoint.successful_headers_result_code,
        )
<commit_msg>Fix some mypy<commit_after>"""
Tests for when endpoints are called with unexpected header data.
"""

from datetime import datetime, timedelta

import pytest
import requests
from freezegun import freeze_time
from requests import codes

from mock_vws._constants import ResultCodes
from tests.mock_vws.utils import (
    TargetAPIEndpoint,
    VuforiaDatabaseKeys,
    assert_vws_failure,
    assert_vws_response,
    authorization_header,
    rfc_1123_date,
)

# @pytest.mark.usefixtures('verify_mock_vuforia')
# class TestHeaders:
#     """
#     Tests for what happens when the headers are not as expected.
#     """
#
#     def test_empty(self, endpoint: TargetAPIEndpoint) -> None:
#         """
#         When no headers are given, an `UNAUTHORIZED` response is returned.
#         """
#         endpoint.prepared_request.prepare_headers(headers={})
#         session = requests.Session()
#         response = session.send(  # type: ignore
#             request=endpoint.prepared_request,
#         )
#         assert_vws_failure(
#             response=response,
#             status_code=codes.UNAUTHORIZED,
#             result_code=ResultCodes.AUTHENTICATION_FAILURE,
#         )
#
#
# @pytest.mark.usefixtures('verify_mock_vuforia')
# class TestAuthorizationHeader:
#     """
#     Tests for what happens when the `Authorization` header isn't as expected.
#     """
#
#     def test_missing(self, endpoint: TargetAPIEndpoint) -> None:
#         """
#         An `UNAUTHORIZED` response is returned when no `Authorization` header
#         is given.
#         """
#         date = rfc_1123_date()
#
#         headers = {
#             **endpoint.prepared_request.headers,
#             'Date': date,
#         }
#
#         headers.pop('Authorization', None)
#
#         endpoint.prepared_request.prepare_headers(headers=headers)
#         session = requests.Session()
#         response = session.send(  # type: ignore
#             request=endpoint.prepared_request,
#         )
#
#         assert_vws_failure(
#             response=response,
#             status_code=codes.UNAUTHORIZED,
#             result_code=ResultCodes.AUTHENTICATION_FAILURE,
#         )
#
#     def test_incorrect(self, endpoint: TargetAPIEndpoint) -> None:
#         """
#         If an incorrect `Authorization` header is given, a `BAD_REQUEST`
#         response is given.
#         """
#         date = rfc_1123_date()
#
#         headers = {
#             **endpoint.prepared_request.headers,
#             'Authorization': 'gibberish',
#             'Date': date,
#         }
#
#         endpoint.prepared_request.prepare_headers(headers=headers)
#         session = requests.Session()
#         response = session.send(  # type: ignore
#             request=endpoint.prepared_request,
#         )
#
#         assert_vws_failure(
#             response=response,
#             status_code=codes.BAD_REQUEST,
#             result_code=ResultCodes.FAIL,
#         )
#
#
@pytest.mark.usefixtures('verify_mock_vuforia')
class TestDateHeader:
    """
    Tests for what happens when the `Date` header isn't as expected.
    """

    # def test_no_date_header(
    #     self,
    #     vuforia_database_keys: VuforiaDatabaseKeys,
    #     endpoint: TargetAPIEndpoint,
    # ) -> None:
    #     """
    #     A `BAD_REQUEST` response is returned when no `Date` header is given.
    #     """
    #     content_type = str(endpoint.prepared_request.headers.get(
    #         key='Content-Type',
    #         default='',
    #     ))
    #
    #     content = endpoint.prepared_request.body or b''
    #
    #     authorization_string = authorization_header(
    #         access_key=vuforia_database_keys.server_access_key,
    #         secret_key=vuforia_database_keys.server_secret_key,
    #         method=endpoint.prepared_request.method,
    #         content=content,
    #         content_type=content_type,
    #         date='',
    #         request_path=endpoint.prepared_request.path_url,
    #     )
    #
    #     headers = {
    #         **endpoint.prepared_request.headers,
    #         'Authorization': authorization_string,
    #     }
    #
    #     headers.pop('Date', None)
    #
    #     endpoint.prepared_request.prepare_headers(headers=headers)
    #     session = requests.Session()
    #     response = session.send(  # type: ignore
    #         request=endpoint.prepared_request,
    #     )
    #
    #     assert_vws_failure(
    #         response=response,
    #         status_code=codes.BAD_REQUEST,
    #         result_code=ResultCodes.FAIL,
    #     )
    #
    def test_incorrect_date_format(
        self,
        vuforia_database_keys: VuforiaDatabaseKeys,
        endpoint: TargetAPIEndpoint,
    ) -> None:
        """
        A `BAD_REQUEST` response is returned when the date given in the date
        header is not in the expected format (RFC 1123).
        """
        with freeze_time(datetime.now()):
            now = datetime.now()
            date_incorrect_format = now.strftime('%a %b %d %H:%M:%S %Y')

        endpoint_headers = dict(endpoint.prepared_request.headers)
        content_type = endpoint_headers.get('Content-Type', '')
        assert isinstance(content_type, str)
        content = endpoint.prepared_request.body or b''
        assert isinstance(content, bytes)

        authorization_string = authorization_header(
            access_key=vuforia_database_keys.server_access_key,
            secret_key=vuforia_database_keys.server_secret_key,
            method=str(endpoint.prepared_request.method),
            content=content,
            content_type=content_type,
            date=date_incorrect_format,
            request_path=endpoint.prepared_request.path_url,
        )

        headers = {
            **endpoint_headers,
            'Authorization': authorization_string,
            'Date': date_incorrect_format,
        }

        endpoint.prepared_request.prepare_headers(  # type: ignore
            headers=headers,
        )
        session = requests.Session()
        response = session.send(  # type: ignore
            request=endpoint.prepared_request,
        )

        assert_vws_failure(
            response=response,
            status_code=codes.BAD_REQUEST,
            result_code=ResultCodes.FAIL,
        )

    @pytest.mark.parametrize(
        'time_multiplier',
        [1, -1],
        ids=(['After', 'Before']),
    )
    def test_date_out_of_range(
        self,
        vuforia_database_keys: VuforiaDatabaseKeys,
        time_multiplier: int,
        endpoint: TargetAPIEndpoint,
    ) -> None:
        """
        If the date header is more than five minutes before or after the
        request is sent, a `FORBIDDEN` response is returned.

        Because there is a small delay in sending requests and Vuforia isn't
        consistent, some leeway is given.
        """
        time_difference_from_now = timedelta(minutes=5, seconds=10)
        time_difference_from_now *= time_multiplier
        with freeze_time(datetime.now() + time_difference_from_now):
            date = rfc_1123_date()

        endpoint_headers = dict(endpoint.prepared_request.headers)
        content_type = endpoint_headers.get('Content-Type', '')
        assert isinstance(content_type, str)
        content = endpoint.prepared_request.body or b''
        assert isinstance(content, bytes)

        authorization_string = authorization_header(
            access_key=vuforia_database_keys.server_access_key,
            secret_key=vuforia_database_keys.server_secret_key,
            method=str(endpoint.prepared_request.method),
            content=content,
            content_type=content_type,
            date=date,
            request_path=endpoint.prepared_request.path_url,
        )

        headers = {
            **endpoint_headers,
            'Authorization': authorization_string,
            'Date': date,
        }

        endpoint.prepared_request.prepare_headers(  # type: ignore
            headers=headers,
        )
        session = requests.Session()
        response = session.send(  # type: ignore
            request=endpoint.prepared_request,
        )

        assert_vws_failure(
            response=response,
            status_code=codes.FORBIDDEN,
            result_code=ResultCodes.REQUEST_TIME_TOO_SKEWED,
        )

    @pytest.mark.parametrize(
        'time_multiplier',
        [1, -1],
        ids=(['After', 'Before']),
    )
    def test_date_in_range(
        self,
        vuforia_database_keys: VuforiaDatabaseKeys,
        time_multiplier: int,
        endpoint: TargetAPIEndpoint,
    ) -> None:
        """
        If a date header is within five minutes before or after the request
        is sent, no error is returned.

        Because there is a small delay in sending requests and Vuforia isn't
        consistent, some leeway is given.
        """
        time_difference_from_now = timedelta(minutes=4, seconds=50)
        time_difference_from_now *= time_multiplier
        with freeze_time(datetime.now() + time_difference_from_now):
            date = rfc_1123_date()
        time_difference_from_now *= time_multiplier
        with freeze_time(datetime.now() + time_difference_from_now):
            date = rfc_1123_date()

        endpoint_headers = dict(endpoint.prepared_request.headers)
        content_type = endpoint_headers.get('Content-Type', '')
        assert isinstance(content_type, str)
        content = endpoint.prepared_request.body or b''
        assert isinstance(content, bytes)

        authorization_string = authorization_header(
            access_key=vuforia_database_keys.server_access_key,
            secret_key=vuforia_database_keys.server_secret_key,
            method=str(endpoint.prepared_request.method),
            content=content,
            content_type=content_type,
            date=date,
            request_path=endpoint.prepared_request.path_url,
        )

        headers = {
            **endpoint_headers,
            'Authorization': authorization_string,
            'Date': date,
        }

        endpoint.prepared_request.prepare_headers(  # type: ignore
            headers=headers,
        )
        session = requests.Session()
        response = session.send(  # type: ignore
            request=endpoint.prepared_request,
        )

        assert_vws_response(
            response=response,
            status_code=endpoint.successful_headers_status_code,
            result_code=endpoint.successful_headers_result_code,
        )
<|endoftext|>


<commit_before>#   Copyright 2010-2011 Josh Kearney
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.

"""Pyhole Administration Plugin"""

from pyhole import utils
from pyhole import plugin

class Admin(plugin.Plugin):
    """Provide administration functionality"""

    @plugin.hook_add_command('help')
    def help(self, params=None, **kwargs):
        """Learn how to use active plugins (ex: .help <plugin.command>)"""
        if params:
            # Temporarily load the class for __doc__ access
            try:
                doc = plugin.get_command_doc(params)
                if doc:
                    self.irc.reply(doc)
                else:
                    self.irc.reply("No help available for %s" % params)
            except ImportError:
                self.irc.reply("No plugin named '%s'" % params)
        else:
            self.irc.reply(self.help.__doc__)
            self.irc.reply("Active commands: %s" % self.irc.active_commands())
            self.irc.reply("Active Keywords: %s" % self.irc.active_keywords())

    @plugin.hook_add_command('version')
    def version(self, params=None, **kwargs):
        """Display the current version"""
        self.irc.reply(self.irc.version)

    @plugin.hook_add_command('reload')
    @utils.admin
    def reload(self, params=None, **kwargs):
        """Reload all plugins"""
        self.irc.load_plugins(reload_plugins=True)
        self.irc.reply(self.irc.active_plugins())

    @plugin.hook_add_command('op')
    @utils.admin
    def op(self, params=None, **kwargs):
        """Op a user (ex: .op <channel> <nick>)"""
        if params:
            self.irc.op_user(params)
        else:
            self.irc.reply(self.op.__doc__)

    @plugin.hook_add_command('deop')
    @utils.admin
    def deop(self, params=None, **kwargs):
        """De-op a user (ex: .deop <channel> <nick>)"""
        if params:
            self.irc.deop_user(params)
        else:
            self.irc.reply(self.deop.__doc__)

    @plugin.hook_add_command('nick')
    @utils.admin
    def nick(self, params=None, **kwargs):
        """Change IRC nick (ex: .nick <nick>)"""
        if params:
            self.irc.set_nick(params)
        else:
            self.irc.reply(self.nick.__doc__)

    @plugin.hook_add_command('join')
    @utils.admin
    def join(self, params=None, **kwargs):
        """Join a channel (ex: .join #channel [<key>])"""
        print params
        if params:
            self.irc.join_channel(params)
        else:
            self.irc.reply(self.join.__doc__)

    @plugin.hook_add_command('part')
    @utils.admin
    def part(self, params=None, **kwargs):
        """Part a channel (ex: .part <channel>)"""
        if params:
            self.irc.part_channel(params)
        else:
            self.irc.reply(self.part.__doc__)

    @plugin.hook_add_command('say')
    @utils.admin
    def say(self, params=None, **kwargs):
        """Send a PRIVMSG (ex: .say <channel>|<nick> message)"""
        if params:
            (target, msg) = params.split(' ')
            self.irc.privmsg(target, msg)
        else:
            self.irc.reply(self.part.__doc__)
<commit_msg>admin say command didn't work when more than 2 words were tried<commit_after>#   Copyright 2010-2011 Josh Kearney
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.

"""Pyhole Administration Plugin"""

from pyhole import utils
from pyhole import plugin

class Admin(plugin.Plugin):
    """Provide administration functionality"""

    @plugin.hook_add_command('help')
    def help(self, params=None, **kwargs):
        """Learn how to use active plugins (ex: .help <plugin.command>)"""
        if params:
            # Temporarily load the class for __doc__ access
            try:
                doc = plugin.get_command_doc(params)
                if doc:
                    self.irc.reply(doc)
                else:
                    self.irc.reply("No help available for %s" % params)
            except ImportError:
                self.irc.reply("No plugin named '%s'" % params)
        else:
            self.irc.reply(self.help.__doc__)
            self.irc.reply("Active commands: %s" % self.irc.active_commands())
            self.irc.reply("Active Keywords: %s" % self.irc.active_keywords())

    @plugin.hook_add_command('version')
    def version(self, params=None, **kwargs):
        """Display the current version"""
        self.irc.reply(self.irc.version)

    @plugin.hook_add_command('reload')
    @utils.admin
    def reload(self, params=None, **kwargs):
        """Reload all plugins"""
        self.irc.load_plugins(reload_plugins=True)
        self.irc.reply(self.irc.active_plugins())

    @plugin.hook_add_command('op')
    @utils.admin
    def op(self, params=None, **kwargs):
        """Op a user (ex: .op <channel> <nick>)"""
        if params:
            self.irc.op_user(params)
        else:
            self.irc.reply(self.op.__doc__)

    @plugin.hook_add_command('deop')
    @utils.admin
    def deop(self, params=None, **kwargs):
        """De-op a user (ex: .deop <channel> <nick>)"""
        if params:
            self.irc.deop_user(params)
        else:
            self.irc.reply(self.deop.__doc__)

    @plugin.hook_add_command('nick')
    @utils.admin
    def nick(self, params=None, **kwargs):
        """Change IRC nick (ex: .nick <nick>)"""
        if params:
            self.irc.set_nick(params)
        else:
            self.irc.reply(self.nick.__doc__)

    @plugin.hook_add_command('join')
    @utils.admin
    def join(self, params=None, **kwargs):
        """Join a channel (ex: .join #channel [<key>])"""
        print params
        if params:
            self.irc.join_channel(params)
        else:
            self.irc.reply(self.join.__doc__)

    @plugin.hook_add_command('part')
    @utils.admin
    def part(self, params=None, **kwargs):
        """Part a channel (ex: .part <channel>)"""
        if params:
            self.irc.part_channel(params)
        else:
            self.irc.reply(self.part.__doc__)

    @plugin.hook_add_command('say')
    @utils.admin
    def say(self, params=None, **kwargs):
        """Send a PRIVMSG (ex: .say <channel>|<nick> message)"""
        if params:
            (target, msg) = params.split(' ', 1)
            self.irc.privmsg(target, msg)
        else:
            self.irc.reply(self.part.__doc__)
<|endoftext|>


<commit_before># No shebang line, this module is meant to be imported
#
# Copyright 2013 Oliver Palmer
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Process
--------

Module responsible for connecting a Twisted process object and
a job type.  Additionally this module contains other classes which
are useful in starting or managing a process.
"""

import os

from twisted.internet.protocol import ProcessProtocol as _ProcessProtocol

from pyfarm.core.enums import STRING_TYPES, NUMERIC_TYPES

class ProcessInputs(object):
    """
    Simple class used to store inputs for :meth:`JobType.spawn_process`.  This
    class does not handle data so refer to the documentation below to
    ensure your job type does not fail.

    :param dict task:
        The task these process inputs correspond to.

    :param list command:
        The list which contains the absolute path to the command to run
        and the arguments as well.

    :param dict env:
        The environment to pass along to the process.  Any value other than
        ``None`` will fully replace the environment.  A value of ``None`` will
        use the job type's ``get_environment``.

    :param string path:
        The path the process should run in.  If no path is provided
        then this will use the agent's ``--chroot`` flag to determine the
        location change into.  If this flag was not present when the agent
        was launched then the directory the agent was launched in will be used
        as a fallback.

    :param string user:
        The user name the process should run as when its launched.

        .. note::

            This value is ignored on Windows.  The underlying library used
            to execute processes does not support changing the user which
            will run the process.

    :param string group:
        The group name the process should run as when its launched.

        .. note::

            This value is ignored on Windows.  The underlying library used
            to execute processes does not support changing the group which
            will run the process.

    :param bool expandvars:
        If ``True`` then environment variables in ``command``, ``path`` and
        ``group`` will be expanded before the process is launched.

    :raises TypeError:
        Raised if we got an unexpected type for one of the input arguments or
        if we got a type we couldn't convert in :param:`command` to a string.
    """
    def __init__(
            self, task, command, env=None, path=None, user=None, group=None,
            expandvars=True):
        if not isinstance(task, dict):
            raise TypeError("Expected a dictionary for `task`")

        if not isinstance(command, (list, tuple)):
            raise TypeError("Expected a list or tuple for `command`")

        if env is not None and not isinstance(env, dict):
            raise TypeError("Expected `env` to be a dictionary")

        if path is not None and not isinstance(path, STRING_TYPES):
            raise TypeError("Expected `path` to be a string")

        if user is not None and not isinstance(user, STRING_TYPES):
            raise TypeError("Expected `user` to be a string")

        if group is not None and not isinstance(group, STRING_TYPES):
            raise TypeError("Expected `group` to be a string")

        # needed for modification below
        if isinstance(command, tuple):
            command = list(command)

        # Iterate over all entries in `command` and convert any
        # numeric values to a string.  Anything that's neither
        # string or number will raise TypeError because str(value)
        # will likely be wrong.
        for index, value in enumerate(command[:]):
            if isinstance(value, NUMERIC_TYPES):
                command[index] = str(value)

            # This is neither a string or a number, fail because
            # str() is probably going to return the wrong thing.
            elif not isinstance(value, STRING_TYPES):
                raise TypeError(
                    "Expected a string or number of entry command[%s]" % index)

        self.task = task
        self.command = tuple(command)
        self.env = env
        self.path = path
        self.user = user
        self.group = group
        self.expandvars = expandvars

    def __repr__(self):
        return "%s(task=%r, command=%r, env=%r, user=%r, group=%r)" % (
            self.__class__.__name__,
            self.task, self.command, self.env, self.user, self.group)

class ReplaceEnvironment(object):
    """
    A context manager which will replace ``os.environ``'s, or dictionary of
    your choosing, for a short period of time.  After exiting the
    context manager the original environment will be restored.

    This is useful if you have something like a process that's using
    global environment and you want to ensure that global environment is
    always consistent.

    :param dict environment:
        If provided, use this as the environment dictionary instead
        of ``os.environ``
    """
    def __init__(self, frozen_environment, environment=None):
        if environment is None:
            environment = os.environ

        self.environment = environment
        self.original_environment = None
        self.frozen_environment = frozen_environment

    def __enter__(self):
        self.original_environment = self.environment.copy()
        self.environment.clear()
        self.environment.update(self.frozen_environment)
        return self

    def __exit__(self, *_):
        self.environment.clear()
        self.environment.update(self.original_environment)

class ProcessProtocol(_ProcessProtocol):
    """
    Subclass of :class:`.Protocol` which hooks into the various systems
    necessary to run and manage a process.  More specifically, this helps
    to act as plumbing between the process being run and the job type.
    """
    def __init__(self, jobtype, process_inputs, command, arguments, env,
                 path, uid, gid, protocol_id=None):
        self.jobtype = jobtype
        self.inputs = process_inputs
        self.command = command
        self.args = arguments
        self.env = env
        self.path = path
        self.uid = uid
        self.gid = gid

        if protocol_id is None:
            protocol_id = self.inputs.task["id"]

        self.id = protocol_id

    def __repr__(self):
        return "Process(task=%r, pid=%r, command=%r, args=%r, path=%r, " \
               "uid=%r, gid=%r)" % (
            self.id, self.pid, self.command, self.args,
            self.path, self.uid, self.gid)

    @property
    def pid(self):
        return self.transport.pid

    @property
    def process(self):
        return self.transport

    def connectionMade(self):
        """
        Called when the process first starts and the file descriptors
        have opened.
        """
        self.jobtype.process_started(self)

    def processEnded(self, reason):
        """
        Called when the process has terminated and all file descriptors
        have been closed.  :meth:`processExited` is called to however we
        only want to notify the parent job type once the process has freed
        up the last bit of resources.
        """
        self.jobtype.process_stopped(self, reason)

    def outReceived(self, data):
        """Called when the process emits on stdout"""
        self.jobtype.received_stdout(self, data)

    def errReceived(self, data):
        """Called when the process emits on stderr"""
        self.jobtype.received_stderr(self, data)
<commit_msg>Switching from 'task' to 'tasks' inside ProcessInputs and ProcessProtocol.  These will start to allow for multiple tasks to account for a single process.<commit_after># No shebang line, this module is meant to be imported
#
# Copyright 2013 Oliver Palmer
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Process
--------

Module responsible for connecting a Twisted process object and
a job type.  Additionally this module contains other classes which
are useful in starting or managing a process.
"""

import os

from twisted.internet.protocol import ProcessProtocol as _ProcessProtocol

from pyfarm.core.enums import STRING_TYPES, NUMERIC_TYPES

PROTOCOL_ID_TYPES = set(list(NUMERIC_TYPES) + [list, tuple])

class ProcessInputs(object):
    """
    Simple class used to store inputs for :meth:`JobType.spawn_process`.  This
    class does not handle data so refer to the documentation below to
    ensure your job type does not fail.

    :param list tasks:
        The task or tasks that this instance is running for.

    :param list command:
        The list which contains the absolute path to the command to run
        and the arguments as well.

    :param dict env:
        The environment to pass along to the process.  Any value other than
        ``None`` will fully replace the environment.  A value of ``None`` will
        use the job type's ``get_environment``.

    :param string path:
        The path the process should run in.  If no path is provided
        then this will use the agent's ``--chroot`` flag to determine the
        location change into.  If this flag was not present when the agent
        was launched then the directory the agent was launched in will be used
        as a fallback.

    :param string user:
        The user name the process should run as when its launched.

        .. note::

            This value is ignored on Windows.  The underlying library used
            to execute processes does not support changing the user which
            will run the process.

    :param string group:
        The group name the process should run as when its launched.

        .. note::

            This value is ignored on Windows.  The underlying library used
            to execute processes does not support changing the group which
            will run the process.

    :param bool expandvars:
        If ``True`` then environment variables in ``command``, ``path`` and
        ``group`` will be expanded before the process is launched.

    :raises TypeError:
        Raised if we got an unexpected type for one of the input arguments or
        if we got a type we couldn't convert in :param:`command` to a string.
    """
    def __init__(
            self, tasks, command, env=None, path=None, user=None, group=None,
            expandvars=True):
        if not isinstance(tasks, (list, tuple)):
            raise TypeError("Expected a list for `tasks`")

        if not isinstance(command, (list, tuple)):
            raise TypeError("Expected a list or tuple for `command`")

        if env is not None and not isinstance(env, dict):
            raise TypeError("Expected `env` to be a dictionary")

        if path is not None and not isinstance(path, STRING_TYPES):
            raise TypeError("Expected `path` to be a string")

        if user is not None and not isinstance(user, STRING_TYPES):
            raise TypeError("Expected `user` to be a string")

        if group is not None and not isinstance(group, STRING_TYPES):
            raise TypeError("Expected `group` to be a string")

        # needed for modification below
        if isinstance(command, tuple):
            command = list(command)

        # Iterate over all entries in `command` and convert any
        # numeric values to a string.  Anything that's neither
        # string or number will raise TypeError because str(value)
        # will likely be wrong.
        for index, value in enumerate(command[:]):
            if isinstance(value, NUMERIC_TYPES):
                command[index] = str(value)

            # This is neither a string or a number, fail because
            # str() is probably going to return the wrong thing.
            elif not isinstance(value, STRING_TYPES):
                raise TypeError(
                    "Expected a string or number of entry command[%s]" % index)

        self.tasks = tuple(tasks)
        self.command = tuple(command)
        self.env = env
        self.path = path
        self.user = user
        self.group = group
        self.expandvars = expandvars

    def __repr__(self):
        return "%s(tasks=%r, command=%r, env=%r, user=%r, group=%r)" % (
            self.__class__.__name__,
            self.tasks, self.command, self.env, self.user, self.group)

class ReplaceEnvironment(object):
    """
    A context manager which will replace ``os.environ``'s, or dictionary of
    your choosing, for a short period of time.  After exiting the
    context manager the original environment will be restored.

    This is useful if you have something like a process that's using
    global environment and you want to ensure that global environment is
    always consistent.

    :param dict environment:
        If provided, use this as the environment dictionary instead
        of ``os.environ``
    """
    def __init__(self, frozen_environment, environment=None):
        if environment is None:
            environment = os.environ

        self.environment = environment
        self.original_environment = None
        self.frozen_environment = frozen_environment

    def __enter__(self):
        self.original_environment = self.environment.copy()
        self.environment.clear()
        self.environment.update(self.frozen_environment)
        return self

    def __exit__(self, *_):
        self.environment.clear()
        self.environment.update(self.original_environment)

class ProcessProtocol(_ProcessProtocol):
    """
    Subclass of :class:`.Protocol` which hooks into the various systems
    necessary to run and manage a process.  More specifically, this helps
    to act as plumbing between the process being run and the job type.
    """
    def __init__(self, jobtype, process_inputs, command, arguments, env,
                 path, uid, gid):
        self.jobtype = jobtype
        self.inputs = process_inputs
        self.command = command
        self.args = arguments
        self.env = env
        self.path = path
        self.uid = uid
        self.gid = gid
        self.id = tuple(task["id"] for task in process_inputs.tasks)

    def __repr__(self):
        return "Process(id=%r, pid=%r, command=%r, args=%r, path=%r, " \
               "uid=%r, gid=%r)" % (
                   self.tasks, self.pid, self.command, self.args,
                   self.path, self.uid, self.gid)

    @property
    def pid(self):
        return self.transport.pid

    @property
    def process(self):
        return self.transport

    def connectionMade(self):
        """
        Called when the process first starts and the file descriptors
        have opened.
        """
        self.jobtype.process_started(self)

    def processEnded(self, reason):
        """
        Called when the process has terminated and all file descriptors
        have been closed.  :meth:`processExited` is called to however we
        only want to notify the parent job type once the process has freed
        up the last bit of resources.
        """
        self.jobtype.process_stopped(self, reason)

    def outReceived(self, data):
        """Called when the process emits on stdout"""
        self.jobtype.received_stdout(self, data)

    def errReceived(self, data):
        """Called when the process emits on stderr"""
        self.jobtype.received_stderr(self, data)
<|endoftext|>


<commit_before>import unittest
from opentrons_sdk.drivers.motor import OpenTrons, GCodeLogger

class SerialTestCase(unittest.TestCase):

    def assertLastCommand(self, *commands, index=-1):
        lastCommand = self.motor.connection.write_buffer[index].decode("utf-8")
        foundOne = False
        for command in commands:
            if lastCommand.startswith(command):
                foundOne = True
                break

        msg = "Expected last CNC command to be "

        if len(commands) is 1:
            msg += '"' + commands[0] + '" '
        else:
            msg += "one of: " + ", ".join(commands) + " "

        msg += "but got \"" + lastCommand.strip() + "\"."

        self.assertTrue(foundOne, msg=msg)

    def assertLastArguments(self, *arguments, index=-1):
        lastCommand = self.motor.connection.write_buffer[index].decode("utf-8")
        for arg in arguments:
            msg = "Expected last command arguments to include " + \
                  "\"" + arg + "\" but got \"" + lastCommand.strip() +\
                  "\" instead."
            self.assertTrue(arg in lastCommand, msg=msg)

class OpenTronsTest(SerialTestCase):

    def setUp(self):

        # set this to True if testing with a robot connected
        # testing while connected allows the response handlers
        # and serial handshakes to be tested
        self.smoothie_connected = True

        self.motor = OpenTrons()

        self.smoothie_connected = False

        if sys.argv[1]:
            self.smoothie_connected = True
            success = self.motor.connect(sys.argv[1])
            self.assertTrue(success)
            self.motor.resume()
        else:
            self.motor.connection = GCodeLogger()

    def tearDown(self):
        self.motor.disconnect()

    def test_get_position(self):
        self.motor.home()
        coords = self.motor.get_position()
        if self.smoothie_connected:
            expected_coords = {
                'target':{'x':0,'y':0,'z':0,'a':0,'b':0},
                'current':{'x':0,'y':0,'z':0,'a':0,'b':0}
            }
            self.assertEquals(coords,expected_coords)
        else:
            self.assertLastCommand('M114')

    def test_halt(self):
        success = self.motor.halt()
        if self.smoothie_connected:
            self.assertTrue(success)
        else:
            self.assertLastCommand('M112')

        # must resume before any other commands can be processed
        success = self.motor.resume()
        if self.smoothie_connected:
            self.assertTrue(success)
        else:
            self.assertLastCommand('M999')

    def test_home(self):
        success = self.motor.home('x','y')
        if self.smoothie_connected:
            self.assertTrue(success)
        else:
            self.assertLastCommand('G28XY',index=-2)
            self.assertLastCommand('G92')
            self.assertLastArguments('X0', 'Y0')

        success = self.motor.home('ba')
        if self.smoothie_connected:
            self.assertTrue(success)
        else:
            self.assertLastCommand('G28AB',index=-2)
            self.assertLastCommand('G92')
            self.assertLastArguments('A0', 'B0')

    def test_move_x(self):
        success = self.motor.move(x=100)
        if self.smoothie_connected:
            self.assertTrue(success)
        else:
            self.assertLastCommand('G0', 'G1')
            self.assertLastArguments('X100')

    def test_move_y(self):
        success = self.motor.move(y=100)
        if self.smoothie_connected:
            self.assertTrue(success)
        else:
            self.assertLastCommand('G0', 'G1')
            self.assertLastArguments('Y100')

    def test_move_z(self):
        success = self.motor.move(z=30)
        if self.smoothie_connected:
            self.assertTrue(success)
        else:
            self.assertLastCommand('G0', 'G1')
            self.assertLastArguments('Z30')

    def test_send_command(self):
        success = self.motor.send_command('G999 X1 Y1 Z1')
        if self.smoothie_connected:
            self.assertTrue(success)
        else:
            self.assertLastCommand('G999')
            self.assertLastArguments('X1', 'Y1', 'Z1')

    def test_send_command_with_kwargs(self):
        success = self.motor.send_command('G999', x=1, y=2, z=3)
        if self.smoothie_connected:
            self.assertTrue(success)
        else:
            self.assertLastCommand('G999')
            self.assertLastArguments('x1', 'y2', 'z3')

    def test_wait(self):
        success = self.motor.wait(1.234)
        if self.smoothie_connected:
            self.assertTrue(success)
        else:
            self.assertLastCommand('G4')
            self.assertLastArguments('S1', 'P234')

    def test_wait_for_arrival(self):
        if self.smoothie_connected:
            self.motor.home()
            self.motor.move(x=200,y=200)
            self.motor.move(z=30)
            success = self.motor.wait_for_arrival()
            self.assertTrue(success)
<commit_msg>import sys<commit_after>import sys
import unittest
from opentrons_sdk.drivers.motor import OpenTrons, GCodeLogger

class SerialTestCase(unittest.TestCase):

    def assertLastCommand(self, *commands, index=-1):
        lastCommand = self.motor.connection.write_buffer[index].decode("utf-8")
        foundOne = False
        for command in commands:
            if lastCommand.startswith(command):
                foundOne = True
                break

        msg = "Expected last CNC command to be "

        if len(commands) is 1:
            msg += '"' + commands[0] + '" '
        else:
            msg += "one of: " + ", ".join(commands) + " "

        msg += "but got \"" + lastCommand.strip() + "\"."

        self.assertTrue(foundOne, msg=msg)

    def assertLastArguments(self, *arguments, index=-1):
        lastCommand = self.motor.connection.write_buffer[index].decode("utf-8")
        for arg in arguments:
            msg = "Expected last command arguments to include " + \
                  "\"" + arg + "\" but got \"" + lastCommand.strip() +\
                  "\" instead."
            self.assertTrue(arg in lastCommand, msg=msg)

class OpenTronsTest(SerialTestCase):

    def setUp(self):

        # set this to True if testing with a robot connected
        # testing while connected allows the response handlers
        # and serial handshakes to be tested
        self.smoothie_connected = True

        self.motor = OpenTrons()

        self.smoothie_connected = False

        if sys.argv[1]:
            self.smoothie_connected = True
            success = self.motor.connect(sys.argv[1])
            self.assertTrue(success)
            self.motor.resume()
        else:
            self.motor.connection = GCodeLogger()

    def tearDown(self):
        self.motor.disconnect()

    def test_get_position(self):
        self.motor.home()
        coords = self.motor.get_position()
        if self.smoothie_connected:
            expected_coords = {
                'target':{'x':0,'y':0,'z':0,'a':0,'b':0},
                'current':{'x':0,'y':0,'z':0,'a':0,'b':0}
            }
            self.assertEquals(coords,expected_coords)
        else:
            self.assertLastCommand('M114')

    def test_halt(self):
        success = self.motor.halt()
        if self.smoothie_connected:
            self.assertTrue(success)
        else:
            self.assertLastCommand('M112')

        # must resume before any other commands can be processed
        success = self.motor.resume()
        if self.smoothie_connected:
            self.assertTrue(success)
        else:
            self.assertLastCommand('M999')

    def test_home(self):
        success = self.motor.home('x','y')
        if self.smoothie_connected:
            self.assertTrue(success)
        else:
            self.assertLastCommand('G28XY',index=-2)
            self.assertLastCommand('G92')
            self.assertLastArguments('X0', 'Y0')

        success = self.motor.home('ba')
        if self.smoothie_connected:
            self.assertTrue(success)
        else:
            self.assertLastCommand('G28AB',index=-2)
            self.assertLastCommand('G92')
            self.assertLastArguments('A0', 'B0')

    def test_move_x(self):
        success = self.motor.move(x=100)
        if self.smoothie_connected:
            self.assertTrue(success)
        else:
            self.assertLastCommand('G0', 'G1')
            self.assertLastArguments('X100')

    def test_move_y(self):
        success = self.motor.move(y=100)
        if self.smoothie_connected:
            self.assertTrue(success)
        else:
            self.assertLastCommand('G0', 'G1')
            self.assertLastArguments('Y100')

    def test_move_z(self):
        success = self.motor.move(z=30)
        if self.smoothie_connected:
            self.assertTrue(success)
        else:
            self.assertLastCommand('G0', 'G1')
            self.assertLastArguments('Z30')

    def test_send_command(self):
        success = self.motor.send_command('G999 X1 Y1 Z1')
        if self.smoothie_connected:
            self.assertTrue(success)
        else:
            self.assertLastCommand('G999')
            self.assertLastArguments('X1', 'Y1', 'Z1')

    def test_send_command_with_kwargs(self):
        success = self.motor.send_command('G999', x=1, y=2, z=3)
        if self.smoothie_connected:
            self.assertTrue(success)
        else:
            self.assertLastCommand('G999')
            self.assertLastArguments('x1', 'y2', 'z3')

    def test_wait(self):
        success = self.motor.wait(1.234)
        if self.smoothie_connected:
            self.assertTrue(success)
        else:
            self.assertLastCommand('G4')
            self.assertLastArguments('S1', 'P234')

    def test_wait_for_arrival(self):
        if self.smoothie_connected:
            self.motor.home()
            self.motor.move(x=200,y=200)
            self.motor.move(z=30)
            success = self.motor.wait_for_arrival()
            self.assertTrue(success)
<|endoftext|>


<commit_before>""" This plugin has some emoji to PNG utilities!

Keep in mind that this library requires pycario, which I've found to be quite
difficult to install for windows.

Commands:
    greater
"""

import os
import re
from io import BytesIO

import discord
import cairosvg
from PIL import Image

import plugins
from pcbot import Annotate, utils

client = plugins.client  # type: discord.Client

emoji_path = "plugins/twemoji21lib/"
default_size = 1024
max_width = 2048
max_emoji = 64
emoji = {}

emote_regex = re.compile(r"<:(?P<name>\w+):(?P<id>\d+)>")
emote_cache = {}  # Cache for all custom emotes/emoji
emote_size = 112

def init_emoji():
    """ This will be a list of all emoji SVG files. Storing this in RAM is for
    easier access, considering the library of emojies only take up about 2.5MB
    (or 4MB in disk) as of now. If the library expands, the size should not
    increase drastically. """
    for file in os.listdir(emoji_path):
        with open(os.path.join(emoji_path, file), "rb") as f:
            emoji_name = file.split(".")[0]  # Strip the extension
            emoji_bytes = f.read()
            emoji[emoji_name] = emoji_bytes

def get_emoji(chars: str, size=default_size):
    """ Return the emoji with the specified character and optionally
    with the given size. """
    if chars not in emoji:
        return None

    emoji_bytes = emoji[chars]
    size_bytes = bytes(str(size), encoding="utf-8")
    emoji_bytes = emoji_bytes.replace(b"<svg ",
                                      b"<svg width=\"" + size_bytes + b"px\" height=\"" + size_bytes + b"px\" ")
    return Image.open(BytesIO(cairosvg.svg2png(emoji_bytes)))

async def get_emote(emote_id: str, server: discord.Server):
    """ Return the image of a custom emote. """
    emote = discord.Emoji(id=emote_id, server=server)

    # Return the cached version if possible
    if emote.id in emote_cache:
        return emote_cache[emote.id]

    # Otherwise, download the emote, store it in the cache and return
    emote_bytes = await utils.download_file(emote.url)
    emote_cache[emote.id] = emote_bytes
    return Image.open(BytesIO(emote_bytes))

def parse_emoji(chars: list, size):
    """ Go through and return all emoji in the given list of characters
    (or Image objects). """
    # Convert all characters in the given list to hex format strings, and leave the Image objects alone
    chars = [hex(ord(c))[2:] if type(c) is str else c for c in chars]
    chars_remaining = length = len(chars)

    # Try the entire string backwards, and reduce the length by one character until there's a match
    while True:
        sliced_emoji = chars[:length]
        if not sliced_emoji:
            break

        # If this is a custom emote, yield it and progress
        if type(sliced_emoji[0]) is Image.Image:
            yield sliced_emoji[0]

            chars = chars[length:]
            chars_remaining = length = len(chars)
            continue

        # If the emoji is in the list, update the index and reset the length, with the updated index
        if "-".join(sliced_emoji) in emoji.keys():
            yield get_emoji("-".join(sliced_emoji), size=size)

            chars = chars[length:]
            chars_remaining = length = len(chars)
            continue

        # When we don't find an emoji, reduce the length
        length -= 1

        # When the length is 0, but the amount of characters is greater than 1, remove the first one
        if length == 0 and chars_remaining > 1:
            chars = chars[1:]
            chars_remaining = length = len(chars)

async def format_emoji(text: str, server: discord.Server):
    """ Creates a list supporting both emoji and custom emotes. """
    char_and_emotes = []

    # Loop through each character and compare with custom emotes.
    # Add characters to list, along with emote byte-strings
    text_iter = iter(enumerate(text))
    has_custom = False
    for i, c in text_iter:
        match = emote_regex.match(text[i:])
        if match:
            char_and_emotes.append(await get_emote(match.group("id"), server))
            has_custom = True

            # Skip ahead in the iterator
            for _ in range(len(match.group(0)) - 1):
                next(text_iter)
        else:
            char_and_emotes.append(c)

    # Return the list of emoji, and set the respective size (should be managed manually if needed)
    return list(parse_emoji(char_and_emotes, emote_size if has_custom else default_size))

@plugins.command(aliases="huge")
async def greater(message: discord.Message, text: Annotate.CleanContent):
    """ Gives a **huge** version of emojies. """
    # Parse all unicode and load the emojies
    parsed_emoji = await format_emoji(text, message.server)
    assert parsed_emoji, "I couldn't find any emoji in that text of yours."

    # Combine multiple images if necessary, otherwise send just the one
    if len(parsed_emoji) == 1:
        image_fp = utils.convert_image_object(parsed_emoji[0])
        await client.send_file(message.channel, image_fp, filename="emoji.png")
        return

    # See if there's a need to rescale all images.
    size = default_size
    for e in parsed_emoji:
        width, height = e.size

        # Set the size to the lowest dimensions
        if width < size:
            size = width
        if height < size:
            size = height

    # When the size of all emoji next to each other is greater than the max width,
    # divide the size to properly fit the max_width at all times
    if size * len(parsed_emoji) > max_width:
        scale = 1 / ((size * len(parsed_emoji) - 1) // max_width + 1)
        size *= scale

    # Resize all emoji (so that the height == size) when one doesn't match any of the predetermined sizes
    total_width = 0
    if size not in (default_size, emote_size):
        for e in parsed_emoji:
            if e.height > size:
                width = round(size / e.height)
                total_width += width
                e.resize((width, e.height), Image.ANTIALIAS)
            else:
                total_width += e.width
    else:
        total_width = len(parsed_emoji) * size

    # Stitch all the images together
    image = Image.new("RGBA", (total_width, size))
    x = 0
    for image_object in parsed_emoji:
        image.paste(image_object, box=(x, 0))
        x += image_object.width

    # Upload the stitched image
    image_fp = utils.convert_image_object(image)
    await client.send_file(message.channel, image_fp, filename="emojies.png")

init_emoji()

<commit_msg>V6 of improved !greater command.<commit_after>""" This plugin has some emoji to PNG utilities!

Keep in mind that this library requires pycario, which I've found to be quite
difficult to install for windows.

Commands:
    greater
"""

import os
import re
from io import BytesIO

import discord
import cairosvg
from PIL import Image

import plugins
from pcbot import Annotate, utils

client = plugins.client  # type: discord.Client

emoji_path = "plugins/twemoji21lib/"
default_size = 1024
max_width = 2048
max_emoji = 64
emoji = {}

emote_regex = re.compile(r"<:(?P<name>\w+):(?P<id>\d+)>")
emote_cache = {}  # Cache for all custom emotes/emoji
emote_size = 112

def init_emoji():
    """ This will be a list of all emoji SVG files. Storing this in RAM is for
    easier access, considering the library of emojies only take up about 2.5MB
    (or 4MB in disk) as of now. If the library expands, the size should not
    increase drastically. """
    for file in os.listdir(emoji_path):
        with open(os.path.join(emoji_path, file), "rb") as f:
            emoji_name = file.split(".")[0]  # Strip the extension
            emoji_bytes = f.read()
            emoji[emoji_name] = emoji_bytes

def get_emoji(chars: str, size=default_size):
    """ Return the emoji with the specified character and optionally
    with the given size. """
    if chars not in emoji:
        return None

    emoji_bytes = emoji[chars]
    size_bytes = bytes(str(size), encoding="utf-8")
    emoji_bytes = emoji_bytes.replace(b"<svg ",
                                      b"<svg width=\"" + size_bytes + b"px\" height=\"" + size_bytes + b"px\" ")
    return Image.open(BytesIO(cairosvg.svg2png(emoji_bytes)))

async def get_emote(emote_id: str, server: discord.Server):
    """ Return the image of a custom emote. """
    emote = discord.Emoji(id=emote_id, server=server)

    # Return the cached version if possible
    if emote.id in emote_cache:
        return emote_cache[emote.id]

    # Otherwise, download the emote, store it in the cache and return
    emote_bytes = await utils.download_file(emote.url)
    emote_cache[emote.id] = emote_bytes
    return Image.open(BytesIO(emote_bytes))

def parse_emoji(chars: list, size):
    """ Go through and return all emoji in the given list of characters
    (or Image objects). """
    # Convert all characters in the given list to hex format strings, and leave the Image objects alone
    chars = [hex(ord(c))[2:] if type(c) is str else c for c in chars]
    chars_remaining = length = len(chars)

    # Try the entire string backwards, and reduce the length by one character until there's a match
    while True:
        sliced_emoji = chars[:length]
        if not sliced_emoji:
            break

        # If this is a custom emote, yield it and progress
        if type(sliced_emoji[0]) is Image.Image:
            yield sliced_emoji[0]

            chars = chars[length:]
            chars_remaining = length = len(chars)
            continue

        # If the emoji is in the list, update the index and reset the length, with the updated index
        if "-".join(sliced_emoji) in emoji.keys():
            yield get_emoji("-".join(sliced_emoji), size=size)

            chars = chars[length:]
            chars_remaining = length = len(chars)
            continue

        # When we don't find an emoji, reduce the length
        length -= 1

        # When the length is 0, but the amount of characters is greater than 1, remove the first one
        if length == 0 and chars_remaining > 1:
            chars = chars[1:]
            chars_remaining = length = len(chars)

async def format_emoji(text: str, server: discord.Server):
    """ Creates a list supporting both emoji and custom emotes. """
    char_and_emotes = []

    # Loop through each character and compare with custom emotes.
    # Add characters to list, along with emote byte-strings
    text_iter = iter(enumerate(text))
    has_custom = False
    for i, c in text_iter:
        match = emote_regex.match(text[i:])
        if match:
            char_and_emotes.append(await get_emote(match.group("id"), server))
            has_custom = True

            # Skip ahead in the iterator
            for _ in range(len(match.group(0)) - 1):
                next(text_iter)
        else:
            char_and_emotes.append(c)

    # When the size of all emoji next to each other is greater than the max width,
    # divide the size to properly fit the max_width at all times
    size = 0
    if has_custom:
        size = emote_size
    else:
        if size * len(char_and_emotes) > max_width:
            scale = 1 / ((size * len(char_and_emotes) - 1) // max_width + 1)
            size *= scale

    # Return the list of emoji, and set the respective size (should be managed manually if needed)
    return list(parse_emoji(char_and_emotes, emote_size if has_custom else size)), has_custom

@plugins.command(aliases="huge")
async def greater(message: discord.Message, text: Annotate.CleanContent):
    """ Gives a **huge** version of emojies. """
    # Parse all unicode and load the emojies
    parsed_emoji, has_custom = await format_emoji(text, message.server)
    assert parsed_emoji, "I couldn't find any emoji in that text of yours."

    # Combine multiple images if necessary, otherwise send just the one
    if len(parsed_emoji) == 1:
        image_fp = utils.convert_image_object(parsed_emoji[0])
        await client.send_file(message.channel, image_fp, filename="emoji.png")
        return

    # See if there's a need to rescale all images.
    size = default_size
    for e in parsed_emoji:
        width, height = e.size

        # Set the size to the lowest dimensions
        if width < size:
            size = width
        if height < size:
            size = height

    # Resize all emoji (so that the height == size) when one doesn't match any of the predetermined sizes
    total_width = 0
    if has_custom and not size == emote_size:
        for e in parsed_emoji:
            if e.height > size:
                width = round(size / e.height)
                total_width += width
                e.resize((width, e.height), Image.ANTIALIAS)
            else:
                total_width += e.width
    else:
        total_width = len(parsed_emoji) * size

    # Stitch all the images together
    image = Image.new("RGBA", (total_width, size))
    x = 0
    for image_object in parsed_emoji:
        image.paste(image_object, box=(x, 0))
        x += image_object.width

    # Upload the stitched image
    image_fp = utils.convert_image_object(image)
    await client.send_file(message.channel, image_fp, filename="emojies.png")

init_emoji()

<|endoftext|>


<commit_before>"""Interact with functions using widgets.
"""

#-----------------------------------------------------------------------------
# Copyright (c) 2013, the IPython Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file COPYING.txt, distributed with this software.
#-----------------------------------------------------------------------------

#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------

from IPython.html.widgets import (Widget, TextWidget,
    FloatSliderWidget, IntSliderWidget, CheckboxWidget, DropdownWidget,
    ContainerWidget)
from IPython.display import display, clear_output
from IPython.utils.py3compat import string_types, unicode_type

#-----------------------------------------------------------------------------
# Classes and Functions
#-----------------------------------------------------------------------------

def _matches(o, pattern):
    if not len(o) == len(pattern):
        return False
    comps = zip(o,pattern)
    return all(isinstance(obj,kind) for obj,kind in comps)

def _get_min_max_value(min, max, value):
    """Return min, max, value given input values with possible None."""
    if value is None:
        if not max > min:
            raise ValueError('max must be greater than min: (min={0}, max={1})'.format(min, max))
        value = min + abs(min-max)/2
        value = type(min)(value)
    elif min is None and max is None:
        if value == 0.0:
            min, max, value = 0.0, 1.0, 0.5
        elif value == 0:
            min, max, value = 0, 1, 0
        elif isinstance(value, float):
            min, max = -value, 3.0*value
        elif isinstance(value, int):
            min, max = -value, 3*value
        else:
            raise TypeError('expected a number, got: %r' % number)
    else:
        raise ValueError('unable to infer range, value from: ({0}, {1}, {2})'.format(min, max, value))
    return min, max, value

def _widget_abbrev(o):
    if isinstance(o, string_types):
        return TextWidget(value=unicode_type(o))
    elif isinstance(o, dict):
        labels = [unicode_type(k) for k in o]
        values = o.values()
        w = DropdownWidget(value=values[0], values=values, labels=labels)
        return w
    # Special case float and int == 0.0
    # get_range(value):
    elif isinstance(o, bool):
        return CheckboxWidget(value=o)
    elif isinstance(o, float):
        min, max, value = _get_min_max_value(None, None, o)
        return FloatSliderWidget(value=o, min=min, max=max)
    elif isinstance(o, int):
        min, max, value = _get_min_max_value(None, None, o)
        return IntSliderWidget(value=o, min=min, max=max)
    if isinstance(o, (list, tuple)):
        if _matches(o, (int, int)):
            min, max, value = _get_min_max_value(o[0], o[1], None)
            return IntSliderWidget(value=value, min=min, max=max)
        elif _matches(o, (int, int, int)):
            min, max, value = _get_min_max_value(o[0], o[1], None)
            return IntSliderWidget(value=value, min=min, max=max, step=o[2])
        elif _matches(o, (float, float)):
            min, max, value = _get_min_max_value(o[0], o[1], None)
            return FloatSliderWidget(value=value, min=min, max=max)
        elif _matches(o, (float, float, float)):
            min, max, value = _get_min_max_value(o[0], o[1], None)
            return FloatSliderWidget(value=value, min=min, max=max, step=o[2])
        elif _matches(o, (float, float, int)):
            min, max, value = _get_min_max_value(o[0], o[1], None)
            return FloatSliderWidget(value=value, min=min, max=max, step=float(o[2]))
        elif all(isinstance(x, string_types) for x in o):
            return DropdownWidget(value=unicode_type(o[0]),
                                   values=[unicode_type(k) for k in o])

def interactive(f, **kwargs):
    """Interact with a function using widgets."""

    co = kwargs.pop('clear_output', True)
    # First convert all args to Widget instances
    widgets = []
    container = ContainerWidget()
    container.result = None
    container.kwargs = dict()
    for key, value in kwargs.items():
        if isinstance(value, Widget):
            widget = value
        else:
            widget = _widget_abbrev(value)
            if widget is None:
                raise ValueError("Object cannot be transformed to a Widget")
        widget.description = key
        widgets.append((key,widget))
    widgets.sort(key=lambda e: e[1].__class__.__name__)
    container.children = [e[1] for e in widgets]

    # Build the callback
    def call_f(name, old, new):
        actual_kwargs = {}
        for key, widget in widgets:
            value = widget.value
            container.kwargs[key] = value
            actual_kwargs[key] = value
        if co:
            clear_output(wait=True)
        container.result = f(**actual_kwargs)

    # Wire up the widgets
    for key, widget in widgets:
        widget.on_trait_change(call_f, 'value')

    container.on_displayed(lambda _: call_f(None, None, None))

    return container

def interact(f, **kwargs):
    w = interactive(f, **kwargs)
    display(w)
<commit_msg>Adding .widget to function.<commit_after>"""Interact with functions using widgets.
"""

#-----------------------------------------------------------------------------
# Copyright (c) 2013, the IPython Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file COPYING.txt, distributed with this software.
#-----------------------------------------------------------------------------

#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------

from IPython.html.widgets import (Widget, TextWidget,
    FloatSliderWidget, IntSliderWidget, CheckboxWidget, DropdownWidget,
    ContainerWidget)
from IPython.display import display, clear_output
from IPython.utils.py3compat import string_types, unicode_type

#-----------------------------------------------------------------------------
# Classes and Functions
#-----------------------------------------------------------------------------

def _matches(o, pattern):
    if not len(o) == len(pattern):
        return False
    comps = zip(o,pattern)
    return all(isinstance(obj,kind) for obj,kind in comps)

def _get_min_max_value(min, max, value):
    """Return min, max, value given input values with possible None."""
    if value is None:
        if not max > min:
            raise ValueError('max must be greater than min: (min={0}, max={1})'.format(min, max))
        value = min + abs(min-max)/2
        value = type(min)(value)
    elif min is None and max is None:
        if value == 0.0:
            min, max, value = 0.0, 1.0, 0.5
        elif value == 0:
            min, max, value = 0, 1, 0
        elif isinstance(value, float):
            min, max = -value, 3.0*value
        elif isinstance(value, int):
            min, max = -value, 3*value
        else:
            raise TypeError('expected a number, got: %r' % number)
    else:
        raise ValueError('unable to infer range, value from: ({0}, {1}, {2})'.format(min, max, value))
    return min, max, value

def _widget_abbrev(o):
    if isinstance(o, string_types):
        return TextWidget(value=unicode_type(o))
    elif isinstance(o, dict):
        labels = [unicode_type(k) for k in o]
        values = o.values()
        w = DropdownWidget(value=values[0], values=values, labels=labels)
        return w
    # Special case float and int == 0.0
    # get_range(value):
    elif isinstance(o, bool):
        return CheckboxWidget(value=o)
    elif isinstance(o, float):
        min, max, value = _get_min_max_value(None, None, o)
        return FloatSliderWidget(value=o, min=min, max=max)
    elif isinstance(o, int):
        min, max, value = _get_min_max_value(None, None, o)
        return IntSliderWidget(value=o, min=min, max=max)
    if isinstance(o, (list, tuple)):
        if _matches(o, (int, int)):
            min, max, value = _get_min_max_value(o[0], o[1], None)
            return IntSliderWidget(value=value, min=min, max=max)
        elif _matches(o, (int, int, int)):
            min, max, value = _get_min_max_value(o[0], o[1], None)
            return IntSliderWidget(value=value, min=min, max=max, step=o[2])
        elif _matches(o, (float, float)):
            min, max, value = _get_min_max_value(o[0], o[1], None)
            return FloatSliderWidget(value=value, min=min, max=max)
        elif _matches(o, (float, float, float)):
            min, max, value = _get_min_max_value(o[0], o[1], None)
            return FloatSliderWidget(value=value, min=min, max=max, step=o[2])
        elif _matches(o, (float, float, int)):
            min, max, value = _get_min_max_value(o[0], o[1], None)
            return FloatSliderWidget(value=value, min=min, max=max, step=float(o[2]))
        elif all(isinstance(x, string_types) for x in o):
            return DropdownWidget(value=unicode_type(o[0]),
                                   values=[unicode_type(k) for k in o])

def interactive(f, **kwargs):
    """Interact with a function using widgets."""

    co = kwargs.pop('clear_output', True)
    # First convert all args to Widget instances
    widgets = []
    container = ContainerWidget()
    container.result = None
    container.kwargs = dict()
    for key, value in kwargs.items():
        if isinstance(value, Widget):
            widget = value
        else:
            widget = _widget_abbrev(value)
            if widget is None:
                raise ValueError("Object cannot be transformed to a Widget")
        widget.description = key
        widgets.append((key,widget))
    widgets.sort(key=lambda e: e[1].__class__.__name__)
    container.children = [e[1] for e in widgets]

    # Build the callback
    def call_f(name, old, new):
        actual_kwargs = {}
        for key, widget in widgets:
            value = widget.value
            container.kwargs[key] = value
            actual_kwargs[key] = value
        if co:
            clear_output(wait=True)
        container.result = f(**actual_kwargs)

    # Wire up the widgets
    for key, widget in widgets:
        widget.on_trait_change(call_f, 'value')

    container.on_displayed(lambda _: call_f(None, None, None))

    return container

def interact(f, **kwargs):
    w = interactive(f, **kwargs)
    f.widget = w
    display(w)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
Test FastMarkerCluster
------------------
"""

from jinja2 import Template
import numpy as np

import folium
from folium import plugins

def test_fast_marker_cluster():
    n = 100
    np.random.seed(seed=26082009)
    data = np.array([
        np.random.uniform(low=35, high=60, size=n),   # Random latitudes.
        np.random.uniform(low=-12, high=30, size=n),  # Random longitudes.
        range(n),                                     # Popups.
    ]).tolist()
    m = folium.Map([45., 3.], zoom_start=4)
    mc = plugins.FastMarkerCluster(data, callback=None)
    m.add_child(mc)
    m._repr_html_()

    out = m._parent.render()

    # We verify that imports
    assert ('<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.'
            'markercluster/0.4.0/leaflet.markercluster.js"></script>') in out
    assert ('<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/'
            'libs/leaflet.markercluster/0.4.0/MarkerCluster.css" />') in out
    assert ('<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/'
            'libs/leaflet.markercluster/0.4.0/MarkerCluster.Default.css" />'
            ) in out

    # Verify the script part is okay.
    tmpl = Template("""
        {% macro script(this, kwargs) %}
        (function() {
            var data = {{this._data}};
            var map = {{this._parent.get_name()}};
            var cluster = L.markerClusterGroup();
            {{this._callback}}

            for (var i = 0; i < data.length; i++) {
                var row = data[i];
                var marker = callback(row, popup='names');
                marker.addTo(cluster);
            }

            cluster.addTo(map);
        })();
        {% endmacro %}
    """)

    assert ''.join(tmpl.render(this=mc).split()) in ''.join(out.split())
<commit_msg>Update marker cluster version number to get tests passing<commit_after># -*- coding: utf-8 -*-
"""
Test FastMarkerCluster
------------------
"""

from jinja2 import Template
import numpy as np

import folium
from folium import plugins

def test_fast_marker_cluster():
    n = 100
    np.random.seed(seed=26082009)
    data = np.array([
        np.random.uniform(low=35, high=60, size=n),   # Random latitudes.
        np.random.uniform(low=-12, high=30, size=n),  # Random longitudes.
        range(n),                                     # Popups.
    ]).tolist()
    m = folium.Map([45., 3.], zoom_start=4)
    mc = plugins.FastMarkerCluster(data, callback=None)
    m.add_child(mc)
    m._repr_html_()

    out = m._parent.render()
    print(out)
    # We verify that imports
    assert ('<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.'
            'markercluster/1.0.0/leaflet.markercluster.js"></script>') in out
    assert ('<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/'
            'libs/leaflet.markercluster/1.0.0/MarkerCluster.css" />') in out
    assert ('<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/'
            'libs/leaflet.markercluster/1.0.0/MarkerCluster.Default.css" />'
            ) in out

    # Verify the script part is okay.
    tmpl = Template("""
        {% macro script(this, kwargs) %}
        (function() {
            var data = {{this._data}};
            var map = {{this._parent.get_name()}};
            var cluster = L.markerClusterGroup();
            {{this._callback}}

            for (var i = 0; i < data.length; i++) {
                var row = data[i];
                var marker = callback(row, popup='names');
                marker.addTo(cluster);
            }

            cluster.addTo(map);
        })();
        {% endmacro %}
    """)

    assert ''.join(tmpl.render(this=mc).split()) in ''.join(out.split())
<|endoftext|>


<commit_before>
print ("Герой нашей сегодняшней программы - Аврора Дюпен")
print ("Под каким именем мы знаем этого человека? Ваш ответ:")
name = input ('Под каким именем мы знаем этого человка? Ваш ответ: ')
print ("Всё верно: Аврора Дюпен - " + name + "!")
<commit_msg>Update task-3-46.py<commit_after>
print ("Герой нашей сегодняшней программы - Аврора Дюпен")
name = input ('Под каким именем мы знаем этого человка? Ваш ответ: ')
print ("Всё верно: Аврора Дюпен - " + name + "!")
<|endoftext|>


<commit_before>import random

greeting_words = [
    "hi","hey","hello","yo","yoyo","greetings","hiya",
    "howdy","sup","wasup","whatsup","whats up","what's up"
]
leaving_words = [
    "bye","cya","seeya","later","bai","im out"
]
laughing_words = [
    "lol","lolz","hehe","haha","i don't get it","rofl",
    "lmfao","lmfao","jokes are funny","not funny"
]
reactions = [
    "How about never? Is never good for you?",
    "You sound reasonable... Time to up the medication.",
    "I see you've set aside this special time to humiliate yourself in public.",
    "I don't work here. I'm a consultant.",
    "Ahhh... I see the screw-up fairy has visited us again",
    "I don't know what your problem is, but I'll bet it's hard to pronounce.",
    "I like you. You remind me of when I was young and stupid.",
    "You are validating my inherent mistrust of strangers.",
    "I'll try being nicer if you'll try being smarter.",
    "I'm out of my mind, but feel free to leave a message.",
    "I'm really easy to get along with once you people learn to worship me.",
    "It sounds like English, but I can't understand a word you're saying.",
    "I can see your point, but I still think you're full of it.",
    "What am I? Flypaper for freaks!?",
    "Any connection between your reality and mine is purely coincidental.",
    "I have plenty of talent and vision. I just don't give a damn.",
    "I'm already visualizing the duct tape over your mouth.",
    "Your teeth are brighter than you is.",
    "No, my powers can only be used for good.",
    "We're all refreshed and challenged by your unique point of view.",
    "The fact that no one understands you doesn't mean you're an artist.",
    "I will always cherish the initial misconceptions I had about you.",
    "Who me? I just wander from room to room.",
    "I'm not being rude. You're just insignificant.",
    "It's a thankless job, but I've got a lot of Karma to burn off."
]

def greet_msg(ctx, serv, nick, dest, msg):
    msg = msg.lower()
    msg = msg.replace("?", " ").replace("!", " ").replace(".", " ")

    for w in greeting_words:
        if msg == w:
            serv.say(dest, random.choice(greeting_words))
            return True
        elif msg.startswith(w) or msg.endswith(w):
            if NICK in msg:
                serv.say(dest, nick+": "+random.choice(greeting_words))
                return True

    for w in leaving_words:
        if msg == w or msg.startswith(w+" ") or msg.endswith(" "+w):
            serv.say(dest, random.choice(leaving_words))
            return True

    #for w in laughing_words:
    #    if msg == w or msg.startswith(w+" ") or msg.endswith(" "+w):
    #        if random.random() > 0.50:
    #            serv.say(dest, random.choice(laughing_words))
    #        return True

    if NICK in msg:
        serv.say(dest, random.choice(reactions))
        return True

def greet_quit(ctx, serv):
    for chan in CHANS:
        serv.say(chan, random.choice(leaving_words))

serv.on_msg.connect(greet_msg, "greet")
serv.on_quit.connect(greet_quit, "greet")

<commit_msg>greet.py rate limiter<commit_after>import random,time

greeting_words = [
    "hi","hey","hello","yo","yoyo","greetings","hiya",
    "howdy","sup","wasup","whatsup","whats up","what's up"
]
leaving_words = [
    "bye","cya","seeya","later","bai","im out"
]
laughing_words = [
    "lol","lolz","hehe","haha","i don't get it","rofl",
    "lmfao","lmfao","jokes are funny","not funny"
]
reactions = [
    "How about never? Is never good for you?",
    "You sound reasonable... Time to up the medication.",
    "I see you've set aside this special time to humiliate yourself in public.",
    "I don't work here. I'm a consultant.",
    "Ahhh... I see the screw-up fairy has visited us again",
    "I don't know what your problem is, but I'll bet it's hard to pronounce.",
    "I like you. You remind me of when I was young and stupid.",
    "You are validating my inherent mistrust of strangers.",
    "I'll try being nicer if you'll try being smarter.",
    "I'm out of my mind, but feel free to leave a message.",
    "I'm really easy to get along with once you people learn to worship me.",
    "It sounds like English, but I can't understand a word you're saying.",
    "I can see your point, but I still think you're full of it.",
    "What am I? Flypaper for freaks!?",
    "Any connection between your reality and mine is purely coincidental.",
    "I have plenty of talent and vision. I just don't give a damn.",
    "I'm already visualizing the duct tape over your mouth.",
    "Your teeth are brighter than you is.",
    "No, my powers can only be used for good.",
    "We're all refreshed and challenged by your unique point of view.",
    "The fact that no one understands you doesn't mean you're an artist.",
    "I will always cherish the initial misconceptions I had about you.",
    "Who me? I just wander from room to room.",
    "I'm not being rude. You're just insignificant.",
    "It's a thankless job, but I've got a lot of Karma to burn off."
]

last_greet = 0.0
last_part = 0.0
throttle = 5.0

def greet_msg(ctx, serv, nick, dest, msg):
    global last_greet
    global last_part
    msg = msg.lower()
    msg = msg.replace("?", " ").replace("!", " ").replace(".", " ")
    t = time.time()

    for w in greeting_words:
        if msg == w:
            if t - last_greet > throttle:
                last_greet = t
                serv.say(dest, random.choice(greeting_words))
            return True
        elif msg.startswith(w) or msg.endswith(w):
            if NICK in msg:
                if t - last_greet > throttle:
                    last_greet = t
                    serv.say(dest, random.choice(leaving_words))
                serv.say(dest, nick+": "+random.choice(greeting_words))
                last_greet = t
                return True

    for w in leaving_words:
        if msg == w or msg.startswith(w+" ") or msg.endswith(" "+w):
            if t - last_part > throttle:
                last_part = t
                serv.say(dest, random.choice(leaving_words))
            return True

    #for w in laughing_words:
    #    if msg == w or msg.startswith(w+" ") or msg.endswith(" "+w):
    #        if random.random() > 0.50:
    #            serv.say(dest, random.choice(laughing_words))
    #        return True

    if NICK in msg:
        serv.say(dest, random.choice(reactions))
        return True

def greet_quit(ctx, serv):
    for chan in CHANS:
        serv.say(chan, random.choice(leaving_words))

serv.on_msg.connect(greet_msg, "greet")
serv.on_quit.connect(greet_quit, "greet")

<|endoftext|>