<commit_before># -*- coding: utf-8 -*-

import werobot

import inspect
import hashlib
import logging

from bottle import Bottle, request, response, abort, template

from .parser import parse_user_msg
from .reply import create_reply
from .utils import py3k

__all__ = ['BaseRoBot', 'WeRoBot']


class BaseRoBot(object):
    message_types = ['subscribe', 'unsubscribe', 'click',  # event
                     'text', 'image', 'link', 'location', 'voice']

    def __init__(self, token=None, logger=None, enable_session=False,
                 session_storage=None):
        self._handlers = dict((k, []) for k in self.message_types)
        self._handlers['all'] = []
        self.token = token
        if logger is None:
            logger = logging.getLogger("WeRoBot")
        self.logger = logger

        if enable_session and session_storage is None:
            from .session.filestorage import FileStorage
            session_storage = FileStorage()
        self.session_storage = session_storage

    def handler(self, f):
        """
        Decorator to add a handler function for every messages
        """
        self.add_handler(f, type='all')
        return f

    def text(self, f):
        """
        Decorator to add a handler function for ``text`` messages
        """
        self.add_handler(f, type='text')
        return f

    def image(self, f):
        """
        Decorator to add a handler function for ``image`` messages
        """
        self.add_handler(f, type='image')
        return f

    def location(self, f):
        """
        Decorator to add a handler function for ``location`` messages
        """
        self.add_handler(f, type='location')
        return f

    def link(self, f):
        """
        Decorator to add a handler function for ``link`` messages
        """
        self.add_handler(f, type='link')
        return f

    def subscribe(self, f):
        """
        Decorator to add a handler function for ``subscribe event`` messages
        """
        self.add_handler(f, type='subscribe')

    def unsubscribe(self, f):
        """
        Decorator to add a handler function for ``unsubscribe event`` messages
        """
        self.add_handler(f, type='unsubscribe')

    def click(self, f):
        """
        Decorator to add a handler function for ``click`` messages
        """
        self.add_handler(f, type='click')
        
    def key_click(self, key):
        """
        Shortcut for ``click`` messages
        @key_click('KEYNAME') for special key on click event
        """
        def d(f):
            argc = len(inspect.getargspec(f).args)
            @self.click
            def onclick(message, session):
                if message.key == key :
                    return f(*[message, session][:argc])

        return d

    def voice(self, f):
        """
        Decorator to add a handler function for ``voice`` messages
        """
        self.add_handler(f, type='voice')

    def add_handler(self, func, type='all'):
        """
        Add a handler function for messages of given type.
        """
        if not inspect.isfunction(func) or len(inspect.getargspec(func).args) > 2:
            raise TypeError

        self._handlers[type].append(func)

    def get_handlers(self, type):
        return self._handlers[type] + self._handlers['all']

    def get_reply(self, message):
        """
        Return the raw xml reply for the given message.
        """
        session_storage = self.session_storage

        id = None
        session = None
        if session_storage:
            if hasattr(message, "source"):
                id = message.source
                session = session_storage[id]

        handlers = self.get_handlers(message.type)
        try:
            for handler in handlers:
                
                argc = len(inspect.getargspec(handler).args)

                reply = handler(*[message, session][:argc])

                if session_storage and id:
                    session_storage[id] = session

                if reply:
                    return reply
        except:
            self.logger.warning("Catch an exception", exc_info=True)

    def check_signature(self, timestamp, nonce, signature):
        sign = [self.token, timestamp, nonce]
        sign.sort()
        sign = ''.join(sign)
        if py3k:
            sign = sign.encode()
        sign = hashlib.sha1(sign).hexdigest()
        return sign == signature


