From da4b83057102db4552e945fae9aab58d1eb052f0 Mon Sep 17 00:00:00 2001 From: rht Date: Sat, 5 Aug 2017 13:35:24 +0200 Subject: [PATCH] bots: Init interrealm bridge bot. --- .../bots/interrealm_bridge/README.md | 15 +++ .../interrealm_bridge_config.py | 14 +++ .../interrealm_bridge/run-interrealm-bridge | 92 +++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 zulip_bots/zulip_bots/bots/interrealm_bridge/README.md create mode 100644 zulip_bots/zulip_bots/bots/interrealm_bridge/interrealm_bridge_config.py create mode 100755 zulip_bots/zulip_bots/bots/interrealm_bridge/run-interrealm-bridge diff --git a/zulip_bots/zulip_bots/bots/interrealm_bridge/README.md b/zulip_bots/zulip_bots/bots/interrealm_bridge/README.md new file mode 100644 index 0000000..6b344d8 --- /dev/null +++ b/zulip_bots/zulip_bots/bots/interrealm_bridge/README.md @@ -0,0 +1,15 @@ +# Inter-realm bot + +Let `realm_1` be the first realm, `realm_2` be the second realm. Let `bot_1` be +the relay bot in `realm_1`, `bot_2` be the relay bot in `realm_2`. + +This bot relays each message received at a specified subject in a specified +stream from `realm_1` to a specified subject in a specified stream in `realm_2`. + +Steps to create an inter-realm bridge: +1. Register a generic bot (`bot_1`) in `realm_1` +2. Enter the api info of `bot_1` into the config file (interrealm_bridge_config.py) +3. Create a stream in `realm_1` (`stream_1`) and a subject for the bridge +4. Make sure `bot_1` is subscribed to `stream_1` +5. Enter the stream and the subject into the config file. +6. Do step 1-5 but for `bot_2` and with all occurrences of `_1` replaced with `_2` diff --git a/zulip_bots/zulip_bots/bots/interrealm_bridge/interrealm_bridge_config.py b/zulip_bots/zulip_bots/bots/interrealm_bridge/interrealm_bridge_config.py new file mode 100644 index 0000000..c28cd7c --- /dev/null +++ b/zulip_bots/zulip_bots/bots/interrealm_bridge/interrealm_bridge_config.py @@ -0,0 +1,14 @@ +config = { + "bot_1": { + "email": "tunnel-bot@realm1.zulipchat.com", + "api_key": "key1", + "site": "https://realm1.zulipchat.com", + "stream": "bridges", + "subject": "<- realm2"}, + "bot_2": { + "email": "tunnel-bot@realm2.zulipchat.com", + "api_key": "key2", + "site": "https://realm2.zulipchat.com", + "stream": "bridges", + "subject": "<- realm1"} +} diff --git a/zulip_bots/zulip_bots/bots/interrealm_bridge/run-interrealm-bridge b/zulip_bots/zulip_bots/bots/interrealm_bridge/run-interrealm-bridge new file mode 100755 index 0000000..91e0cca --- /dev/null +++ b/zulip_bots/zulip_bots/bots/interrealm_bridge/run-interrealm-bridge @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import sys +import os +import argparse +import multiprocessing as mp +import zulip +import interrealm_bridge_config + +from typing import Any, Callable, Dict + + +def create_pipe_event(to_client: zulip.Client, from_bot: Dict[str, Any], + to_bot: Dict[str, Any], stream_wide: bool + ) -> Callable[[Dict[str, Any]], None]: + def _pipe_message(msg: Dict[str, Any]) -> None: + isa_stream = msg["type"] == "stream" + not_from_bot = msg["sender_email"] not in (from_bot["email"], to_bot["email"]) + in_the_specified_stream = msg["display_recipient"] == from_bot["stream"] + + if stream_wide: + # If tunnel granularity is at stream-wide, all subjects are + # mirrored as-is without translation. + at_the_specified_subject = True + subject = msg["subject"] + else: + at_the_specified_subject = msg["subject"] == from_bot["subject"] + subject = to_bot["subject"] + + if isa_stream and not_from_bot and in_the_specified_stream and at_the_specified_subject: + if "/user_uploads/" in msg["content"]: + # Fix the upload URL of the image to be the source of where it + # comes from + msg["content"] = msg["content"].replace("/user_uploads/", + from_bot["site"] + "/user_uploads/") + msg_data = { + "sender": to_client.email, + "type": "stream", + "to": to_bot["stream"], + "subject": subject, + "content": "**{0}**: {1}".format(msg["sender_full_name"], msg["content"]), + "has_attachment": msg.get("has_attachment", False), + "has_image": msg.get("has_image", False), + "has_link": msg.get("has_link", False) + } + print(msg_data) + print(to_client.send_message(msg_data)) + + def _pipe_event(event: Dict[str, Any]) -> None: + # See zerver/lib/events.py for a comprehensive event list + if event["type"] == "message": + msg = event["message"] + _pipe_message(msg) + return _pipe_event + +if __name__ == "__main__": + usage = """run-interrealm-bridge [--stream] + + Relay each message received at a specified subject in a specified stream from + the first realm to a specified subject in a specified stream in the second realm. + + If the --stream flag is added, then the tunnel granularity becomes + stream-wide. All subjects are mirrored as-is without translation. + """ + sys.path.append(os.path.join(os.path.dirname(__file__), '..')) + + parser = argparse.ArgumentParser(usage=usage) + parser.add_argument('--stream', + action='store_true', + help="", + default=False) + args = parser.parse_args() + + options = interrealm_bridge_config.config + + bot1 = options["bot_1"] + bot2 = options["bot_2"] + client1 = zulip.Client(email=bot1["email"], api_key=bot1["api_key"], + site=bot1["site"]) + client2 = zulip.Client(email=bot2["email"], api_key=bot2["api_key"], + site=bot2["site"]) + # A bidirectional tunnel + pipe_event1 = create_pipe_event(client2, bot1, bot2, args.stream) + p1 = mp.Process(target=client1.call_on_each_event, args=(pipe_event1, ["message"])) + pipe_event2 = create_pipe_event(client1, bot2, bot1, args.stream) + p2 = mp.Process(target=client2.call_on_each_event, args=(pipe_event2, ["message"])) + p1.start() + p2.start() + print("Listening...") + p1.join() + p2.join()