コード例 #1
0
ファイル: run.py プロジェクト: TijeeCorp/zulip
def run_message_handler_for_bot(lib_module, quiet, config_file):
    # Make sure you set up your ~/.zuliprc
    client = Client(config_file=config_file)
    restricted_client = RestrictedClient(client)

    message_handler = lib_module.handler_class()

    class StateHandler(object):
        def __init__(self):
            self.state = None

        def set_state(self, state):
            self.state = state

        def get_state(self):
            return self.state

    state_handler = StateHandler()

    if not quiet:
        print(message_handler.usage())

    def handle_message(message):
        logging.info('waiting for next message')
        if message_handler.triage_message(message=message):
            message_handler.handle_message(
                message=message,
                client=restricted_client,
                state_handler=state_handler
                )

    logging.info('starting message handling...')
    client.call_on_each_message(handle_message)
コード例 #2
0
ファイル: bot_lib.py プロジェクト: christi3k/zulip
def run_message_handler_for_bot(lib_module, quiet, config_file):
    # Make sure you set up your ~/.zuliprc
    client = Client(config_file=config_file)
    restricted_client = BotHandlerApi(client)

    message_handler = lib_module.handler_class()

    state_handler = StateHandler()

    if not quiet:
        print(message_handler.usage())

    def extract_query_without_mention(message, client):
        """
        If the bot is the first @mention in the message, then this function returns
        the message with the bot's @mention removed.  Otherwise, it returns None.
        """
        bot_mention = r'^@(\*\*{0}\*\*)'.format(client.full_name)
        start_with_mention = re.compile(bot_mention).match(message['content'])
        if start_with_mention is None:
            return None
        query_without_mention = message['content'][len(start_with_mention.group()):]
        return query_without_mention.lstrip()

    def is_private(message, client):
        # bot will not reply if the sender name is the same as the bot name
        # to prevent infinite loop
        if message['type'] == 'private':
            return client.full_name != message['sender_full_name']
        return False

    def handle_message(message):
        logging.info('waiting for next message')

        # is_mentioned is true if the bot is mentioned at ANY position (not necessarily
        # the first @mention in the message).
        is_mentioned = message['is_mentioned']
        is_private_message = is_private(message, restricted_client)

        # Strip at-mention botname from the message
        if is_mentioned:
            # message['content'] will be None when the bot's @-mention is not at the beginning.
            # In that case, the message shall not be handled.
            message['content'] = extract_query_without_mention(message=message, client=restricted_client)
            if message['content'] is None:
                return

        if is_private_message or is_mentioned:
            message_handler.handle_message(
                message=message,
                client=restricted_client,
                state_handler=state_handler
            )

    signal.signal(signal.SIGINT, exit_gracefully)

    logging.info('starting message handling...')
    client.call_on_each_message(handle_message)
コード例 #3
0
ファイル: run.py プロジェクト: ahmadassaf/Zulip
def run_message_handler_for_bot(lib_module, quiet, config_file):
    # Make sure you set up your ~/.zuliprc
    client = Client(config_file=config_file)
    restricted_client = RestrictedClient(client)

    message_handler = lib_module.handler_class()

    if not quiet:
        print(message_handler.usage())

    def handle_message(message):
        logging.info('waiting for next message')
        if message_handler.triage_message(message=message):
            message_handler.handle_message(
                message=message,
                client=restricted_client)

    logging.info('starting message handling...')
    client.call_on_each_message(handle_message)
コード例 #4
0
ファイル: lib.py プロジェクト: Juljan/python-zulip-api
    def __init__(
        self,
        client: Client,
        root_dir: str,
        bot_details: Dict[str, Any],
        bot_config_file: Optional[str]=None,
        bot_config_parser: Optional[configparser.ConfigParser]=None,
    ) -> None:
        # Only expose a subset of our Client's functionality
        try:
            user_profile = client.get_profile()
        except ZulipError as e:
            print('''
                ERROR: {}

                Have you not started the server?
                Or did you mis-specify the URL?
                '''.format(e))
            sys.exit(1)

        if user_profile.get('result') == 'error':
            msg = user_profile.get('msg', 'unknown')
            print('''
                ERROR: {}
                '''.format(msg))
            sys.exit(1)

        self._rate_limit = RateLimit(20, 5)
        self._client = client
        self._root_dir = root_dir
        self.bot_details = bot_details
        self.bot_config_file = bot_config_file
        self._bot_config_parser = bot_config_parser
        self._storage = StateHandler(client)
        try:
            self.user_id = user_profile['user_id']
            self.full_name = user_profile['full_name']
            self.email = user_profile['email']
        except KeyError:
            logging.error('Cannot fetch user profile, make sure you have set'
                          ' up the zuliprc file correctly.')
            sys.exit(1)
コード例 #5
0
def add_alert_words(client: Client) -> None:
    word = ['foo', 'bar']

    result = client.add_alert_words(word)

    assert result['result'] == 'success'
コード例 #6
0
def get_user_agent(client: Client) -> None:

    result = client.get_user_agent()
    assert result.startswith("ZulipPython/")
コード例 #7
0
def test_user_not_authorized_error(nonadmin_client: Client) -> None:
    result = nonadmin_client.get_streams(include_all_active=True)

    validate_against_openapi_schema(result, "/rest-error-handling", "post",
                                    "400_2")
コード例 #8
0
def remove_alert_words(client: Client) -> None:
    word = ["foo"]

    result = client.remove_alert_words(word)

    assert result["result"] == "success"
コード例 #9
0
def get_alert_words(client: Client) -> None:
    result = client.get_alert_words()

    assert result["result"] == "success"
コード例 #10
0
ファイル: python_examples.py プロジェクト: twoconk/zulip
def get_subscribers(client: Client) -> None:

    result = client.get_subscribers(stream='new stream')
    assert result['subscribers'] == ['*****@*****.**', '*****@*****.**']
