Skip to content

Commit 866c2cc

Browse files
committed
slack bridge: Remove the legacy RTM API based bridge.
Slack Bridge now uses the Slack Webhook integration to get messages accross from Slack instead of the legacy RTM API based connection our Slack Bridge use.
1 parent 306261c commit 866c2cc

File tree

2 files changed

+26
-55
lines changed

2 files changed

+26
-55
lines changed

zulip/integrations/bridge_with_slack/bridge_with_slack_config.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
},
77
"slack": {
88
"username": "slack_username",
9-
"token": "xoxb-your-slack-token",
9+
"token": "xoxp-your-slack-token",
1010
},
1111
# Mapping between Slack channels and Zulip stream-topic's.
1212
# You can specify multiple pairs.
1313
"channel_mapping": {
1414
# Slack channel; must be channel ID
1515
"C5Z5N7R8A": {
16-
# Zulip stream
17-
"stream": "test here",
16+
# Zulip channel
17+
"channel": "test here",
1818
# Zulip topic
1919
"topic": "<- slack-bridge",
2020
},

zulip/integrations/bridge_with_slack/run-slack-bridge

Lines changed: 23 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,11 @@ import traceback
88
from typing import Any, Callable, Dict, Optional, Tuple
99

1010
import bridge_with_slack_config
11-
import slack_sdk
12-
from slack_sdk.rtm_v2 import RTMClient
11+
from slack_sdk.web.client import WebClient
1312

1413
import zulip
1514

1615
# change these templates to change the format of displayed message
17-
ZULIP_MESSAGE_TEMPLATE = "**{username}**: {message}"
1816
SLACK_MESSAGE_TEMPLATE = "<{username}> {message}"
1917

2018
StreamTopicT = Tuple[str, str]
@@ -41,15 +39,26 @@ def get_slack_channel_for_zulip_message(
4139
return zulip_to_slack_map[stream_topic]
4240

4341

42+
def check_token_access(token: str) -> None:
43+
if token.startswith("xoxp-"):
44+
print(
45+
"--- Warning! ---\n"
46+
"You entered a Slack user token, please copy the token under\n"
47+
"'Bot User OAuth Token' which starts with 'xoxb-...'."
48+
)
49+
sys.exit(1)
50+
elif token.startswith("xoxb-"):
51+
return
52+
53+
4454
class SlackBridge:
4555
def __init__(self, config: Dict[str, Any]) -> None:
4656
self.config = config
4757
self.zulip_config = config["zulip"]
4858
self.slack_config = config["slack"]
4959

50-
self.slack_to_zulip_map: Dict[str, Dict[str, str]] = config["channel_mapping"]
5160
self.zulip_to_slack_map: Dict[StreamTopicT, str] = {
52-
(z["stream"], z["topic"]): s for s, z in config["channel_mapping"].items()
61+
(z["channel"], z["topic"]): s for s, z in config["channel_mapping"].items()
5362
}
5463

5564
# zulip-specific
@@ -65,25 +74,16 @@ class SlackBridge:
6574
# https://github.com/zulip/python-zulip-api/issues/761 is fixed.
6675
self.zulip_client_constructor = zulip_client_constructor
6776

68-
# slack-specific
69-
self.slack_client = rtm
7077
# Spawn a non-websocket client for getting the users
7178
# list and for posting messages in Slack.
72-
self.slack_webclient = slack_sdk.WebClient(token=self.slack_config["token"])
79+
self.slack_webclient = WebClient(token=self.slack_config["token"])
7380

7481
def wrap_slack_mention_with_bracket(self, zulip_msg: Dict[str, Any]) -> None:
7582
words = zulip_msg["content"].split(" ")
7683
for w in words:
7784
if w.startswith("@"):
7885
zulip_msg["content"] = zulip_msg["content"].replace(w, "<" + w + ">")
7986

80-
def replace_slack_id_with_name(self, msg: Dict[str, Any]) -> None:
81-
words = msg["text"].split(" ")
82-
for w in words:
83-
if w.startswith("<@") and w.endswith(">"):
84-
_id = w[2:-1]
85-
msg["text"] = msg["text"].replace(_id, self.slack_id_to_name[_id])
86-
8787
def zulip_to_slack(self) -> Callable[[Dict[str, Any]], None]:
8888
def _zulip_to_slack(msg: Dict[str, Any]) -> None:
8989
slack_channel = get_slack_channel_for_zulip_message(
@@ -101,36 +101,6 @@ class SlackBridge:
101101

102102
return _zulip_to_slack
103103

104-
def run_slack_listener(self) -> None:
105-
members = self.slack_webclient.users_list()["members"]
106-
# See also https://api.slack.com/changelog/2017-09-the-one-about-usernames
107-
self.slack_id_to_name: Dict[str, str] = {
108-
u["id"]: u["profile"].get("display_name", u["profile"]["real_name"]) for u in members
109-
}
110-
self.slack_name_to_id = {v: k for k, v in self.slack_id_to_name.items()}
111-
112-
@rtm.on("message")
113-
def slack_to_zulip(client: RTMClient, event: Dict[str, Any]) -> None:
114-
if event["channel"] not in self.slack_to_zulip_map:
115-
return
116-
user_id = event["user"]
117-
user = self.slack_id_to_name[user_id]
118-
from_bot = user == self.slack_config["username"]
119-
if from_bot:
120-
return
121-
self.replace_slack_id_with_name(event)
122-
content = ZULIP_MESSAGE_TEMPLATE.format(username=user, message=event["text"])
123-
zulip_endpoint = self.slack_to_zulip_map[event["channel"]]
124-
msg_data = dict(
125-
type="stream",
126-
to=zulip_endpoint["stream"],
127-
subject=zulip_endpoint["topic"],
128-
content=content,
129-
)
130-
self.zulip_client_constructor().send_message(msg_data)
131-
132-
self.slack_client.start()
133-
134104

135105
if __name__ == "__main__":
136106
usage = """run-slack-bridge
@@ -142,6 +112,8 @@ if __name__ == "__main__":
142112
sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
143113
parser = argparse.ArgumentParser(usage=usage)
144114

115+
args = parser.parse_args()
116+
145117
config: Dict[str, Any] = bridge_with_slack_config.config
146118
if "channel_mapping" not in config:
147119
print(
@@ -150,12 +122,11 @@ if __name__ == "__main__":
150122
)
151123
sys.exit(1)
152124

125+
check_token_access(config["slack"]["token"])
126+
153127
print("Starting slack mirroring bot")
154128
print("MAKE SURE THE BOT IS SUBSCRIBED TO THE RELEVANT ZULIP STREAM(S) & SLACK CHANNEL(S)!")
155129

156-
# We have to define rtm outside of SlackBridge because the rtm variable is used as a method decorator.
157-
rtm = RTMClient(token=config["slack"]["token"])
158-
159130
backoff = zulip.RandomExponentialBackoff(timeout_success_equivalent=300)
160131
while backoff.keep_going():
161132
try:
@@ -164,14 +135,14 @@ if __name__ == "__main__":
164135
zp = threading.Thread(
165136
target=sb.zulip_client.call_on_each_message, args=(sb.zulip_to_slack(),)
166137
)
167-
sp = threading.Thread(target=sb.run_slack_listener, args=())
168138
print("Starting message handler on Zulip client")
169139
zp.start()
170-
print("Starting message handler on Slack client")
171-
sp.start()
172140

141+
print(
142+
"Make sure your Slack Webhook integration is running\n"
143+
"to receive messages from Slack."
144+
)
173145
zp.join()
174-
sp.join()
175146
except Exception:
176147
traceback.print_exc()
177148
backoff.fail()

0 commit comments

Comments
 (0)