Skip to content

Commit d1b6c09

Browse files
committed
Add Rocket.Chat integration
1 parent 4af8bcd commit d1b6c09

File tree

3 files changed

+158
-0
lines changed

3 files changed

+158
-0
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Zulip <--> Rocket.Chat mirror
2+
3+
## Usage
4+
5+
0. `pip install rocketchat_API`
6+
7+
### 1. Zulip endpoint
8+
1. Create a generic Zulip bot
9+
2. (don't forget this step!) Make sure the bot is subscribed to the relevant stream
10+
3. Enter the bot's email and api_key into rocket_mirror_config.py
11+
4. Enter the destination subject and realm into the config file
12+
13+
### 2. Rocket.Chat endpoint
14+
1. Create a user
15+
2. Enter the user's username and password into rocket_bridge_config.py
16+
3. Enter the Rocket.Chat server url into the config file
17+
4. Enter the channel id and channel name to be mirrored into the config file
18+
19+
After the steps above have been completed, run `./rocket.chat-mirror` to start the mirroring.
20+
note: Run the script relative to its directory !
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
#!/usr/bin/env python3
2+
3+
import os
4+
import logging
5+
import time
6+
import signal
7+
import traceback
8+
import multiprocessing as mp
9+
import datetime
10+
11+
from types import FrameType
12+
from typing import Any, Callable, Dict
13+
14+
from rocketchat_API.rocketchat import RocketChat
15+
from rocket_mirror_config import config
16+
import zulip
17+
from pprint import pprint
18+
19+
Cfg = Dict[str, Any]
20+
21+
def die(signal, frame):
22+
# type: (int, FrameType) -> None
23+
24+
# We actually want to exit, so run os._exit (so as not to be caught and restarted)
25+
os._exit(1)
26+
27+
def zulip_to_rocket_username(full_name, site):
28+
# type: (str, str) -> str
29+
return "@**{0}**:{1}".format(full_name, site)
30+
31+
def rocket_to_zulip(zulip_client, cfg, res):
32+
# type: (Any, Cfg) -> None
33+
zulip_cfg = cfg["zulip"]
34+
rocket_cfg = cfg["rocket"]
35+
36+
if res['success']:
37+
for msg in res['messages']:
38+
user = msg['u']
39+
content = "**{0}**: {1}".format(user['name'], msg['msg'])
40+
pprint(msg['u']['username'])
41+
42+
is_not_from_bot = user['username'] != rocket_cfg['username']
43+
if is_not_from_bot:
44+
msg_data = dict(
45+
sender=zulip_client.email,
46+
type="stream",
47+
to=zulip_cfg["stream"],
48+
subject=zulip_cfg["subject"],
49+
content=content)
50+
print(msg_data)
51+
zulip_client.send_message(msg_data)
52+
53+
def zulip_to_rocket(rocket_client, cfg):
54+
# type: (Any, Cfg) -> Callable[[Dict[str, Any]], None]
55+
zulip_cfg = cfg["zulip"]
56+
rocket_cfg = cfg["rocket"]
57+
site_without_http = zulip_cfg["site"].replace("https://", "").replace("http://", "")
58+
59+
def _zulip_to_rocket(msg):
60+
# type: (Dict[str, Any]) -> None
61+
"""Zulip -> Matrix
62+
"""
63+
isa_stream = msg["type"] == "stream"
64+
not_from_bot = msg["sender_email"] != zulip_cfg["email"]
65+
in_the_specified_stream = msg["display_recipient"] == zulip_cfg["stream"]
66+
at_the_specified_subject = msg["subject"] == zulip_cfg["subject"]
67+
if isa_stream and not_from_bot and in_the_specified_stream and at_the_specified_subject:
68+
rocket_username = zulip_to_rocket_username(msg["sender_full_name"], site_without_http)
69+
rocket_text = "{0}: {1}".format(rocket_username,
70+
msg["content"])
71+
pprint(rocket_client.chat_post_message(rocket_text, channel=rocket_cfg["channel_id"]).json())
72+
return _zulip_to_rocket
73+
74+
def rocket_listener(rocket_client, zulip_client, cfg):
75+
interval = 2
76+
rocket_cfg = cfg["rocket"]
77+
while True:
78+
now = datetime.datetime.utcnow()
79+
oldest = now - datetime.timedelta(seconds=interval)
80+
oldest_str = str(oldest)[:-3] + 'Z'
81+
res = rocket_client.channels_history(rocket_cfg['channel_id'],
82+
oldest=oldest_str).json()
83+
rocket_to_zulip(zulip_client, cfg, res)
84+
time.sleep(interval)
85+
86+
if __name__ == '__main__':
87+
signal.signal(signal.SIGINT, die)
88+
logging.basicConfig(level=logging.WARNING)
89+
90+
# Get config for each clients
91+
zulip_config = config["zulip"]
92+
rocket_config = config["rocket"]
93+
94+
# Initiate clients
95+
print("Starting rocketchat mirroring bot")
96+
97+
while True:
98+
try:
99+
zulip_client = zulip.Client(email=zulip_config["email"],
100+
api_key=zulip_config["api_key"],
101+
site=zulip_config["site"])
102+
103+
rocket_client = RocketChat(rocket_config['username'],
104+
rocket_config['password'],
105+
server_url=rocket_config['server_url'])
106+
107+
# A bidirectional mirror
108+
p1 = mp.Process(target=zulip_client.call_on_each_message,
109+
args=(zulip_to_rocket(rocket_client, config),))
110+
p2 = mp.Process(target=rocket_listener,
111+
args=(rocket_client, zulip_client, config))
112+
113+
print("Starting message handler on Zulip client")
114+
p1.start()
115+
print("Starting message handler on Rocket.Chat client")
116+
p2.start()
117+
118+
p1.join()
119+
p2.join()
120+
121+
except Exception:
122+
traceback.print_exc()
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
config = {
2+
"rocket": {
3+
"server_url": "https://chat.serverurl.org",
4+
"username": "botusername",
5+
"password": "botpassword",
6+
"channel_id": "idofthechannel",
7+
"channel_name": "nameofthechannel"
8+
},
9+
"zulip": {
10+
"email": "[email protected]",
11+
"api_key": "someapikey",
12+
"site": "https://chat.someserver.org",
13+
"stream": "somestream",
14+
"subject": "somesubject"
15+
}
16+
}

0 commit comments

Comments
 (0)