コード例 #11
0
def test_invalid_api_key(client_with_invalid_key: Client) -> None:
    result = client_with_invalid_key.get_subscriptions()
    validate_against_openapi_schema(result, "/rest-error-handling", "post",
                                    "400_0")
コード例 #12
0
ファイル: queue_processors.py プロジェクト: spadejac/zulip
 def get_bot_api_client(self, user_profile):
     # type: (UserProfile) -> BotHandlerApi
     raw_client = Client(email=str(user_profile.email),
                         api_key=str(user_profile.api_key),
                         site=str(user_profile.realm.uri))
     return BotHandlerApi(raw_client)
コード例 #13
0
ファイル: lib.py プロジェクト: ejfwebserve/python-zulip-api
def run_message_handler_for_bot(lib_module, quiet, config_file, bot_name):
    # type: (Any, bool, str, str) -> Any
    #
    # lib_module is of type Any, since it can contain any bot's
    # handler class. Eventually, we want bot's handler classes to
    # inherit from a common prototype specifying the handle_message
    # function.
    #
    # Set default bot_details, then override from class, if provided
    bot_details = {
        'name': bot_name.capitalize(),
        'description': "",
    }
    bot_details.update(getattr(lib_module.handler_class, 'META', {}))
    # Make sure you set up your ~/.zuliprc

    client_name = "Zulip{}Bot".format(bot_name.capitalize())

    try:
        client = Client(config_file=config_file, client=client_name)
    except configparser.Error as e:
        file_contents = open(config_file).read()
        print('\nERROR: {} seems to be broken:\n\n{}'.format(
            config_file, file_contents))
        print('\nMore details here:\n\n' + str(e) + '\n')
        sys.exit(1)

    bot_dir = os.path.dirname(lib_module.__file__)
    restricted_client = ExternalBotHandler(client, bot_dir, bot_details)

    message_handler = lib_module.handler_class()
    if hasattr(message_handler, 'initialize'):
        message_handler.initialize(bot_handler=restricted_client)

    if not quiet:
        print("Running {} Bot:".format(bot_details['name']))
        if bot_details['description'] != "":
            print("\n\t{}".format(bot_details['description']))
        print(message_handler.usage())

    def handle_message(message, flags):
        # type: (Dict[str, Any], List[str]) -> None
        logging.info('waiting for next message')

        # `mentioned` will be in `flags` if the bot is mentioned at ANY position
        # (not necessarily the first @mention in the message).
        is_mentioned = 'mentioned' in flags
        is_private_message = is_private_message_from_another_user(
            message, restricted_client.user_id)

        # Strip at-mention botname from the message
        if is_mentioned:
            # message['content'] will be None when the bot's @-mention is not at the beginning.
            # In that case, the message shall not be handled.
            message['content'] = extract_query_without_mention(
                message=message, client=restricted_client)
            if message['content'] is None:
                return

        if is_private_message or is_mentioned:
            message_handler.handle_message(message=message,
                                           bot_handler=restricted_client)

    signal.signal(signal.SIGINT, exit_gracefully)

    logging.info('starting message handling...')

    def event_callback(event):
        # type: (Dict[str, Any]) -> None
        if event['type'] == 'message':
            handle_message(event['message'], event['flags'])

    client.call_on_each_event(event_callback, ['message'])
コード例 #14
0
def run_message_handler_for_bot(
    lib_module: Any,
    quiet: bool,
    config_file: str,
    bot_config_file: str,
    bot_name: str,
    bot_source: str,
) -> Any:
    """
    lib_module is of type Any, since it can contain any bot's
    handler class. Eventually, we want bot's handler classes to
    inherit from a common prototype specifying the handle_message
    function.

    Set default bot_details, then override from class, if provided
    """
    bot_details = {
        "name": bot_name.capitalize(),
        "description": "",
    }
    bot_details.update(getattr(lib_module.handler_class, "META", {}))
    # Make sure you set up your ~/.zuliprc

    client_name = f"Zulip{bot_name.capitalize()}Bot"

    try:
        client = Client(config_file=config_file, client=client_name)
    except configparser.Error as e:
        display_config_file_errors(str(e), config_file)
        sys.exit(1)

    bot_dir = os.path.dirname(lib_module.__file__)
    restricted_client = ExternalBotHandler(client, bot_dir, bot_details,
                                           bot_config_file)

    message_handler = prepare_message_handler(bot_name, restricted_client,
                                              lib_module)

    if not quiet:
        print("Running {} Bot (from {}):".format(bot_details["name"],
                                                 bot_source))
        if bot_details["description"] != "":
            print("\n\t{}".format(bot_details["description"]))
        if hasattr(message_handler, "usage"):
            print(message_handler.usage())
        else:
            print(
                f"WARNING: {bot_name} is missing usage handler, please add one eventually"
            )

    def handle_message(message: Dict[str, Any], flags: List[str]) -> None:
        logging.info("waiting for next message")
        # `mentioned` will be in `flags` if the bot is mentioned at ANY position
        # (not necessarily the first @mention in the message).
        is_mentioned = "mentioned" in flags
        is_private_message = is_private_message_but_not_group_pm(
            message, restricted_client)

        # Provide bots with a way to access the full, unstripped message
        message["full_content"] = message["content"]
        # Strip at-mention botname from the message
        if is_mentioned:
            # message['content'] will be None when the bot's @-mention is not at the beginning.
            # In that case, the message shall not be handled.
            message["content"] = extract_query_without_mention(
                message=message, client=restricted_client)
            if message["content"] is None:
                return

        if is_private_message or is_mentioned:
            message_handler.handle_message(message=message,
                                           bot_handler=restricted_client)

    signal.signal(signal.SIGINT, exit_gracefully)

    logging.info("starting message handling...")

    def event_callback(event: Dict[str, Any]) -> None:
        if event["type"] == "message":
            handle_message(event["message"], event["flags"])

    client.call_on_each_event(event_callback, ["message"])