class WeRoBot(BaseRoBot):

    ERROR_PAGE_TEMPLATE = """
    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf8" />
            <title>Error: {{e.status}}</title>
            <style type="text/css">
              html {background-color: #eee; font-family: sans;}
              body {background-color: #fff; border: 1px solid #ddd;
                    padding: 15px; margin: 15px;}
              pre {background-color: #eee; border: 1px solid #ddd; padding: 5px;}
            </style>
        </head>
        <body>
            <h1>Error: {{e.status}}</h1>
            <p>微信机器人不可以通过 GET 方式直接进行访问。</p>
            <p>想要使用本机器人，请在微信后台中将 URL 设置为 <pre>{{request.url}}</pre> 并将 Token 值设置正确。</p>

            <p>如果你仍有疑问，请<a href="http://werobot.readthedocs.org/en/%s/">阅读文档</a>
        </body>
    </html>
    """ % werobot.__version__

    @property
    def wsgi(self):
        if not self._handlers:
            raise
        app = Bottle()

        @app.get('/<t:path>')
        def echo(t):
            if not self.check_signature(
                request.query.timestamp,
                request.query.nonce,
                request.query.signature
            ):
                return abort(403)
            return request.query.echostr

        @app.post('/<t:path>')
        def handle(t):
            if not self.check_signature(
                request.query.timestamp,
                request.query.nonce,
                request.query.signature
            ):
                return abort(403)

            body = request.body.read()
            message = parse_user_msg(body)
            logging.info("Receive message %s" % message)
            reply = self.get_reply(message)
            if not reply:
                self.logger.warning("No handler responded message %s"
                                    % message)
                return ''
            response.content_type = 'application/xml'
            return create_reply(reply, message=message)

        @app.error(403)
        def error403(error):
            return template(self.ERROR_PAGE_TEMPLATE,
                            e=error, request=request)

        return app

    def run(self, server='auto', host='127.0.0.1',
            port=8888, enable_pretty_logging=True):
        if enable_pretty_logging:
            from werobot.utils import enable_pretty_logging
            enable_pretty_logging(self.logger)
        self.wsgi.run(server=server, host=host, port=port)
<commit_msg>Use ``config`` to manage Configurations in WeRoBot<commit_after># -*- coding: utf-8 -*-

import werobot

import inspect
import hashlib
import logging

from bottle import Bottle, request, response, abort, template

from werobot.config import Config
from werobot.parser import parse_user_msg
from werobot.reply import create_reply
from werobot.utils import py3k

__all__ = ['BaseRoBot', 'WeRoBot']


class BaseRoBot(object):
    message_types = ['subscribe', 'unsubscribe', 'click',  # event
                     'text', 'image', 'link', 'location', 'voice']

    def __init__(self, token=None, logger=None, enable_session=False,
                 session_storage=None):
        self._handlers = dict((k, []) for k in self.message_types)
        self._handlers['all'] = []
        if logger is None:
            logger = logging.getLogger("WeRoBot")
        self.logger = logger

        if enable_session and session_storage is None:
            from .session.filestorage import FileStorage
            session_storage = FileStorage()
        self.config = Config(
            TOKEN=token,
            SESSION_STORAGE=session_storage,
        )

    def handler(self, f):
        """
        Decorator to add a handler function for every messages
        """
        self.add_handler(f, type='all')
        return f

    def text(self, f):
        """
        Decorator to add a handler function for ``text`` messages
        """
        self.add_handler(f, type='text')
        return f

    def image(self, f):
        """
        Decorator to add a handler function for ``image`` messages
        """
        self.add_handler(f, type='image')
        return f

    def location(self, f):
        """
        Decorator to add a handler function for ``location`` messages
        """
        self.add_handler(f, type='location')
        return f

    def link(self, f):
        """
        Decorator to add a handler function for ``link`` messages
        """
        self.add_handler(f, type='link')
        return f

    def subscribe(self, f):
        """
        Decorator to add a handler function for ``subscribe event`` messages
        """
        self.add_handler(f, type='subscribe')

    def unsubscribe(self, f):
        """
        Decorator to add a handler function for ``unsubscribe event`` messages
        """
        self.add_handler(f, type='unsubscribe')

    def click(self, f):
        """
        Decorator to add a handler function for ``click`` messages
        """
        self.add_handler(f, type='click')
        
    def key_click(self, key):
        """
        Shortcut for ``click`` messages
        @key_click('KEYNAME') for special key on click event
        """
        def d(f):
            argc = len(inspect.getargspec(f).args)
            @self.click
            def onclick(message, session):
                if message.key == key :
                    return f(*[message, session][:argc])

        return d

    def voice(self, f):
        """
        Decorator to add a handler function for ``voice`` messages
        """
        self.add_handler(f, type='voice')

    def add_handler(self, func, type='all'):
        """
        Add a handler function for messages of given type.
        """
        if not inspect.isfunction(func) or len(inspect.getargspec(func).args) > 2:
            raise TypeError

        self._handlers[type].append(func)

    def get_handlers(self, type):
        return self._handlers[type] + self._handlers['all']

    def get_reply(self, message):
        """
        Return the raw xml reply for the given message.
        """
        session_storage = self.session_storage

        id = None
        session = None
        if session_storage:
            if hasattr(message, "source"):
                id = message.source
                session = session_storage[id]

        handlers = self.get_handlers(message.type)
        try:
            for handler in handlers:
                
                argc = len(inspect.getargspec(handler).args)

                reply = handler(*[message, session][:argc])

                if session_storage and id:
                    session_storage[id] = session

                if reply:
                    return reply
        except:
            self.logger.warning("Catch an exception", exc_info=True)

    def check_signature(self, timestamp, nonce, signature):
        sign = [self.token, timestamp, nonce]
        sign.sort()
        sign = ''.join(sign)
        if py3k:
            sign = sign.encode()
        sign = hashlib.sha1(sign).hexdigest()
        return sign == signature

    def __getattribute__(self, item):
        try:
            return object.__getattribute__(self, item)
        except AttributeError as e:
            if item in ["token", "session_storage"]:
                return self.config[item.upper()]
            raise e

    def __setattr__(self, key, value):
        if key in ["token", "session_storage"]:
            self.config[key.upper()] = value
        else:
            object.__setattr__(self, key, value)