コード例 #15
0
def test_invalid_api_key(client_with_invalid_key: Client) -> None:
    result = client_with_invalid_key.list_subscriptions()
    validate_against_openapi_schema(result, '/rest-error-handling', 'post', '400_0')
コード例 #16
0
class ZulipBot:

	def __init__(self):
		self.client = Client(email = os.environ['ZULIP_USERNAME'], api_key = os.environ['ZULIP_API_KEY'])
		self.subscribe_streams()


	def subscribe_streams(self):
		response = get('https://api.zulip.com/v1/streams', auth=(os.environ['ZULIP_USERNAME'], os.environ['ZULIP_API_KEY']))
		
		if response.status_code == 200:
			streams = [{'name': stream['name']} for stream in response.json()['streams']]
			self.client.add_subscriptions(streams)

		else:
			raise RuntimeError(response)


	##Function to check for any messages

	def read_message(self, msg):
		content = msg['content'].split(',')
		sender_email = msg['sender_email']

		if sender_email == os.environ['ZULIP_USERNAME']:
			return
		if content[0].upper() in ['RUNNING', 'RUNNINGBOT', '@**RUNNING**']:
			return_info = self.find_runs(content)
			if return_info is None:
				self.send_message("No results", msg)
			else:
				[self.send_message(run, msg) for run in return_info]
		else:
			return 


	def find_runs(self, content):
		run_info = sorted(content[1:])
		
		if len(run_info) == 2:
			run_info.append('min=1')
		elif len(run_info) == 1:
			run_info.extend(['max=5.5', 'min=1'])

		run_params = [r.split("=")[-1] for r in run_info]

		get_coords = GoogleRequests()
		lat, lon = get_coords.get_geocode(run_params[0])

		new_req = MMFRouteAPI()
		json_data = new_req.get_routes(lat, lon, run_params[1], run_params[2])
		list_runs = new_req.list_runs(json_data)
		
		if len(list_runs) < 1:
			return None

		return list_runs



	def send_message(self, return_content, msg):
		links = ["Run Name: ", return_content[0], "Distance (miles): ", return_content[1], "Link:", return_content[-1]]
		return_str = " ".join(links)
		if msg['type'] == 'stream':
			self.client.send_message({
                'type': 'stream',
                'subject': 'RUNNINGBOT',
                'to': msg['display_recipient'],
                'content': return_str})
            
		elif msg['type'] == 'private':
			self.client.send_message({
                    'type': 'private',
                    'to': msg['sender_email'],
                    'content': return_str })      
コード例 #17
0
def set_typing_status(client: Client) -> None:
    ensure_users([10, 11], ["hamlet", "iago"])

    # {code_example|start}
    # The user has started to type in the group PM with Iago and Polonius
    user_id1 = 10
    user_id2 = 11

    request = {
        "op": "start",
        "to": [user_id1, user_id2],
    }
    result = client.set_typing_status(request)

    # {code_example|end}

    validate_against_openapi_schema(result, "/typing", "post", "200")

    # {code_example|start}
    # The user has finished typing in the group PM with Iago and Polonius
    user_id1 = 10
    user_id2 = 11

    request = {
        "op": "stop",
        "to": [user_id1, user_id2],
    }
    result = client.set_typing_status(request)

    # {code_example|end}

    validate_against_openapi_schema(result, "/typing", "post", "200")

    # {code_example|start}
    # The user has started to type in topic "typing status" of stream "Denmark"
    stream_id = client.get_stream_id("Denmark")["stream_id"]
    topic = "typing status"

    request = {
        "type": "stream",
        "op": "start",
        "to": [stream_id],
        "topic": topic,
    }
    result = client.set_typing_status(request)

    # {code_example|end}

    validate_against_openapi_schema(result, "/typing", "post", "200")

    # {code_example|start}
    # The user has finished typing in topic "typing status" of stream "Denmark"
    stream_id = client.get_stream_id("Denmark")["stream_id"]
    topic = "typing status"

    request = {
        "type": "stream",
        "op": "stop",
        "to": [stream_id],
        "topic": topic,
    }
    result = client.set_typing_status(request)

    # {code_example|end}

    validate_against_openapi_schema(result, "/typing", "post", "200")
コード例 #18
0
ファイル: python_examples.py プロジェクト: twoconk/zulip
def update_presence(client: Client) -> None:
    request = {'status': 'active', 'ping_only': False, 'new_user_input': False}

    result = client.update_presence(request)

    assert result['result'] == 'success'
コード例 #19
0
class ZulipBot(object):
    def __init__(self, name, stream):
        self._client = Client()
        self._name = name
        self._self_short_name = u'{name}-bot'.format(name=self._name)
        self._self_mention = self._format_mention(self._name)
        self._stream = stream
        self._topic_router = {}
        self._command_handlers = {
            'hello': self._hello_command_handler,
            'help': self._help_command_handler,
        }

    def run(self):
        self._client.call_on_each_message(self._message_handler)

    def send_public_message(self, content, topic, stream=None):
        message = {
            'type': 'stream',
            'to': stream or self._stream,
            'content': content,
            'topic': topic,
        }
        self._client.send_message(message)

    def send_private_message(self, content, address):
        message = {'type': 'private', 'to': address, 'content': content}
        self._client.send_message(message)

    def send_reply(self, content, message):
        """Send content as reply to message."""
        if self._is_private_message(message):
            self.send_private_message(content, message.get('sender_email'))
        else:
            self.send_public_message(content, message.get('subject'),
                                     message.get('stream'))

    @staticmethod
    def _format_mention(name):
        return u'@**{name}**'.format(name=name)

    def _is_self_sent(self, message):
        return message.get('sender_short_name') == self._self_short_name

    def _is_self_mention(self, message):
        return message.get('content').find(self._self_mention) != -1

    def _strip_self_mention(self, content):
        return content.replace(self._self_mention, '')

    @staticmethod
    def _is_private_message(message):
        return message.get('type') == 'private'

    def _message_handler(self, message):
        is_relevant_message = not self._is_self_sent(message) and (
            self._is_self_mention(message)
            or self._is_private_message(message))
        if is_relevant_message:
            topic = message.get('subject')
            self._topic_router.get(topic, self._default_router)(message)

    def _default_router(self, message):
        commands = [
            cmd for cmd in self._strip_self_mention(message.get(
                'content')).split(' ') if cmd
        ]
        self._command_handlers.get(commands[0],
                                   self._default_command_handler)(commands[1:],
                                                                  message)

    def _default_command_handler(self, subcommands, message):
        reply = (u'I did not understand the message:\n'
                 u'```quote\n'
                 u'{content}\n'
                 u'```\n'
                 u'For a list of recognized commands, send `help`.').format(
                     content=message.get('content'))
        self.send_reply(reply, message)

    def _help_command_handler(self, subcommands, message):
        """Get help about recognized commands."""
        if subcommands and subcommands[0] in self._command_handlers:
            command = subcommands[0]
            reply = u'*{command}*: {desc}'.format(
                command=command, desc=self._command_handlers[command].__doc__)
        else:
            reply = [(u'**Supported commands**\n'
                      u'\n'
                      u'Command|Description\n'
                      u'-------|-----------')]
            reply.extend(
                u'{cmd}|{desc}'.format(cmd=k, desc=v.__doc__.split('\n')[0])
                for (k, v) in iteritems(self._command_handlers))
            reply.append(u'\nSend `help {command}` for more information.')
            reply = '\n'.join(reply)

        self.send_reply(reply, message)

    def _hello_command_handler(self, subcommands, message):
        """Say hello."""
        sender = message.get('sender_short_name')
        reply = u'Hi {mention} :wave:'.format(
            mention=self._format_mention(sender))
        self.send_reply(reply, message)
コード例 #20
0
def run_message_handler_for_bot(
    lib_module: Any,
    quiet: bool,
    config_file: str,
    bot_config_file: str,
    bot_name: str,
) -> Any:
    """
    lib_module is of type Any, since it can contain any bot's
    handler class. Eventually, we want bot's handler classes to
    inherit from a common prototype specifying the handle_message
    function.

    Set default bot_details, then override from class, if provided
    """
    bot_details = {
        'name': bot_name.capitalize(),
        'description': "",
    }
    bot_details.update(getattr(lib_module.handler_class, 'META', {}))
    # Make sure you set up your ~/.zuliprc

    client_name = "Zulip{}Bot".format(bot_name.capitalize())

    try:
        client = Client(config_file=config_file, client=client_name)
    except configparser.Error as e:
        display_config_file_errors(str(e), config_file)
        sys.exit(1)

    bot_dir = os.path.dirname(lib_module.__file__)
    restricted_client = ExternalBotHandler(client, bot_dir, bot_details,
                                           bot_config_file)

    message_handler = prepare_message_handler(bot_name, restricted_client,
                                              lib_module)

    if not quiet:
        print("Running {} Bot:".format(bot_details['name']))
        if bot_details['description'] != "":
            print("\n\t{}".format(bot_details['description']))
        print(message_handler.usage())

    def handle_message(message: Dict[str, Any], flags: List[str]) -> None:
        logging.info('waiting for next message')
        # `mentioned` will be in `flags` if the bot is mentioned at ANY position
        # (not necessarily the first @mention in the message).
        is_mentioned = 'mentioned' in flags
        is_private_message = is_private_message_from_another_user(
            message, restricted_client.user_id)

        # Provide bots with a way to access the full, unstripped message
        message['full_content'] = message['content']
        # Strip at-mention botname from the message
        if is_mentioned:
            # message['content'] will be None when the bot's @-mention is not at the beginning.
            # In that case, the message shall not be handled.
            message['content'] = extract_query_without_mention(
                message=message, client=restricted_client)
            if message['content'] is None:
                return

        if is_private_message or is_mentioned:
            message_handler.handle_message(message=message,
                                           bot_handler=restricted_client)

    signal.signal(signal.SIGINT, exit_gracefully)

    logging.info('starting message handling...')

    def event_callback(event: Dict[str, Any]) -> None:
        if event['type'] == 'message':
            handle_message(event['message'], event['flags'])

    client.call_on_each_event(event_callback, ['message'])