class WeRoBot(BaseRoBot):

    ERROR_PAGE_TEMPLATE = """
    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf8" />
            <title>Error: {{e.status}}</title>
            <style type="text/css">
              html {background-color: #eee; font-family: sans;}
              body {background-color: #fff; border: 1px solid #ddd;
                    padding: 15px; margin: 15px;}
              pre {background-color: #eee; border: 1px solid #ddd; padding: 5px;}
            </style>
        </head>
        <body>
            <h1>Error: {{e.status}}</h1>
            <p>微信机器人不可以通过 GET 方式直接进行访问。</p>
            <p>想要使用本机器人，请在微信后台中将 URL 设置为 <pre>{{request.url}}</pre> 并将 Token 值设置正确。</p>

            <p>如果你仍有疑问，请<a href="http://werobot.readthedocs.org/en/%s/">阅读文档</a>
        </body>
    </html>
    """ % werobot.__version__

    @property
    def wsgi(self):
        if not self._handlers:
            raise
        app = Bottle()

        @app.get('/<t:path>')
        def echo(t):
            if not self.check_signature(
                request.query.timestamp,
                request.query.nonce,
                request.query.signature
            ):
                return abort(403)
            return request.query.echostr

        @app.post('/<t:path>')
        def handle(t):
            if not self.check_signature(
                request.query.timestamp,
                request.query.nonce,
                request.query.signature
            ):
                return abort(403)

            body = request.body.read()
            message = parse_user_msg(body)
            logging.info("Receive message %s" % message)
            reply = self.get_reply(message)
            if not reply:
                self.logger.warning("No handler responded message %s"
                                    % message)
                return ''
            response.content_type = 'application/xml'
            return create_reply(reply, message=message)

        @app.error(403)
        def error403(error):
            return template(self.ERROR_PAGE_TEMPLATE,
                            e=error, request=request)

        return app

    def run(self, server='auto', host='127.0.0.1',
            port=8888, enable_pretty_logging=True):
        if enable_pretty_logging:
            from werobot.utils import enable_pretty_logging
            enable_pretty_logging(self.logger)
        self.wsgi.run(server=server, host=host, port=port)

{'fraction_non_alphanumeric': 0.11117245186667586, 'fraction_numerical': 0.005106617900765993, 'mean_word_length': 1.7853161637516817, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 4}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4465098', 'n_tokens_mistral': 4221, 'n_tokens_neox': 4045, 'n_words': 1035}