コード例 #21
0
ファイル: bot_lib.py プロジェクト: JamesLinus/zulip
def run_message_handler_for_bot(lib_module, quiet, config_file):
    # type: (Any, bool, str) -> Any
    #
    # lib_module is of type Any, since it can contain any bot's
    # handler class. Eventually, we want bot's handler classes to
    # inherit from a common prototype specifying the handle_message
    # function.
    #
    # Make sure you set up your ~/.zuliprc
    client = Client(config_file=config_file)
    restricted_client = ExternalBotHandler(client)

    message_handler = lib_module.handler_class()
    if hasattr(message_handler, 'initialize'):
        message_handler.initialize(bot_handler=restricted_client)

    state_handler = StateHandler()

    if not quiet:
        print(message_handler.usage())

    def extract_query_without_mention(message, client):
        # type: (Dict[str, Any], ExternalBotHandler) -> str
        """
        If the bot is the first @mention in the message, then this function returns
        the message with the bot's @mention removed.  Otherwise, it returns None.
        """
        bot_mention = r'^@(\*\*{0}\*\*)'.format(client.full_name)
        start_with_mention = re.compile(bot_mention).match(message['content'])
        if start_with_mention is None:
            return None
        query_without_mention = message['content'][len(start_with_mention.group()):]
        return query_without_mention.lstrip()

    def is_private(message, client):
        # type: (Dict[str, Any], ExternalBotHandler) -> bool
        # bot will not reply if the sender name is the same as the bot name
        # to prevent infinite loop
        if message['type'] == 'private':
            return client.full_name != message['sender_full_name']
        return False

    def handle_message(message):
        # type: (Dict[str, Any]) -> None
        logging.info('waiting for next message')

        # is_mentioned is true if the bot is mentioned at ANY position (not necessarily
        # the first @mention in the message).
        is_mentioned = message['is_mentioned']
        is_private_message = is_private(message, restricted_client)

        # Strip at-mention botname from the message
        if is_mentioned:
            # message['content'] will be None when the bot's @-mention is not at the beginning.
            # In that case, the message shall not be handled.
            message['content'] = extract_query_without_mention(message=message, client=restricted_client)
            if message['content'] is None:
                return

        if is_private_message or is_mentioned:
            message_handler.handle_message(
                message=message,
                bot_handler=restricted_client,
                state_handler=state_handler
            )

    signal.signal(signal.SIGINT, exit_gracefully)

    logging.info('starting message handling...')
    client.call_on_each_message(handle_message)
コード例 #22
0
def add_alert_words(client: Client) -> None:
    word = ["foo", "bar"]

    result = client.add_alert_words(word)

    assert result["result"] == "success"
コード例 #23
0
def test_generated_curl_examples_for_success(client: Client,
                                             owner_client: Client) -> None:
    authentication_line = f"{client.email}:{client.api_key}"
    # A limited Markdown engine that just processes the code example syntax.
    realm = get_realm("zulip")
    md_engine = markdown.Markdown(extensions=[
        markdown_extension.makeExtension(api_url=realm.uri + "/api")
    ])

    # We run our curl tests in alphabetical order (except that we
    # delay the deactivate-user test to the very end), since we depend
    # on "add" tests coming before "remove" tests in some cases.  We
    # should try to either avoid ordering dependencies or make them
    # very explicit.
    for file_name in sorted(glob.glob("templates/zerver/api/*.md")):
        with open(file_name) as f:
            for line in f:
                # A typical example from the Markdown source looks like this:
                #     {generate_code_example(curl, ...}
                if not line.startswith("{generate_code_example(curl"):
                    continue
                # To do an end-to-end test on the documentation examples
                # that will be actually shown to users, we use the
                # Markdown rendering pipeline to compute the user-facing
                # example, and then run that to test it.
                curl_command_html = md_engine.convert(line.strip())
                unescaped_html = html.unescape(curl_command_html)
                curl_command_text = unescaped_html[len("<p><code>curl\n"
                                                       ):-len("</code></p>")]
                curl_command_text = curl_command_text.replace(
                    "BOT_EMAIL_ADDRESS:BOT_API_KEY", authentication_line)

                # TODO: This needs_reactivation block is a hack.
                # However, it's awkward to test the "deactivate
                # myself" endpoint with how this system tries to use
                # the same account for all tests without some special
                # logic for that endpoint; and the hack is better than
                # just not documenting the endpoint.
                needs_reactivation = False
                user_id = 0
                if file_name == "templates/zerver/api/deactivate-own-user.md":
                    needs_reactivation = True
                    user_id = client.get_profile()["user_id"]

                print("Testing {} ...".format(
                    curl_command_text.split("\n")[0]))

                # Turn the text into an arguments list.
                generated_curl_command = [
                    x for x in shlex.split(curl_command_text) if x != "\n"
                ]

                response_json = None
                response = None
                try:
                    # We split this across two lines so if curl fails and
                    # returns non-JSON output, we'll still print it.
                    response_json = subprocess.check_output(
                        generated_curl_command, universal_newlines=True)
                    response = json.loads(response_json)
                    assert response["result"] == "success"
                    if needs_reactivation:
                        owner_client.reactivate_user_by_id(user_id)
                except (AssertionError, Exception):
                    error_template = """
Error verifying the success of the API documentation curl example.

File: {file_name}
Line: {line}
Curl command:
{curl_command}
Response:
{response}

This test is designed to check each generate_code_example(curl) instance in the
API documentation for success. If this fails then it means that the curl example
that was generated was faulty and when tried, it resulted in an unsuccessful
response.

Common reasons for why this could occur:
    1. One or more example values in zerver/openapi/zulip.yaml for this endpoint
       do not line up with the values in the test database.
    2. One or more mandatory parameters were included in the "exclude" list.

To learn more about the test itself, see zerver/openapi/test_curl_examples.py.
"""
                    print(
                        error_template.format(
                            file_name=file_name,
                            line=line,
                            curl_command=generated_curl_command,
                            response=response_json if response is None else
                            json.dumps(response, indent=4),
                        ))
                    raise

    if REGISTERED_GENERATOR_FUNCTIONS != CALLED_GENERATOR_FUNCTIONS:
        raise Exception(
            "Some registered generator functions were not called:\n"
            " " +
            str(REGISTERED_GENERATOR_FUNCTIONS - CALLED_GENERATOR_FUNCTIONS))
コード例 #24
0
def test_invalid_stream_error(client: Client) -> None:
    result = client.get_stream_id("nonexistent")

    validate_against_openapi_schema(result, "/get_stream_id", "get", "400")
コード例 #25
0
ファイル: lib.py プロジェクト: rheaparekh/python-zulip-api
def run_message_handler_for_bot(lib_module, quiet, config_file, bot_name):
    # type: (Any, bool, str) -> Any
    #
    # lib_module is of type Any, since it can contain any bot's
    # handler class. Eventually, we want bot's handler classes to
    # inherit from a common prototype specifying the handle_message
    # function.
    #
    # Make sure you set up your ~/.zuliprc
    client = Client(config_file=config_file,
                    client="Zulip{}Bot".format(bot_name.capitalize()))
    bot_dir = os.path.dirname(lib_module.__file__)
    restricted_client = ExternalBotHandler(client, bot_dir)

    message_handler = lib_module.handler_class()
    if hasattr(message_handler, 'initialize'):
        message_handler.initialize(bot_handler=restricted_client)

    state_handler = StateHandler()

    # Set default bot_details, then override from class, if provided
    bot_details = {
        'name': bot_name.capitalize(),
        'description': "",
    }
    bot_details.update(getattr(lib_module.handler_class, 'META', {}))

    if not quiet:
        print("Running {} Bot:".format(bot_details['name']))
        if bot_details['description'] != "":
            print("\n\t{}".format(bot_details['description']))
        print(message_handler.usage())

    def handle_message(message):
        # type: (Dict[str, Any]) -> None
        logging.info('waiting for next message')

        # is_mentioned is true if the bot is mentioned at ANY position (not necessarily
        # the first @mention in the message).
        is_mentioned = message['is_mentioned']
        is_private_message = is_private_message_from_another_user(
            message, restricted_client.user_id)

        # Strip at-mention botname from the message
        if is_mentioned:
            # message['content'] will be None when the bot's @-mention is not at the beginning.
            # In that case, the message shall not be handled.
            message['content'] = extract_query_without_mention(
                message=message, client=restricted_client)
            if message['content'] is None:
                return

        if is_private_message or is_mentioned:
            message_handler.handle_message(message=message,
                                           bot_handler=restricted_client,
                                           state_handler=state_handler)

    signal.signal(signal.SIGINT, exit_gracefully)

    logging.info('starting message handling...')
    client.call_on_each_message(handle_message)
コード例 #26
0
def get_subscribers(client: Client) -> None:

    result = client.get_subscribers(stream="new stream")
    assert result["subscribers"] == ["*****@*****.**", "*****@*****.**"]
コード例 #27
0
ファイル: zulip.py プロジェクト: jayvdb/errbot-backend-zulip
class ZulipBackend(ErrBot):
    def __init__(self, config):
        super().__init__(config)
        config.MESSAGE_SIZE_LIMIT = ZULIP_MESSAGE_SIZE_LIMIT

        self.identity = config.BOT_IDENTITY
        for key in ('email', 'key', 'site'):
            if key not in self.identity:
                log.fatal(
                    "You need to supply the key `{}` for me to use. `{key}` and its value "
                    "can be found in your bot's `zuliprc` config file.".format(
                        key))
                sys.exit(1)

        compact = config.COMPACT_OUTPUT if hasattr(config,
                                                   'COMPACT_OUTPUT') else False
        enable_format('text', TEXT_CHRS, borders=not compact)
        self.client = Client(email=self.identity['email'],
                             api_key=self.identity['key'],
                             site=self.identity['site'])

    def serve_once(self):
        self.bot_identifier = self.build_identifier(self.client.email)
        log.info("Initializing connection")
        self.client.ensure_session()
        log.info("Connected")
        self.reset_reconnection_count()
        self.connect_callback()
        try:
            self.client.call_on_each_message(self._handle_message)
        except KeyboardInterrupt:
            log.info("Interrupt received, shutting down..")
            return True  # True means shutdown was requested.
        except Exception:
            log.exception("Error reading from Zulip updates stream.")
            raise
        finally:
            log.debug("Triggering disconnect callback.")
            self.disconnect_callback()

    def _handle_message(self, message):
        """
        Handles incoming messages.
        In Zulip, there are three types of messages: Private messages, Private group messages,
        and Stream messages. This plugin handles Group PMs as normal PMs between the bot and the
        user. Stream messages are handled as messages to rooms.
        """
        if not message['content']:
            log.warning("Unhandled message type (not a text message) ignored")
            return

        message_instance = self.build_message(message['content'])
        if message['type'] == 'private':
            message_instance.frm = ZulipPerson(
                id=message['sender_email'],
                full_name=message['sender_full_name'],
                emails=[message['sender_email']],
                client=message['client'])
            message_instance.to = ZulipPerson(
                id=message['sender_email'],
                full_name=','.join([
                    recipient['full_name']
                    for recipient in message['display_recipient']
                ]),
                emails=[
                    recipient['email']
                    for recipient in message['display_recipient']
                ],
                client=None)
        elif message['type'] == 'stream':
            room = ZulipRoom(id=message['display_recipient'],
                             title=message['display_recipient'],
                             subject=message['subject'])
            message_instance.frm = ZulipRoomOccupant(
                id=message['sender_email'],
                full_name=message['sender_full_name'],
                emails=[message['sender_email']],
                client=message['client'],
                room=room)
            message_instance.to = room
        else:
            raise ValueError("Invalid message type `{}`.".format(
                message['type']))
        self.callback_message(message_instance)

    def send_message(self, msg):
        super().send_message(msg)
        msg_data = {
            'content': msg.body,
        }
        if isinstance(msg.to, ZulipRoom):
            msg_data['type'] = 'stream'
            msg_data['subject'] = msg.to.subject
            msg_data['to'] = msg.to.title

        elif isinstance(msg.to, ZulipPerson):
            if isinstance(msg.to, ZulipRoomOccupant):
                msg_data['type'] = 'stream'
                msg_data['subject'] = msg.to.room.subject
                msg_data['to'] = msg.to.room.title
            else:
                msg_data['type'] = 'private'
                msg_data['to'] = msg.to.emails
        else:
            raise ValueError("Invalid message recipient of type {}".format(
                type(msg.to).__name__))
        try:
            self.client.send_message(msg_data)
        except Exception:
            log.exception(
                "An exception occurred while trying to send the following message "
                "to %s: %s" % (msg.to.id, msg.body))
            raise

    def is_from_self(self, msg):
        return msg.frm.aclattr == self.client.email

    def change_presence(self, status: str = ONLINE, message: str = '') -> None:
        # At this time, Zulip doesn't support active presence change.
        pass

    def build_identifier(self, txtrep):
        return ZulipPerson(id=txtrep,
                           full_name=txtrep,
                           emails=[txtrep],
                           client=self.client)

    def build_reply(self, msg, text=None, private=False, threaded=False):
        response = self.build_message(text)
        response.to = msg.to
        return response

    @property
    def mode(self):
        return 'zulip'

    def query_room(self, room):
        return ZulipRoom(title=room, client=self.client)

    def rooms(self):
        result = parse_query_result(self.client.list_subscriptions())
        return [
            ZulipRoom(title=subscription['name'], id=subscription['name'])
            for subscription in result['subscriptions']
        ]

    def prefix_groupchat_reply(self, message, identifier):
        super().prefix_groupchat_reply(message, identifier)
        message.body = '@**{0}** {1}'.format(identifier.full_name,
                                             message.body)

    def _zulip_upload_stream(self, stream):
        """Perform upload defined in a stream."""
        try:
            stream.accept()
            result = self.client.upload_file(stream.raw)
            if result['result'] == 'success':
                message_instance = self.build_message("[{}]({})".format(
                    stream.name, result['uri']))
                message_instance.to = stream.identifier
                self.send_message(message_instance)
                stream.success()

            else:
                stream.error()
        except Exception:
            log.exception("Upload of {0} to {1} failed.".format(
                stream.name, stream.identifier))

    def send_stream_request(self,
                            identifier,
                            fsource,
                            name='file',
                            size=None,
                            stream_type=None):
        """Starts a file transfer.

        :param identifier: ZulipPerson or ZulipRoom
            Identifier of the Person or Room to send the stream to.

        :param fsource: str, dict or binary data
            File URL or binary content from a local file.
            Optionally a dict with binary content plus metadata can be given.
            See `stream_type` for more details.

        :param name: str, optional
            Name of the file. Not sure if this works always.

        :param size: str, optional
            Size of the file obtained with os.path.getsize.
            This is only used for debug logging purposes.

        :param stream_type: str, optional
            Type of the stream. Choices: 'document', 'photo', 'audio', 'video', 'sticker', 'location'.
            Right now used for debug logging purposes only.

        :return stream: str or Stream
            If `fsource` is str will return str, else return Stream.
        """
        def _metadata(fsource):
            if isinstance(fsource, dict):
                return fsource.pop('content'), fsource
            else:
                return fsource, None

        def _is_valid_url(url):
            try:
                from urlparse import urlparse
            except Exception:
                from urllib.parse import urlparse

            return bool(urlparse(url).scheme)

        content, meta = _metadata(fsource)
        if isinstance(content, str):
            if not _is_valid_url(content):
                raise ValueError("Not valid URL: {}".format(content))
            else:
                raise NotImplementedError(
                    "The Zulip backend does not yet support URL stream requests."
                )
        else:
            stream = Stream(identifier, content, name, size, stream_type)
            log.debug(
                "Requesting upload of {0} to {1} (size hint: {2}, stream type: {3})"
                .format(name, identifier, size, stream_type))
            self.thread_pool.apply_async(self._zulip_upload_stream, (stream, ))

        return stream
コード例 #28
0
def get_alert_words(client: Client) -> None:
    result = client.get_alert_words()

    assert result['result'] == 'success'
コード例 #29
0
ファイル: bot_lib.py プロジェクト: aakash-cr7/zulip
def run_message_handler_for_bot(lib_module, quiet, config_file):
    # Make sure you set up your ~/.zuliprc
    client = Client(config_file=config_file)
    restricted_client = BotHandlerApi(client)

    message_handler = lib_module.handler_class()

    class StateHandler(object):
        def __init__(self):
            self.state = None

        def set_state(self, state):
            self.state = state

        def get_state(self):
            return self.state

    state_handler = StateHandler()

    if not quiet:
        print(message_handler.usage())

    def extract_message_if_mentioned(message, client):
        bot_mention = r'^@(\*\*{0}\*\*\s|{0}\s)(?=.*)'.format(client.full_name)
        start_with_mention = re.compile(bot_mention).match(message['content'])
        if start_with_mention:
            query = message['content'][len(start_with_mention.group()):]
            return query
        else:
            bot_response = 'Please mention me first, then type the query.'
            if message['type'] == 'private':
                client.send_message(dict(
                    type='private',
                    to=message['sender_email'],
                    content=bot_response,
                ))
            else:
                client.send_message(dict(
                    type='stream',
                    to=message['display_recipient'],
                    subject=message['subject'],
                    content=bot_response,
                ))
            return None

    def is_private(message, client):
        # bot will not reply if the sender name is the same as the bot name
        # to prevent infinite loop
        if message['type'] == 'private':
            return client.full_name != message['sender_full_name']
        return False

    def handle_message(message):
        logging.info('waiting for next message')

        is_mentioned = message['is_mentioned']
        is_private_message = is_private(message, restricted_client)

        # Strip at-mention botname from the message
        if is_mentioned:
            message['content'] = extract_message_if_mentioned(message=message, client=restricted_client)
            if message['content'] is None:
                return

        if is_private_message or is_mentioned:
            message_handler.handle_message(
                message=message,
                client=restricted_client,
                state_handler=state_handler
            )

    signal.signal(signal.SIGINT, exit_gracefully)

    logging.info('starting message handling...')
    client.call_on_each_message(handle_message)
コード例 #30
0
def remove_alert_words(client: Client) -> None:
    word = ['foo']

    result = client.remove_alert_words(word)

    assert result['result'] == 'success'
コード例 #31
0
def run_message_handler_for_bot(lib_module, quiet, config_file):
    # type: (Any, bool, str) -> Any
    #
    # lib_module is of type Any, since it can contain any bot's
    # handler class. Eventually, we want bot's handler classes to
    # inherit from a common prototype specifying the handle_message
    # function.
    #
    # Make sure you set up your ~/.zuliprc
    client = Client(config_file=config_file)
    restricted_client = BotHandlerApi(client)

    message_handler = lib_module.handler_class()

    state_handler = StateHandler()

    if not quiet:
        print(message_handler.usage())

    def extract_query_without_mention(message, client):
        # type: (Dict[str, Any], BotHandlerApi) -> str
        """
        If the bot is the first @mention in the message, then this function returns
        the message with the bot's @mention removed.  Otherwise, it returns None.
        """
        bot_mention = r'^@(\*\*{0}\*\*)'.format(client.full_name)
        start_with_mention = re.compile(bot_mention).match(message['content'])
        if start_with_mention is None:
            return None
        query_without_mention = message['content'][len(start_with_mention.
                                                       group()):]
        return query_without_mention.lstrip()

    def is_private(message, client):
        # type: (Dict[str, Any], BotHandlerApi) -> bool
        # bot will not reply if the sender name is the same as the bot name
        # to prevent infinite loop
        if message['type'] == 'private':
            return client.full_name != message['sender_full_name']
        return False

    def handle_message(message):
        # type: (Dict[str, Any]) -> None
        logging.info('waiting for next message')

        # is_mentioned is true if the bot is mentioned at ANY position (not necessarily
        # the first @mention in the message).
        is_mentioned = message['is_mentioned']
        is_private_message = is_private(message, restricted_client)

        # Strip at-mention botname from the message
        if is_mentioned:
            # message['content'] will be None when the bot's @-mention is not at the beginning.
            # In that case, the message shall not be handled.
            message['content'] = extract_query_without_mention(
                message=message, client=restricted_client)
            if message['content'] is None:
                return

        if is_private_message or is_mentioned:
            message_handler.handle_message(message=message,
                                           bot_handler=restricted_client,
                                           state_handler=state_handler)

    signal.signal(signal.SIGINT, exit_gracefully)

    logging.info('starting message handling...')
    client.call_on_each_message(handle_message)
コード例 #32
0
def test_missing_request_argument(client: Client) -> None:
    result = client.render_message({})

    validate_against_openapi_schema(result, '/rest-error-handling', 'post', '400_1')
コード例 #33
0
ファイル: python_examples.py プロジェクト: twoconk/zulip
def test_invalid_stream_error(client: Client) -> None:
    result = client.get_stream_id('nonexistent')

    validate_against_openapi_schema(result, '/get_stream_id', 'get', '400')
コード例 #34
0
	def __init__(self):
		self.client = Client(email = os.environ['ZULIP_USERNAME'], api_key = os.environ['ZULIP_API_KEY'])
		self.subscribe_streams()
コード例 #35
0
ファイル: lib.py プロジェクト: Juljan/python-zulip-api
def run_message_handler_for_bot(
    lib_module: Any,
    quiet: bool,
    config_file: str,
    bot_config_file: str,
    bot_name: str,
) -> Any:
    """
    lib_module is of type Any, since it can contain any bot's
    handler class. Eventually, we want bot's handler classes to
    inherit from a common prototype specifying the handle_message
    function.

    Set default bot_details, then override from class, if provided
    """
    bot_details = {
        'name': bot_name.capitalize(),
        'description': "",
    }
    bot_details.update(getattr(lib_module.handler_class, 'META', {}))
    # Make sure you set up your ~/.zuliprc

    client_name = "Zulip{}Bot".format(bot_name.capitalize())

    try:
        client = Client(config_file=config_file, client=client_name)
    except configparser.Error as e:
        display_config_file_errors(str(e), config_file)
        sys.exit(1)

    bot_dir = os.path.dirname(lib_module.__file__)
    restricted_client = ExternalBotHandler(client, bot_dir, bot_details, bot_config_file)

    message_handler = prepare_message_handler(bot_name, restricted_client, lib_module)

    if not quiet:
        print("Running {} Bot:".format(bot_details['name']))
        if bot_details['description'] != "":
            print("\n\t{}".format(bot_details['description']))
        print(message_handler.usage())

    def handle_message(message: Dict[str, Any], flags: List[str]) -> None:
        logging.info('waiting for next message')
        # `mentioned` will be in `flags` if the bot is mentioned at ANY position
        # (not necessarily the first @mention in the message).
        is_mentioned = 'mentioned' in flags
        is_private_message = is_private_message_from_another_user(message, restricted_client.user_id)

        # Provide bots with a way to access the full, unstripped message
        message['full_content'] = message['content']
        # Strip at-mention botname from the message
        if is_mentioned:
            # message['content'] will be None when the bot's @-mention is not at the beginning.
            # In that case, the message shall not be handled.
            message['content'] = extract_query_without_mention(message=message, client=restricted_client)
            if message['content'] is None:
                return

        if is_private_message or is_mentioned:
            message_handler.handle_message(
                message=message,
                bot_handler=restricted_client
            )

    signal.signal(signal.SIGINT, exit_gracefully)

    logging.info('starting message handling...')

    def event_callback(event: Dict[str, Any]) -> None:
        if event['type'] == 'message':
            handle_message(event['message'], event['flags'])

    client.call_on_each_event(event_callback, ['message'])