black: Reformat without skipping string normalization.
This commit is contained in:
parent
fba21bb00d
commit
6f3f9bf7e4
178 changed files with 5242 additions and 5242 deletions
|
@ -71,10 +71,10 @@ if __name__ == "__main__":
|
|||
all topics within the stream are mirrored as-is without
|
||||
translation.
|
||||
"""
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
|
||||
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)
|
||||
parser.add_argument("--stream", action="store_true", help="", default=False)
|
||||
args = parser.parse_args()
|
||||
|
||||
options = interrealm_bridge_config.config
|
||||
|
|
|
@ -29,13 +29,13 @@ if __name__ == "__main__":
|
|||
parser = zulip.add_default_arguments(
|
||||
argparse.ArgumentParser(usage=usage), allow_provisioning=True
|
||||
)
|
||||
parser.add_argument('--irc-server', default=None)
|
||||
parser.add_argument('--port', default=6667)
|
||||
parser.add_argument('--nick-prefix', default=None)
|
||||
parser.add_argument('--channel', default=None)
|
||||
parser.add_argument('--stream', default="general")
|
||||
parser.add_argument('--topic', default="IRC")
|
||||
parser.add_argument('--nickserv-pw', default='')
|
||||
parser.add_argument("--irc-server", default=None)
|
||||
parser.add_argument("--port", default=6667)
|
||||
parser.add_argument("--nick-prefix", default=None)
|
||||
parser.add_argument("--channel", default=None)
|
||||
parser.add_argument("--stream", default="general")
|
||||
parser.add_argument("--topic", default="IRC")
|
||||
parser.add_argument("--nickserv-pw", default="")
|
||||
|
||||
options = parser.parse_args()
|
||||
# Setting the client to irc_mirror is critical for this to work
|
||||
|
|
|
@ -18,7 +18,7 @@ class IRCBot(irc.bot.SingleServerIRCBot):
|
|||
channel: irc.bot.Channel,
|
||||
nickname: str,
|
||||
server: str,
|
||||
nickserv_password: str = '',
|
||||
nickserv_password: str = "",
|
||||
port: int = 6667,
|
||||
) -> None:
|
||||
self.channel = channel # type: irc.bot.Channel
|
||||
|
@ -61,8 +61,8 @@ class IRCBot(irc.bot.SingleServerIRCBot):
|
|||
|
||||
def on_welcome(self, c: ServerConnection, e: Event) -> None:
|
||||
if len(self.nickserv_password) > 0:
|
||||
msg = 'identify %s' % (self.nickserv_password,)
|
||||
c.privmsg('NickServ', msg)
|
||||
msg = "identify %s" % (self.nickserv_password,)
|
||||
c.privmsg("NickServ", msg)
|
||||
c.join(self.channel)
|
||||
|
||||
def forward_to_irc(msg: Dict[str, Any]) -> None:
|
||||
|
|
|
@ -17,8 +17,8 @@ from requests.exceptions import MissingSchema
|
|||
|
||||
import zulip
|
||||
|
||||
GENERAL_NETWORK_USERNAME_REGEX = '@_?[a-zA-Z0-9]+_([a-zA-Z0-9-_]+):[a-zA-Z0-9.]+'
|
||||
MATRIX_USERNAME_REGEX = '@([a-zA-Z0-9-_]+):matrix.org'
|
||||
GENERAL_NETWORK_USERNAME_REGEX = "@_?[a-zA-Z0-9]+_([a-zA-Z0-9-_]+):[a-zA-Z0-9.]+"
|
||||
MATRIX_USERNAME_REGEX = "@([a-zA-Z0-9-_]+):matrix.org"
|
||||
|
||||
# change these templates to change the format of displayed message
|
||||
ZULIP_MESSAGE_TEMPLATE = "**{username}**: {message}"
|
||||
|
@ -77,10 +77,10 @@ def matrix_to_zulip(
|
|||
"""
|
||||
content = get_message_content_from_event(event, no_noise)
|
||||
|
||||
zulip_bot_user = '@%s:matrix.org' % (matrix_config['username'],)
|
||||
zulip_bot_user = "@%s:matrix.org" % (matrix_config["username"],)
|
||||
# We do this to identify the messages generated from Zulip -> Matrix
|
||||
# and we make sure we don't forward it again to the Zulip stream.
|
||||
not_from_zulip_bot = event['sender'] != zulip_bot_user
|
||||
not_from_zulip_bot = event["sender"] != zulip_bot_user
|
||||
|
||||
if not_from_zulip_bot and content:
|
||||
try:
|
||||
|
@ -95,31 +95,31 @@ def matrix_to_zulip(
|
|||
except Exception as exception: # XXX This should be more specific
|
||||
# Generally raised when user is forbidden
|
||||
raise Bridge_ZulipFatalException(exception)
|
||||
if result['result'] != 'success':
|
||||
if result["result"] != "success":
|
||||
# Generally raised when API key is invalid
|
||||
raise Bridge_ZulipFatalException(result['msg'])
|
||||
raise Bridge_ZulipFatalException(result["msg"])
|
||||
|
||||
return _matrix_to_zulip
|
||||
|
||||
|
||||
def get_message_content_from_event(event: Dict[str, Any], no_noise: bool) -> Optional[str]:
|
||||
irc_nick = shorten_irc_nick(event['sender'])
|
||||
if event['type'] == "m.room.member":
|
||||
irc_nick = shorten_irc_nick(event["sender"])
|
||||
if event["type"] == "m.room.member":
|
||||
if no_noise:
|
||||
return None
|
||||
# Join and leave events can be noisy. They are ignored by default.
|
||||
# To enable these events pass `no_noise` as `False` as the script argument
|
||||
if event['membership'] == "join":
|
||||
if event["membership"] == "join":
|
||||
content = ZULIP_MESSAGE_TEMPLATE.format(username=irc_nick, message="joined")
|
||||
elif event['membership'] == "leave":
|
||||
elif event["membership"] == "leave":
|
||||
content = ZULIP_MESSAGE_TEMPLATE.format(username=irc_nick, message="quit")
|
||||
elif event['type'] == "m.room.message":
|
||||
if event['content']['msgtype'] == "m.text" or event['content']['msgtype'] == "m.emote":
|
||||
elif event["type"] == "m.room.message":
|
||||
if event["content"]["msgtype"] == "m.text" or event["content"]["msgtype"] == "m.emote":
|
||||
content = ZULIP_MESSAGE_TEMPLATE.format(
|
||||
username=irc_nick, message=event['content']['body']
|
||||
username=irc_nick, message=event["content"]["body"]
|
||||
)
|
||||
else:
|
||||
content = event['type']
|
||||
content = event["type"]
|
||||
return content
|
||||
|
||||
|
||||
|
@ -147,7 +147,7 @@ def zulip_to_matrix(config: Dict[str, Any], room: Any) -> Callable[[Dict[str, An
|
|||
"""
|
||||
message_valid = check_zulip_message_validity(msg, config)
|
||||
if message_valid:
|
||||
matrix_username = msg["sender_full_name"].replace(' ', '')
|
||||
matrix_username = msg["sender_full_name"].replace(" ", "")
|
||||
matrix_text = MATRIX_MESSAGE_TEMPLATE.format(
|
||||
username=matrix_username, message=msg["content"]
|
||||
)
|
||||
|
@ -186,25 +186,25 @@ def generate_parser() -> argparse.ArgumentParser:
|
|||
description=description, formatter_class=argparse.RawTextHelpFormatter
|
||||
)
|
||||
parser.add_argument(
|
||||
'-c', '--config', required=False, help="Path to the config file for the bridge."
|
||||
"-c", "--config", required=False, help="Path to the config file for the bridge."
|
||||
)
|
||||
parser.add_argument(
|
||||
'--write-sample-config',
|
||||
metavar='PATH',
|
||||
dest='sample_config',
|
||||
"--write-sample-config",
|
||||
metavar="PATH",
|
||||
dest="sample_config",
|
||||
help="Generate a configuration template at the specified location.",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--from-zuliprc',
|
||||
metavar='ZULIPRC',
|
||||
dest='zuliprc',
|
||||
"--from-zuliprc",
|
||||
metavar="ZULIPRC",
|
||||
dest="zuliprc",
|
||||
help="Optional path to zuliprc file for bot, when using --write-sample-config",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--show-join-leave',
|
||||
dest='no_noise',
|
||||
"--show-join-leave",
|
||||
dest="no_noise",
|
||||
default=True,
|
||||
action='store_false',
|
||||
action="store_false",
|
||||
help="Enable IRC join/leave events.",
|
||||
)
|
||||
return parser
|
||||
|
@ -218,7 +218,7 @@ def read_configuration(config_file: str) -> Dict[str, Dict[str, str]]:
|
|||
except configparser.Error as exception:
|
||||
raise Bridge_ConfigException(str(exception))
|
||||
|
||||
if set(config.sections()) != {'matrix', 'zulip'}:
|
||||
if set(config.sections()) != {"matrix", "zulip"}:
|
||||
raise Bridge_ConfigException("Please ensure the configuration has zulip & matrix sections.")
|
||||
|
||||
# TODO Could add more checks for configuration content here
|
||||
|
@ -235,25 +235,25 @@ def write_sample_config(target_path: str, zuliprc: Optional[str]) -> None:
|
|||
sample_dict = OrderedDict(
|
||||
(
|
||||
(
|
||||
'matrix',
|
||||
"matrix",
|
||||
OrderedDict(
|
||||
(
|
||||
('host', 'https://matrix.org'),
|
||||
('username', 'username'),
|
||||
('password', 'password'),
|
||||
('room_id', '#zulip:matrix.org'),
|
||||
("host", "https://matrix.org"),
|
||||
("username", "username"),
|
||||
("password", "password"),
|
||||
("room_id", "#zulip:matrix.org"),
|
||||
)
|
||||
),
|
||||
),
|
||||
(
|
||||
'zulip',
|
||||
"zulip",
|
||||
OrderedDict(
|
||||
(
|
||||
('email', 'glitch-bot@chat.zulip.org'),
|
||||
('api_key', 'aPiKeY'),
|
||||
('site', 'https://chat.zulip.org'),
|
||||
('stream', 'test here'),
|
||||
('topic', 'matrix'),
|
||||
("email", "glitch-bot@chat.zulip.org"),
|
||||
("api_key", "aPiKeY"),
|
||||
("site", "https://chat.zulip.org"),
|
||||
("stream", "test here"),
|
||||
("topic", "matrix"),
|
||||
)
|
||||
),
|
||||
),
|
||||
|
@ -272,13 +272,13 @@ def write_sample_config(target_path: str, zuliprc: Optional[str]) -> None:
|
|||
|
||||
# Can add more checks for validity of zuliprc file here
|
||||
|
||||
sample_dict['zulip']['email'] = zuliprc_config['api']['email']
|
||||
sample_dict['zulip']['site'] = zuliprc_config['api']['site']
|
||||
sample_dict['zulip']['api_key'] = zuliprc_config['api']['key']
|
||||
sample_dict["zulip"]["email"] = zuliprc_config["api"]["email"]
|
||||
sample_dict["zulip"]["site"] = zuliprc_config["api"]["site"]
|
||||
sample_dict["zulip"]["api_key"] = zuliprc_config["api"]["key"]
|
||||
|
||||
sample = configparser.ConfigParser()
|
||||
sample.read_dict(sample_dict)
|
||||
with open(target_path, 'w') as target:
|
||||
with open(target_path, "w") as target:
|
||||
sample.write(target)
|
||||
|
||||
|
||||
|
@ -357,5 +357,5 @@ def main() -> None:
|
|||
backoff.fail()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
@ -59,7 +59,7 @@ class MatrixBridgeScriptTests(TestCase):
|
|||
usage = "usage: {} [-h]".format(script_file)
|
||||
description = "Script to bridge"
|
||||
self.assertIn(usage, output_lines[0])
|
||||
blank_lines = [num for num, line in enumerate(output_lines) if line == '']
|
||||
blank_lines = [num for num, line in enumerate(output_lines) if line == ""]
|
||||
# There should be blank lines in the output
|
||||
self.assertTrue(blank_lines)
|
||||
# There should be finite output
|
||||
|
@ -79,9 +79,9 @@ class MatrixBridgeScriptTests(TestCase):
|
|||
def test_write_sample_config_from_zuliprc(self) -> None:
|
||||
zuliprc_template = ["[api]", "email={email}", "key={key}", "site={site}"]
|
||||
zulip_params = {
|
||||
'email': 'foo@bar',
|
||||
'key': 'some_api_key',
|
||||
'site': 'https://some.chat.serverplace',
|
||||
"email": "foo@bar",
|
||||
"key": "some_api_key",
|
||||
"site": "https://some.chat.serverplace",
|
||||
}
|
||||
with new_temp_dir() as tempdir:
|
||||
path = os.path.join(tempdir, sample_config_path)
|
||||
|
@ -103,9 +103,9 @@ class MatrixBridgeScriptTests(TestCase):
|
|||
with open(path) as sample_file:
|
||||
sample_lines = [line.strip() for line in sample_file.readlines()]
|
||||
expected_lines = sample_config_text.split("\n")
|
||||
expected_lines[7] = 'email = {}'.format(zulip_params['email'])
|
||||
expected_lines[8] = 'api_key = {}'.format(zulip_params['key'])
|
||||
expected_lines[9] = 'site = {}'.format(zulip_params['site'])
|
||||
expected_lines[7] = "email = {}".format(zulip_params["email"])
|
||||
expected_lines[8] = "api_key = {}".format(zulip_params["key"])
|
||||
expected_lines[9] = "site = {}".format(zulip_params["site"])
|
||||
self.assertEqual(sample_lines, expected_lines[:-1])
|
||||
|
||||
def test_detect_zuliprc_does_not_exist(self) -> None:
|
||||
|
@ -131,31 +131,31 @@ class MatrixBridgeZulipToMatrixTests(TestCase):
|
|||
valid_msg = dict(
|
||||
sender_email="John@Smith.smith", # must not be equal to config:email
|
||||
type="stream", # Can only mirror Zulip streams
|
||||
display_recipient=valid_zulip_config['stream'],
|
||||
subject=valid_zulip_config['topic'],
|
||||
display_recipient=valid_zulip_config["stream"],
|
||||
subject=valid_zulip_config["topic"],
|
||||
)
|
||||
|
||||
def test_zulip_message_validity_success(self) -> None:
|
||||
zulip_config = self.valid_zulip_config
|
||||
msg = self.valid_msg
|
||||
# Ensure the test inputs are valid for success
|
||||
assert msg['sender_email'] != zulip_config['email']
|
||||
assert msg["sender_email"] != zulip_config["email"]
|
||||
|
||||
self.assertTrue(check_zulip_message_validity(msg, zulip_config))
|
||||
|
||||
def test_zulip_message_validity_failure(self) -> None:
|
||||
zulip_config = self.valid_zulip_config
|
||||
|
||||
msg_wrong_stream = dict(self.valid_msg, display_recipient='foo')
|
||||
msg_wrong_stream = dict(self.valid_msg, display_recipient="foo")
|
||||
self.assertFalse(check_zulip_message_validity(msg_wrong_stream, zulip_config))
|
||||
|
||||
msg_wrong_topic = dict(self.valid_msg, subject='foo')
|
||||
msg_wrong_topic = dict(self.valid_msg, subject="foo")
|
||||
self.assertFalse(check_zulip_message_validity(msg_wrong_topic, zulip_config))
|
||||
|
||||
msg_not_stream = dict(self.valid_msg, type="private")
|
||||
self.assertFalse(check_zulip_message_validity(msg_not_stream, zulip_config))
|
||||
|
||||
msg_from_bot = dict(self.valid_msg, sender_email=zulip_config['email'])
|
||||
msg_from_bot = dict(self.valid_msg, sender_email=zulip_config["email"])
|
||||
self.assertFalse(check_zulip_message_validity(msg_from_bot, zulip_config))
|
||||
|
||||
def test_zulip_to_matrix(self) -> None:
|
||||
|
@ -166,14 +166,14 @@ class MatrixBridgeZulipToMatrixTests(TestCase):
|
|||
msg = dict(self.valid_msg, sender_full_name="John Smith")
|
||||
|
||||
expected = {
|
||||
'hi': '{} hi',
|
||||
'*hi*': '{} *hi*',
|
||||
'**hi**': '{} **hi**',
|
||||
"hi": "{} hi",
|
||||
"*hi*": "{} *hi*",
|
||||
"**hi**": "{} **hi**",
|
||||
}
|
||||
|
||||
for content in expected:
|
||||
send_msg(dict(msg, content=content))
|
||||
|
||||
for (method, params, _), expect in zip(room.method_calls, expected.values()):
|
||||
self.assertEqual(method, 'send_text')
|
||||
self.assertEqual(params[0], expect.format('<JohnSmith>'))
|
||||
self.assertEqual(method, "send_text")
|
||||
self.assertEqual(params[0], expect.format("<JohnSmith>"))
|
||||
|
|
|
@ -55,17 +55,17 @@ class SlackBridge:
|
|||
self.slack_webclient = slack_sdk.WebClient(token=self.slack_config["token"])
|
||||
|
||||
def wrap_slack_mention_with_bracket(self, zulip_msg: Dict[str, Any]) -> None:
|
||||
words = zulip_msg["content"].split(' ')
|
||||
words = zulip_msg["content"].split(" ")
|
||||
for w in words:
|
||||
if w.startswith('@'):
|
||||
zulip_msg["content"] = zulip_msg["content"].replace(w, '<' + w + '>')
|
||||
if w.startswith("@"):
|
||||
zulip_msg["content"] = zulip_msg["content"].replace(w, "<" + w + ">")
|
||||
|
||||
def replace_slack_id_with_name(self, msg: Dict[str, Any]) -> None:
|
||||
words = msg['text'].split(' ')
|
||||
words = msg["text"].split(" ")
|
||||
for w in words:
|
||||
if w.startswith('<@') and w.endswith('>'):
|
||||
if w.startswith("<@") and w.endswith(">"):
|
||||
_id = w[2:-1]
|
||||
msg['text'] = msg['text'].replace(_id, self.slack_id_to_name[_id])
|
||||
msg["text"] = msg["text"].replace(_id, self.slack_id_to_name[_id])
|
||||
|
||||
def zulip_to_slack(self) -> Callable[[Dict[str, Any]], None]:
|
||||
def _zulip_to_slack(msg: Dict[str, Any]) -> None:
|
||||
|
@ -83,25 +83,25 @@ class SlackBridge:
|
|||
return _zulip_to_slack
|
||||
|
||||
def run_slack_listener(self) -> None:
|
||||
members = self.slack_webclient.users_list()['members']
|
||||
members = self.slack_webclient.users_list()["members"]
|
||||
# See also https://api.slack.com/changelog/2017-09-the-one-about-usernames
|
||||
self.slack_id_to_name = {
|
||||
u["id"]: u["profile"].get("display_name", u["profile"]["real_name"]) for u in members
|
||||
}
|
||||
self.slack_name_to_id = {v: k for k, v in self.slack_id_to_name.items()}
|
||||
|
||||
@RTMClient.run_on(event='message')
|
||||
@RTMClient.run_on(event="message")
|
||||
def slack_to_zulip(**payload: Any) -> None:
|
||||
msg = payload['data']
|
||||
if msg['channel'] != self.channel:
|
||||
msg = payload["data"]
|
||||
if msg["channel"] != self.channel:
|
||||
return
|
||||
user_id = msg['user']
|
||||
user_id = msg["user"]
|
||||
user = self.slack_id_to_name[user_id]
|
||||
from_bot = user == self.slack_config['username']
|
||||
from_bot = user == self.slack_config["username"]
|
||||
if from_bot:
|
||||
return
|
||||
self.replace_slack_id_with_name(msg)
|
||||
content = ZULIP_MESSAGE_TEMPLATE.format(username=user, message=msg['text'])
|
||||
content = ZULIP_MESSAGE_TEMPLATE.format(username=user, message=msg["text"])
|
||||
msg_data = dict(
|
||||
type="stream", to=self.zulip_stream, subject=self.zulip_subject, content=content
|
||||
)
|
||||
|
@ -117,7 +117,7 @@ if __name__ == "__main__":
|
|||
the first realm to a channel in a Slack workspace.
|
||||
"""
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
|
||||
parser = argparse.ArgumentParser(usage=usage)
|
||||
|
||||
print("Starting slack mirroring bot")
|
||||
|
|
|
@ -44,7 +44,7 @@ client = zulip.Client(
|
|||
user_agent = "Codebase To Zulip Mirroring script (zulip-devel@googlegroups.com)"
|
||||
|
||||
# find some form of JSON loader/dumper, with a preference order for speed.
|
||||
json_implementations = ['ujson', 'cjson', 'simplejson', 'json']
|
||||
json_implementations = ["ujson", "cjson", "simplejson", "json"]
|
||||
|
||||
while len(json_implementations):
|
||||
try:
|
||||
|
@ -58,7 +58,7 @@ def make_api_call(path: str) -> Optional[List[Dict[str, Any]]]:
|
|||
response = requests.get(
|
||||
"https://api3.codebasehq.com/%s" % (path,),
|
||||
auth=(config.CODEBASE_API_USERNAME, config.CODEBASE_API_KEY),
|
||||
params={'raw': 'True'},
|
||||
params={"raw": "True"},
|
||||
headers={
|
||||
"User-Agent": user_agent,
|
||||
"Content-Type": "application/json",
|
||||
|
@ -86,36 +86,36 @@ def make_url(path: str) -> str:
|
|||
|
||||
|
||||
def handle_event(event: Dict[str, Any]) -> None:
|
||||
event = event['event']
|
||||
event_type = event['type']
|
||||
actor_name = event['actor_name']
|
||||
event = event["event"]
|
||||
event_type = event["type"]
|
||||
actor_name = event["actor_name"]
|
||||
|
||||
raw_props = event.get('raw_properties', {})
|
||||
raw_props = event.get("raw_properties", {})
|
||||
|
||||
project_link = raw_props.get('project_permalink')
|
||||
project_link = raw_props.get("project_permalink")
|
||||
|
||||
subject = None
|
||||
content = None
|
||||
if event_type == 'repository_creation':
|
||||
if event_type == "repository_creation":
|
||||
stream = config.ZULIP_COMMITS_STREAM_NAME
|
||||
|
||||
project_name = raw_props.get('name')
|
||||
project_repo_type = raw_props.get('scm_type')
|
||||
project_name = raw_props.get("name")
|
||||
project_repo_type = raw_props.get("scm_type")
|
||||
|
||||
url = make_url("projects/%s" % (project_link,))
|
||||
scm = "of type %s" % (project_repo_type,) if project_repo_type else ""
|
||||
|
||||
subject = "Repository %s Created" % (project_name,)
|
||||
content = "%s created a new repository %s [%s](%s)" % (actor_name, scm, project_name, url)
|
||||
elif event_type == 'push':
|
||||
elif event_type == "push":
|
||||
stream = config.ZULIP_COMMITS_STREAM_NAME
|
||||
|
||||
num_commits = raw_props.get('commits_count')
|
||||
branch = raw_props.get('ref_name')
|
||||
project = raw_props.get('project_name')
|
||||
repo_link = raw_props.get('repository_permalink')
|
||||
deleted_ref = raw_props.get('deleted_ref')
|
||||
new_ref = raw_props.get('new_ref')
|
||||
num_commits = raw_props.get("commits_count")
|
||||
branch = raw_props.get("ref_name")
|
||||
project = raw_props.get("project_name")
|
||||
repo_link = raw_props.get("repository_permalink")
|
||||
deleted_ref = raw_props.get("deleted_ref")
|
||||
new_ref = raw_props.get("new_ref")
|
||||
|
||||
subject = "Push to %s on %s" % (branch, project)
|
||||
|
||||
|
@ -130,20 +130,20 @@ def handle_event(event: Dict[str, Any]) -> None:
|
|||
branch,
|
||||
project,
|
||||
)
|
||||
for commit in raw_props.get('commits'):
|
||||
ref = commit.get('ref')
|
||||
for commit in raw_props.get("commits"):
|
||||
ref = commit.get("ref")
|
||||
url = make_url(
|
||||
"projects/%s/repositories/%s/commit/%s" % (project_link, repo_link, ref)
|
||||
)
|
||||
message = commit.get('message')
|
||||
message = commit.get("message")
|
||||
content += "* [%s](%s): %s\n" % (ref, url, message)
|
||||
elif event_type == 'ticketing_ticket':
|
||||
elif event_type == "ticketing_ticket":
|
||||
stream = config.ZULIP_TICKETS_STREAM_NAME
|
||||
|
||||
num = raw_props.get('number')
|
||||
name = raw_props.get('subject')
|
||||
assignee = raw_props.get('assignee')
|
||||
priority = raw_props.get('priority')
|
||||
num = raw_props.get("number")
|
||||
name = raw_props.get("subject")
|
||||
assignee = raw_props.get("assignee")
|
||||
priority = raw_props.get("priority")
|
||||
url = make_url("projects/%s/tickets/%s" % (project_link, num))
|
||||
|
||||
if assignee is None:
|
||||
|
@ -153,13 +153,13 @@ def handle_event(event: Dict[str, Any]) -> None:
|
|||
"""%s created a new ticket [#%s](%s) priority **%s** assigned to %s:\n\n~~~ quote\n %s"""
|
||||
% (actor_name, num, url, priority, assignee, name)
|
||||
)
|
||||
elif event_type == 'ticketing_note':
|
||||
elif event_type == "ticketing_note":
|
||||
stream = config.ZULIP_TICKETS_STREAM_NAME
|
||||
|
||||
num = raw_props.get('number')
|
||||
name = raw_props.get('subject')
|
||||
body = raw_props.get('content')
|
||||
changes = raw_props.get('changes')
|
||||
num = raw_props.get("number")
|
||||
name = raw_props.get("subject")
|
||||
body = raw_props.get("content")
|
||||
changes = raw_props.get("changes")
|
||||
|
||||
url = make_url("projects/%s/tickets/%s" % (project_link, num))
|
||||
subject = "#%s: %s" % (num, name)
|
||||
|
@ -173,33 +173,33 @@ def handle_event(event: Dict[str, Any]) -> None:
|
|||
body,
|
||||
)
|
||||
|
||||
if 'status_id' in changes:
|
||||
status_change = changes.get('status_id')
|
||||
if "status_id" in changes:
|
||||
status_change = changes.get("status_id")
|
||||
content += "Status changed from **%s** to **%s**\n\n" % (
|
||||
status_change[0],
|
||||
status_change[1],
|
||||
)
|
||||
elif event_type == 'ticketing_milestone':
|
||||
elif event_type == "ticketing_milestone":
|
||||
stream = config.ZULIP_TICKETS_STREAM_NAME
|
||||
|
||||
name = raw_props.get('name')
|
||||
identifier = raw_props.get('identifier')
|
||||
name = raw_props.get("name")
|
||||
identifier = raw_props.get("identifier")
|
||||
url = make_url("projects/%s/milestone/%s" % (project_link, identifier))
|
||||
|
||||
subject = name
|
||||
content = "%s created a new milestone [%s](%s)" % (actor_name, name, url)
|
||||
elif event_type == 'comment':
|
||||
elif event_type == "comment":
|
||||
stream = config.ZULIP_COMMITS_STREAM_NAME
|
||||
|
||||
comment = raw_props.get('content')
|
||||
commit = raw_props.get('commit_ref')
|
||||
comment = raw_props.get("content")
|
||||
commit = raw_props.get("commit_ref")
|
||||
|
||||
# If there's a commit id, it's a comment to a commit
|
||||
if commit:
|
||||
repo_link = raw_props.get('repository_permalink')
|
||||
repo_link = raw_props.get("repository_permalink")
|
||||
|
||||
url = make_url(
|
||||
'projects/%s/repositories/%s/commit/%s' % (project_link, repo_link, commit)
|
||||
"projects/%s/repositories/%s/commit/%s" % (project_link, repo_link, commit)
|
||||
)
|
||||
|
||||
subject = "%s commented on %s" % (actor_name, commit)
|
||||
|
@ -223,14 +223,14 @@ def handle_event(event: Dict[str, Any]) -> None:
|
|||
else:
|
||||
content = "%s posted:\n\n~~~ quote\n%s\n~~~" % (actor_name, comment_content)
|
||||
|
||||
elif event_type == 'deployment':
|
||||
elif event_type == "deployment":
|
||||
stream = config.ZULIP_COMMITS_STREAM_NAME
|
||||
|
||||
start_ref = raw_props.get('start_ref')
|
||||
end_ref = raw_props.get('end_ref')
|
||||
environment = raw_props.get('environment')
|
||||
servers = raw_props.get('servers')
|
||||
repo_link = raw_props.get('repository_permalink')
|
||||
start_ref = raw_props.get("start_ref")
|
||||
end_ref = raw_props.get("end_ref")
|
||||
environment = raw_props.get("environment")
|
||||
servers = raw_props.get("servers")
|
||||
repo_link = raw_props.get("repository_permalink")
|
||||
|
||||
start_ref_url = make_url(
|
||||
"projects/%s/repositories/%s/commit/%s" % (project_link, repo_link, start_ref)
|
||||
|
@ -259,30 +259,30 @@ def handle_event(event: Dict[str, Any]) -> None:
|
|||
", ".join(["`%s`" % (server,) for server in servers])
|
||||
)
|
||||
|
||||
elif event_type == 'named_tree':
|
||||
elif event_type == "named_tree":
|
||||
# Docs say named_tree type used for new/deleting branches and tags,
|
||||
# but experimental testing showed that they were all sent as 'push' events
|
||||
pass
|
||||
elif event_type == 'wiki_page':
|
||||
elif event_type == "wiki_page":
|
||||
logging.warn("Wiki page notifications not yet implemented")
|
||||
elif event_type == 'sprint_creation':
|
||||
elif event_type == "sprint_creation":
|
||||
logging.warn("Sprint notifications not yet implemented")
|
||||
elif event_type == 'sprint_ended':
|
||||
elif event_type == "sprint_ended":
|
||||
logging.warn("Sprint notifications not yet implemented")
|
||||
else:
|
||||
logging.info("Unknown event type %s, ignoring!" % (event_type,))
|
||||
|
||||
if subject and content:
|
||||
if len(subject) > 60:
|
||||
subject = subject[:57].rstrip() + '...'
|
||||
subject = subject[:57].rstrip() + "..."
|
||||
|
||||
res = client.send_message(
|
||||
{"type": "stream", "to": stream, "subject": subject, "content": content}
|
||||
)
|
||||
if res['result'] == 'success':
|
||||
logging.info("Successfully sent Zulip with id: %s" % (res['id'],))
|
||||
if res["result"] == "success":
|
||||
logging.info("Successfully sent Zulip with id: %s" % (res["id"],))
|
||||
else:
|
||||
logging.warn("Failed to send Zulip: %s %s" % (res['result'], res['msg']))
|
||||
logging.warn("Failed to send Zulip: %s %s" % (res["result"], res["msg"]))
|
||||
|
||||
|
||||
# the main run loop for this mirror script
|
||||
|
@ -295,7 +295,7 @@ def run_mirror() -> None:
|
|||
try:
|
||||
with open(config.RESUME_FILE) as f:
|
||||
timestamp = f.read()
|
||||
if timestamp == '':
|
||||
if timestamp == "":
|
||||
since = default_since()
|
||||
else:
|
||||
since = datetime.fromtimestamp(float(timestamp), tz=pytz.utc)
|
||||
|
@ -310,7 +310,7 @@ def run_mirror() -> None:
|
|||
if events is not None:
|
||||
sleepInterval = 1
|
||||
for event in events[::-1]:
|
||||
timestamp = event.get('event', {}).get('timestamp', '')
|
||||
timestamp = event.get("event", {}).get("timestamp", "")
|
||||
event_date = dateutil.parser.parse(timestamp)
|
||||
if event_date > since:
|
||||
handle_event(event)
|
||||
|
@ -322,7 +322,7 @@ def run_mirror() -> None:
|
|||
time.sleep(sleepInterval)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
open(config.RESUME_FILE, 'w').write(since.strftime("%s"))
|
||||
open(config.RESUME_FILE, "w").write(since.strftime("%s"))
|
||||
logging.info("Shutting down Codebase mirror")
|
||||
|
||||
|
||||
|
|
|
@ -43,19 +43,19 @@ def git_repository_name() -> Text:
|
|||
|
||||
def git_commit_range(oldrev: str, newrev: str) -> str:
|
||||
log_cmd = ["git", "log", "--reverse", "--pretty=%aE %H %s", "%s..%s" % (oldrev, newrev)]
|
||||
commits = ''
|
||||
commits = ""
|
||||
for ln in subprocess.check_output(log_cmd, universal_newlines=True).splitlines():
|
||||
author_email, commit_id, subject = ln.split(None, 2)
|
||||
if hasattr(config, "format_commit_message"):
|
||||
commits += config.format_commit_message(author_email, subject, commit_id)
|
||||
else:
|
||||
commits += '!avatar(%s) %s\n' % (author_email, subject)
|
||||
commits += "!avatar(%s) %s\n" % (author_email, subject)
|
||||
return commits
|
||||
|
||||
|
||||
def send_bot_message(oldrev: str, newrev: str, refname: str) -> None:
|
||||
repo_name = git_repository_name()
|
||||
branch = refname.replace('refs/heads/', '')
|
||||
branch = refname.replace("refs/heads/", "")
|
||||
destination = config.commit_notice_destination(repo_name, branch, newrev)
|
||||
if destination is None:
|
||||
# Don't forward the notice anywhere
|
||||
|
@ -65,30 +65,30 @@ def send_bot_message(oldrev: str, newrev: str, refname: str) -> None:
|
|||
old_head = oldrev[:12]
|
||||
|
||||
if (
|
||||
oldrev == '0000000000000000000000000000000000000000'
|
||||
or newrev == '0000000000000000000000000000000000000000'
|
||||
oldrev == "0000000000000000000000000000000000000000"
|
||||
or newrev == "0000000000000000000000000000000000000000"
|
||||
):
|
||||
# New branch pushed or old branch removed
|
||||
added = ''
|
||||
removed = ''
|
||||
added = ""
|
||||
removed = ""
|
||||
else:
|
||||
added = git_commit_range(oldrev, newrev)
|
||||
removed = git_commit_range(newrev, oldrev)
|
||||
|
||||
if oldrev == '0000000000000000000000000000000000000000':
|
||||
message = '`%s` was pushed to new branch `%s`' % (new_head, branch)
|
||||
elif newrev == '0000000000000000000000000000000000000000':
|
||||
message = 'branch `%s` was removed (was `%s`)' % (branch, old_head)
|
||||
if oldrev == "0000000000000000000000000000000000000000":
|
||||
message = "`%s` was pushed to new branch `%s`" % (new_head, branch)
|
||||
elif newrev == "0000000000000000000000000000000000000000":
|
||||
message = "branch `%s` was removed (was `%s`)" % (branch, old_head)
|
||||
elif removed:
|
||||
message = '`%s` was pushed to `%s`, **REMOVING**:\n\n%s' % (new_head, branch, removed)
|
||||
message = "`%s` was pushed to `%s`, **REMOVING**:\n\n%s" % (new_head, branch, removed)
|
||||
if added:
|
||||
message += '\n**and adding**:\n\n' + added
|
||||
message += '\n**A HISTORY REWRITE HAS OCCURRED!**'
|
||||
message += '\n@everyone: Please check your local branches to deal with this.'
|
||||
message += "\n**and adding**:\n\n" + added
|
||||
message += "\n**A HISTORY REWRITE HAS OCCURRED!**"
|
||||
message += "\n@everyone: Please check your local branches to deal with this."
|
||||
elif added:
|
||||
message = '`%s` was deployed to `%s` with:\n\n%s' % (new_head, branch, added)
|
||||
message = "`%s` was deployed to `%s` with:\n\n%s" % (new_head, branch, added)
|
||||
else:
|
||||
message = '`%s` was pushed to `%s`... but nothing changed?' % (new_head, branch)
|
||||
message = "`%s` was pushed to `%s`... but nothing changed?" % (new_head, branch)
|
||||
|
||||
message_data = {
|
||||
"type": "stream",
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
from typing import Dict, Optional, Text
|
||||
|
||||
# Name of the stream to send notifications to, default is "commits"
|
||||
STREAM_NAME = 'commits'
|
||||
STREAM_NAME = "commits"
|
||||
|
||||
# Change these values to configure authentication for the plugin
|
||||
ZULIP_USER = "git-bot@example.com"
|
||||
|
@ -37,7 +37,7 @@ def commit_notice_destination(repo: Text, branch: Text, commit: Text) -> Optiona
|
|||
#
|
||||
# return '!avatar(%s) [%s](https://example.com/commits/%s)\n' % (author, subject, commit_id)
|
||||
def format_commit_message(author: Text, subject: Text, commit_id: Text) -> Text:
|
||||
return '!avatar(%s) %s\n' % (author, subject)
|
||||
return "!avatar(%s) %s\n" % (author, subject)
|
||||
|
||||
|
||||
## If properly installed, the Zulip API should be in your import
|
||||
|
|
|
@ -18,12 +18,12 @@ except ImportError:
|
|||
# at zulip/bots/gcal/
|
||||
# NOTE: When adding more scopes, add them after the previous one in the same field, with a space
|
||||
# seperating them.
|
||||
SCOPES = 'https://www.googleapis.com/auth/calendar.readonly'
|
||||
SCOPES = "https://www.googleapis.com/auth/calendar.readonly"
|
||||
# This file contains the information that google uses to figure out which application is requesting
|
||||
# this client's data.
|
||||
CLIENT_SECRET_FILE = 'client_secret.json'
|
||||
APPLICATION_NAME = 'Zulip Calendar Bot'
|
||||
HOME_DIR = os.path.expanduser('~')
|
||||
CLIENT_SECRET_FILE = "client_secret.json"
|
||||
APPLICATION_NAME = "Zulip Calendar Bot"
|
||||
HOME_DIR = os.path.expanduser("~")
|
||||
|
||||
|
||||
def get_credentials() -> client.Credentials:
|
||||
|
@ -36,7 +36,7 @@ def get_credentials() -> client.Credentials:
|
|||
Credentials, the obtained credential.
|
||||
"""
|
||||
|
||||
credential_path = os.path.join(HOME_DIR, 'google-credentials.json')
|
||||
credential_path = os.path.join(HOME_DIR, "google-credentials.json")
|
||||
|
||||
store = Storage(credential_path)
|
||||
credentials = store.get()
|
||||
|
@ -50,7 +50,7 @@ def get_credentials() -> client.Credentials:
|
|||
credentials = tools.run_flow(flow, store, flags)
|
||||
else: # Needed only for compatibility with Python 2.6
|
||||
credentials = tools.run(flow, store)
|
||||
print('Storing credentials to ' + credential_path)
|
||||
print("Storing credentials to " + credential_path)
|
||||
|
||||
|
||||
get_credentials()
|
||||
|
|
|
@ -20,15 +20,15 @@ from oauth2client.file import Storage
|
|||
try:
|
||||
from googleapiclient import discovery
|
||||
except ImportError:
|
||||
logging.exception('Install google-api-python-client')
|
||||
logging.exception("Install google-api-python-client")
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '../../'))
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), "../../"))
|
||||
import zulip
|
||||
|
||||
SCOPES = 'https://www.googleapis.com/auth/calendar.readonly'
|
||||
CLIENT_SECRET_FILE = 'client_secret.json'
|
||||
APPLICATION_NAME = 'Zulip'
|
||||
HOME_DIR = os.path.expanduser('~')
|
||||
SCOPES = "https://www.googleapis.com/auth/calendar.readonly"
|
||||
CLIENT_SECRET_FILE = "client_secret.json"
|
||||
APPLICATION_NAME = "Zulip"
|
||||
HOME_DIR = os.path.expanduser("~")
|
||||
|
||||
# Our cached view of the calendar, updated periodically.
|
||||
events = [] # type: List[Tuple[int, datetime.datetime, str]]
|
||||
|
@ -61,28 +61,28 @@ google-calendar --calendar calendarID@example.calendar.google.com
|
|||
|
||||
|
||||
parser.add_argument(
|
||||
'--interval',
|
||||
dest='interval',
|
||||
"--interval",
|
||||
dest="interval",
|
||||
default=30,
|
||||
type=int,
|
||||
action='store',
|
||||
help='Minutes before event for reminder [default: 30]',
|
||||
metavar='MINUTES',
|
||||
action="store",
|
||||
help="Minutes before event for reminder [default: 30]",
|
||||
metavar="MINUTES",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--calendar',
|
||||
dest='calendarID',
|
||||
default='primary',
|
||||
"--calendar",
|
||||
dest="calendarID",
|
||||
default="primary",
|
||||
type=str,
|
||||
action='store',
|
||||
help='Calendar ID for the calendar you want to receive reminders from.',
|
||||
action="store",
|
||||
help="Calendar ID for the calendar you want to receive reminders from.",
|
||||
)
|
||||
|
||||
options = parser.parse_args()
|
||||
|
||||
if not (options.zulip_email):
|
||||
parser.error('You must specify --user')
|
||||
parser.error("You must specify --user")
|
||||
|
||||
zulip_client = zulip.init_from_options(options)
|
||||
|
||||
|
@ -98,14 +98,14 @@ def get_credentials() -> client.Credentials:
|
|||
Credentials, the obtained credential.
|
||||
"""
|
||||
try:
|
||||
credential_path = os.path.join(HOME_DIR, 'google-credentials.json')
|
||||
credential_path = os.path.join(HOME_DIR, "google-credentials.json")
|
||||
|
||||
store = Storage(credential_path)
|
||||
credentials = store.get()
|
||||
|
||||
return credentials
|
||||
except client.Error:
|
||||
logging.exception('Error while trying to open the `google-credentials.json` file.')
|
||||
logging.exception("Error while trying to open the `google-credentials.json` file.")
|
||||
except OSError:
|
||||
logging.error("Run the get-google-credentials script from this directory first.")
|
||||
|
||||
|
@ -115,7 +115,7 @@ def populate_events() -> Optional[None]:
|
|||
|
||||
credentials = get_credentials()
|
||||
creds = credentials.authorize(httplib2.Http())
|
||||
service = discovery.build('calendar', 'v3', http=creds)
|
||||
service = discovery.build("calendar", "v3", http=creds)
|
||||
|
||||
now = datetime.datetime.now(pytz.utc).isoformat()
|
||||
feed = (
|
||||
|
@ -125,7 +125,7 @@ def populate_events() -> Optional[None]:
|
|||
timeMin=now,
|
||||
maxResults=5,
|
||||
singleEvents=True,
|
||||
orderBy='startTime',
|
||||
orderBy="startTime",
|
||||
)
|
||||
.execute()
|
||||
)
|
||||
|
@ -174,10 +174,10 @@ def send_reminders() -> Optional[None]:
|
|||
key = (id, start)
|
||||
if key not in sent:
|
||||
if start.hour == 0 and start.minute == 0:
|
||||
line = '%s is today.' % (summary,)
|
||||
line = "%s is today." % (summary,)
|
||||
else:
|
||||
line = '%s starts at %s' % (summary, start.strftime('%H:%M'))
|
||||
print('Sending reminder:', line)
|
||||
line = "%s starts at %s" % (summary, start.strftime("%H:%M"))
|
||||
print("Sending reminder:", line)
|
||||
messages.append(line)
|
||||
keys.add(key)
|
||||
|
||||
|
@ -185,12 +185,12 @@ def send_reminders() -> Optional[None]:
|
|||
return
|
||||
|
||||
if len(messages) == 1:
|
||||
message = 'Reminder: ' + messages[0]
|
||||
message = "Reminder: " + messages[0]
|
||||
else:
|
||||
message = 'Reminder:\n\n' + '\n'.join('* ' + m for m in messages)
|
||||
message = "Reminder:\n\n" + "\n".join("* " + m for m in messages)
|
||||
|
||||
zulip_client.send_message(
|
||||
dict(type='private', to=options.zulip_email, sender=options.zulip_email, content=message)
|
||||
dict(type="private", to=options.zulip_email, sender=options.zulip_email, content=message)
|
||||
)
|
||||
|
||||
sent.update(keys)
|
||||
|
|
|
@ -94,7 +94,7 @@ def send_zulip(
|
|||
def get_config(ui: ui, item: str) -> str:
|
||||
try:
|
||||
# config returns configuration value.
|
||||
return ui.config('zulip', item)
|
||||
return ui.config("zulip", item)
|
||||
except IndexError:
|
||||
ui.warn("Zulip: Could not find required item {} in hg config.".format(item))
|
||||
sys.exit(1)
|
||||
|
|
|
@ -62,7 +62,7 @@ def stream_to_room(stream: str) -> str:
|
|||
|
||||
|
||||
def jid_to_zulip(jid: JID) -> str:
|
||||
suffix = ''
|
||||
suffix = ""
|
||||
if not jid.username.endswith("-bot"):
|
||||
suffix = options.zulip_email_suffix
|
||||
return "%s%s@%s" % (jid.username, suffix, options.zulip_domain)
|
||||
|
@ -94,10 +94,10 @@ class JabberToZulipBot(ClientXMPP):
|
|||
self.zulip = None
|
||||
self.use_ipv6 = False
|
||||
|
||||
self.register_plugin('xep_0045') # Jabber chatrooms
|
||||
self.register_plugin('xep_0199') # XMPP Ping
|
||||
self.register_plugin("xep_0045") # Jabber chatrooms
|
||||
self.register_plugin("xep_0199") # XMPP Ping
|
||||
|
||||
def set_zulip_client(self, zulipToJabberClient: 'ZulipToJabberBot') -> None:
|
||||
def set_zulip_client(self, zulipToJabberClient: "ZulipToJabberBot") -> None:
|
||||
self.zulipToJabber = zulipToJabberClient
|
||||
|
||||
def session_start(self, event: Dict[str, Any]) -> None:
|
||||
|
@ -112,7 +112,7 @@ class JabberToZulipBot(ClientXMPP):
|
|||
logging.debug("Joining " + room)
|
||||
self.rooms.add(room)
|
||||
muc_jid = JID(local=room, domain=options.conference_domain)
|
||||
xep0045 = self.plugin['xep_0045']
|
||||
xep0045 = self.plugin["xep_0045"]
|
||||
try:
|
||||
xep0045.joinMUC(muc_jid, self.nick, wait=True)
|
||||
except InvalidJID:
|
||||
|
@ -137,7 +137,7 @@ class JabberToZulipBot(ClientXMPP):
|
|||
logging.debug("Leaving " + room)
|
||||
self.rooms.remove(room)
|
||||
muc_jid = JID(local=room, domain=options.conference_domain)
|
||||
self.plugin['xep_0045'].leaveMUC(muc_jid, self.nick)
|
||||
self.plugin["xep_0045"].leaveMUC(muc_jid, self.nick)
|
||||
|
||||
def message(self, msg: JabberMessage) -> Any:
|
||||
try:
|
||||
|
@ -152,7 +152,7 @@ class JabberToZulipBot(ClientXMPP):
|
|||
logging.exception("Error forwarding Jabber => Zulip")
|
||||
|
||||
def private(self, msg: JabberMessage) -> None:
|
||||
if options.mode == 'public' or msg['thread'] == '\u1FFFE':
|
||||
if options.mode == "public" or msg["thread"] == "\u1FFFE":
|
||||
return
|
||||
sender = jid_to_zulip(msg["from"])
|
||||
recipient = jid_to_zulip(msg["to"])
|
||||
|
@ -168,13 +168,13 @@ class JabberToZulipBot(ClientXMPP):
|
|||
logging.error(str(ret))
|
||||
|
||||
def group(self, msg: JabberMessage) -> None:
|
||||
if options.mode == 'personal' or msg["thread"] == '\u1FFFE':
|
||||
if options.mode == "personal" or msg["thread"] == "\u1FFFE":
|
||||
return
|
||||
|
||||
subject = msg["subject"]
|
||||
if len(subject) == 0:
|
||||
subject = "(no topic)"
|
||||
stream = room_to_stream(msg['from'].local)
|
||||
stream = room_to_stream(msg["from"].local)
|
||||
sender_nick = msg.get_mucnick()
|
||||
if not sender_nick:
|
||||
# Messages from the room itself have no nickname. We should not try
|
||||
|
@ -195,9 +195,9 @@ class JabberToZulipBot(ClientXMPP):
|
|||
logging.error(str(ret))
|
||||
|
||||
def nickname_to_jid(self, room: str, nick: str) -> JID:
|
||||
jid = self.plugin['xep_0045'].getJidProperty(room, nick, "jid")
|
||||
if jid is None or jid == '':
|
||||
return JID(local=nick.replace(' ', ''), domain=self.boundjid.domain)
|
||||
jid = self.plugin["xep_0045"].getJidProperty(room, nick, "jid")
|
||||
if jid is None or jid == "":
|
||||
return JID(local=nick.replace(" ", ""), domain=self.boundjid.domain)
|
||||
else:
|
||||
return jid
|
||||
|
||||
|
@ -211,59 +211,59 @@ class ZulipToJabberBot:
|
|||
self.jabber = client
|
||||
|
||||
def process_event(self, event: Dict[str, Any]) -> None:
|
||||
if event['type'] == 'message':
|
||||
if event["type"] == "message":
|
||||
message = event["message"]
|
||||
if message['sender_email'] != self.client.email:
|
||||
if message["sender_email"] != self.client.email:
|
||||
return
|
||||
|
||||
try:
|
||||
if message['type'] == 'stream':
|
||||
if message["type"] == "stream":
|
||||
self.stream_message(message)
|
||||
elif message['type'] == 'private':
|
||||
elif message["type"] == "private":
|
||||
self.private_message(message)
|
||||
except Exception:
|
||||
logging.exception("Exception forwarding Zulip => Jabber")
|
||||
elif event['type'] == 'subscription':
|
||||
elif event["type"] == "subscription":
|
||||
self.process_subscription(event)
|
||||
|
||||
def stream_message(self, msg: Dict[str, str]) -> None:
|
||||
assert self.jabber is not None
|
||||
stream = msg['display_recipient']
|
||||
stream = msg["display_recipient"]
|
||||
if not stream.endswith("/xmpp"):
|
||||
return
|
||||
|
||||
room = stream_to_room(stream)
|
||||
jabber_recipient = JID(local=room, domain=options.conference_domain)
|
||||
outgoing = self.jabber.make_message(
|
||||
mto=jabber_recipient, mbody=msg['content'], mtype='groupchat'
|
||||
mto=jabber_recipient, mbody=msg["content"], mtype="groupchat"
|
||||
)
|
||||
outgoing['thread'] = '\u1FFFE'
|
||||
outgoing["thread"] = "\u1FFFE"
|
||||
outgoing.send()
|
||||
|
||||
def private_message(self, msg: Dict[str, Any]) -> None:
|
||||
assert self.jabber is not None
|
||||
for recipient in msg['display_recipient']:
|
||||
for recipient in msg["display_recipient"]:
|
||||
if recipient["email"] == self.client.email:
|
||||
continue
|
||||
if not recipient["is_mirror_dummy"]:
|
||||
continue
|
||||
recip_email = recipient['email']
|
||||
recip_email = recipient["email"]
|
||||
jabber_recipient = zulip_to_jid(recip_email, self.jabber.boundjid.domain)
|
||||
outgoing = self.jabber.make_message(
|
||||
mto=jabber_recipient, mbody=msg['content'], mtype='chat'
|
||||
mto=jabber_recipient, mbody=msg["content"], mtype="chat"
|
||||
)
|
||||
outgoing['thread'] = '\u1FFFE'
|
||||
outgoing["thread"] = "\u1FFFE"
|
||||
outgoing.send()
|
||||
|
||||
def process_subscription(self, event: Dict[str, Any]) -> None:
|
||||
assert self.jabber is not None
|
||||
if event['op'] == 'add':
|
||||
streams = [s['name'].lower() for s in event['subscriptions']]
|
||||
if event["op"] == "add":
|
||||
streams = [s["name"].lower() for s in event["subscriptions"]]
|
||||
streams = [s for s in streams if s.endswith("/xmpp")]
|
||||
for stream in streams:
|
||||
self.jabber.join_muc(stream_to_room(stream))
|
||||
if event['op'] == 'remove':
|
||||
streams = [s['name'].lower() for s in event['subscriptions']]
|
||||
if event["op"] == "remove":
|
||||
streams = [s["name"].lower() for s in event["subscriptions"]]
|
||||
streams = [s for s in streams if s.endswith("/xmpp")]
|
||||
for stream in streams:
|
||||
self.jabber.leave_muc(stream_to_room(stream))
|
||||
|
@ -277,14 +277,14 @@ def get_rooms(zulipToJabber: ZulipToJabberBot) -> List[str]:
|
|||
sys.exit("Could not get initial list of Zulip %s" % (key,))
|
||||
return ret[key]
|
||||
|
||||
if options.mode == 'public':
|
||||
if options.mode == "public":
|
||||
stream_infos = get_stream_infos("streams", zulipToJabber.client.get_streams)
|
||||
else:
|
||||
stream_infos = get_stream_infos("subscriptions", zulipToJabber.client.get_subscriptions)
|
||||
|
||||
rooms = [] # type: List[str]
|
||||
for stream_info in stream_infos:
|
||||
stream = stream_info['name']
|
||||
stream = stream_info["name"]
|
||||
if stream.endswith("/xmpp"):
|
||||
rooms.append(stream_to_room(stream))
|
||||
return rooms
|
||||
|
@ -295,20 +295,20 @@ def config_error(msg: str) -> None:
|
|||
sys.exit(2)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
parser = optparse.OptionParser(
|
||||
epilog='''Most general and Jabber configuration options may also be specified in the
|
||||
epilog="""Most general and Jabber configuration options may also be specified in the
|
||||
zulip configuration file under the jabber_mirror section (exceptions are noted
|
||||
in their help sections). Keys have the same name as options with hyphens
|
||||
replaced with underscores. Zulip configuration options go in the api section,
|
||||
as normal.'''.replace(
|
||||
as normal.""".replace(
|
||||
"\n", " "
|
||||
)
|
||||
)
|
||||
parser.add_option(
|
||||
'--mode',
|
||||
"--mode",
|
||||
default=None,
|
||||
action='store',
|
||||
action="store",
|
||||
help='''Which mode to run in. Valid options are "personal" and "public". In
|
||||
"personal" mode, the mirror uses an individual users' credentials and mirrors
|
||||
all messages they send on Zulip to Jabber and all private Jabber messages to
|
||||
|
@ -319,33 +319,33 @@ user and mirrors messages sent to Jabber rooms to Zulip. Defaults to
|
|||
),
|
||||
)
|
||||
parser.add_option(
|
||||
'--zulip-email-suffix',
|
||||
"--zulip-email-suffix",
|
||||
default=None,
|
||||
action='store',
|
||||
help='''Add the specified suffix to the local part of email addresses constructed
|
||||
action="store",
|
||||
help="""Add the specified suffix to the local part of email addresses constructed
|
||||
from JIDs and nicks before sending requests to the Zulip server, and remove the
|
||||
suffix before sending requests to the Jabber server. For example, specifying
|
||||
"+foo" will cause messages that are sent to the "bar" room by nickname "qux" to
|
||||
be mirrored to the "bar/xmpp" stream in Zulip by user "qux+foo@example.com". This
|
||||
option does not affect login credentials.'''.replace(
|
||||
option does not affect login credentials.""".replace(
|
||||
"\n", " "
|
||||
),
|
||||
)
|
||||
parser.add_option(
|
||||
'-d',
|
||||
'--debug',
|
||||
help='set logging to DEBUG. Can not be set via config file.',
|
||||
action='store_const',
|
||||
dest='log_level',
|
||||
"-d",
|
||||
"--debug",
|
||||
help="set logging to DEBUG. Can not be set via config file.",
|
||||
action="store_const",
|
||||
dest="log_level",
|
||||
const=logging.DEBUG,
|
||||
default=logging.INFO,
|
||||
)
|
||||
|
||||
jabber_group = optparse.OptionGroup(parser, "Jabber configuration")
|
||||
jabber_group.add_option(
|
||||
'--jid',
|
||||
"--jid",
|
||||
default=None,
|
||||
action='store',
|
||||
action="store",
|
||||
help="Your Jabber JID. If a resource is specified, "
|
||||
"it will be used as the nickname when joining MUCs. "
|
||||
"Specifying the nickname is mostly useful if you want "
|
||||
|
@ -353,27 +353,27 @@ option does not affect login credentials.'''.replace(
|
|||
"from a dedicated account.",
|
||||
)
|
||||
jabber_group.add_option(
|
||||
'--jabber-password', default=None, action='store', help="Your Jabber password"
|
||||
"--jabber-password", default=None, action="store", help="Your Jabber password"
|
||||
)
|
||||
jabber_group.add_option(
|
||||
'--conference-domain',
|
||||
"--conference-domain",
|
||||
default=None,
|
||||
action='store',
|
||||
action="store",
|
||||
help="Your Jabber conference domain (E.g. conference.jabber.example.com). "
|
||||
"If not specifed, \"conference.\" will be prepended to your JID's domain.",
|
||||
'If not specifed, "conference." will be prepended to your JID\'s domain.',
|
||||
)
|
||||
jabber_group.add_option('--no-use-tls', default=None, action='store_true')
|
||||
jabber_group.add_option("--no-use-tls", default=None, action="store_true")
|
||||
jabber_group.add_option(
|
||||
'--jabber-server-address',
|
||||
"--jabber-server-address",
|
||||
default=None,
|
||||
action='store',
|
||||
action="store",
|
||||
help="The hostname of your Jabber server. This is only needed if "
|
||||
"your server is missing SRV records",
|
||||
)
|
||||
jabber_group.add_option(
|
||||
'--jabber-server-port',
|
||||
default='5222',
|
||||
action='store',
|
||||
"--jabber-server-port",
|
||||
default="5222",
|
||||
action="store",
|
||||
help="The port of your Jabber server. This is only needed if "
|
||||
"your server is missing SRV records",
|
||||
)
|
||||
|
@ -382,7 +382,7 @@ option does not affect login credentials.'''.replace(
|
|||
parser.add_option_group(zulip.generate_option_group(parser, "zulip-"))
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
logging.basicConfig(level=options.log_level, format='%(levelname)-8s %(message)s')
|
||||
logging.basicConfig(level=options.log_level, format="%(levelname)-8s %(message)s")
|
||||
|
||||
if options.zulip_config_file is None:
|
||||
default_config_file = zulip.get_default_config_filename()
|
||||
|
@ -422,9 +422,9 @@ option does not affect login credentials.'''.replace(
|
|||
options.mode = "personal"
|
||||
|
||||
if options.zulip_email_suffix is None:
|
||||
options.zulip_email_suffix = ''
|
||||
options.zulip_email_suffix = ""
|
||||
|
||||
if options.mode not in ('public', 'personal'):
|
||||
if options.mode not in ("public", "personal"):
|
||||
config_error("Bad value for --mode: must be one of 'public' or 'personal'")
|
||||
|
||||
if None in (options.jid, options.jabber_password):
|
||||
|
@ -437,7 +437,7 @@ option does not affect login credentials.'''.replace(
|
|||
zulip.init_from_options(options, "JabberMirror/" + __version__)
|
||||
)
|
||||
# This won't work for open realms that don't have a consistent domain
|
||||
options.zulip_domain = zulipToJabber.client.email.partition('@')[-1]
|
||||
options.zulip_domain = zulipToJabber.client.email.partition("@")[-1]
|
||||
|
||||
try:
|
||||
jid = JID(options.jid)
|
||||
|
@ -460,10 +460,10 @@ option does not affect login credentials.'''.replace(
|
|||
zulipToJabber.set_jabber_client(xmpp)
|
||||
|
||||
xmpp.process(block=False)
|
||||
if options.mode == 'public':
|
||||
event_types = ['stream']
|
||||
if options.mode == "public":
|
||||
event_types = ["stream"]
|
||||
else:
|
||||
event_types = ['message', 'subscription']
|
||||
event_types = ["message", "subscription"]
|
||||
|
||||
try:
|
||||
logging.info("Connecting to Zulip.")
|
||||
|
|
|
@ -96,7 +96,7 @@ def process_logs() -> None:
|
|||
# immediately after rotation, this tool won't notice.
|
||||
file_data["last"] = 1
|
||||
output = subprocess.check_output(["tail", "-n+%s" % (file_data["last"],), log_file])
|
||||
new_lines = output.decode('utf-8', errors='replace').split('\n')[:-1]
|
||||
new_lines = output.decode("utf-8", errors="replace").split("\n")[:-1]
|
||||
if len(new_lines) > 0:
|
||||
process_lines(new_lines, log_file)
|
||||
file_data["last"] += len(new_lines)
|
||||
|
|
|
@ -9,19 +9,19 @@ VERSION = "0.9"
|
|||
# In Nagios, "output" means "first line of output", and "long
|
||||
# output" means "other lines of output".
|
||||
parser = zulip.add_default_arguments(argparse.ArgumentParser()) # type: argparse.ArgumentParser
|
||||
parser.add_argument('--output', default='')
|
||||
parser.add_argument('--long-output', default='')
|
||||
parser.add_argument('--stream', default='nagios')
|
||||
parser.add_argument('--config', default='/etc/nagios3/zuliprc')
|
||||
for opt in ('type', 'host', 'service', 'state'):
|
||||
parser.add_argument('--' + opt)
|
||||
parser.add_argument("--output", default="")
|
||||
parser.add_argument("--long-output", default="")
|
||||
parser.add_argument("--stream", default="nagios")
|
||||
parser.add_argument("--config", default="/etc/nagios3/zuliprc")
|
||||
for opt in ("type", "host", "service", "state"):
|
||||
parser.add_argument("--" + opt)
|
||||
opts = parser.parse_args()
|
||||
|
||||
client = zulip.Client(
|
||||
config_file=opts.config, client="ZulipNagios/" + VERSION
|
||||
) # type: zulip.Client
|
||||
|
||||
msg = dict(type='stream', to=opts.stream) # type: Dict[str, Any]
|
||||
msg = dict(type="stream", to=opts.stream) # type: Dict[str, Any]
|
||||
|
||||
# Set a subject based on the host or service in question. This enables
|
||||
# threaded discussion of multiple concurrent issues, and provides useful
|
||||
|
@ -30,24 +30,24 @@ msg = dict(type='stream', to=opts.stream) # type: Dict[str, Any]
|
|||
# We send PROBLEM and RECOVERY messages to the same subject.
|
||||
if opts.service is None:
|
||||
# Host notification
|
||||
thing = 'host' # type: Text
|
||||
msg['subject'] = 'host %s' % (opts.host,)
|
||||
thing = "host" # type: Text
|
||||
msg["subject"] = "host %s" % (opts.host,)
|
||||
else:
|
||||
# Service notification
|
||||
thing = 'service'
|
||||
msg['subject'] = 'service %s on %s' % (opts.service, opts.host)
|
||||
thing = "service"
|
||||
msg["subject"] = "service %s on %s" % (opts.service, opts.host)
|
||||
|
||||
if len(msg['subject']) > 60:
|
||||
msg['subject'] = msg['subject'][0:57].rstrip() + "..."
|
||||
if len(msg["subject"]) > 60:
|
||||
msg["subject"] = msg["subject"][0:57].rstrip() + "..."
|
||||
# e.g. **PROBLEM**: service is CRITICAL
|
||||
msg['content'] = '**%s**: %s is %s' % (opts.type, thing, opts.state)
|
||||
msg["content"] = "**%s**: %s is %s" % (opts.type, thing, opts.state)
|
||||
|
||||
# The "long output" can contain newlines represented by "\n" escape sequences.
|
||||
# The Nagios mail command uses /usr/bin/printf "%b" to expand these.
|
||||
# We will be more conservative and handle just this one escape sequence.
|
||||
output = (opts.output + '\n' + opts.long_output.replace(r'\n', '\n')).strip() # type: Text
|
||||
output = (opts.output + "\n" + opts.long_output.replace(r"\n", "\n")).strip() # type: Text
|
||||
if output:
|
||||
# Put any command output in a code block.
|
||||
msg['content'] += '\n\n~~~~\n' + output + "\n~~~~\n"
|
||||
msg["content"] += "\n\n~~~~\n" + output + "\n~~~~\n"
|
||||
|
||||
client.send_message(msg)
|
||||
|
|
|
@ -10,7 +10,7 @@ from typing import Dict
|
|||
sys.path.insert(0, os.path.dirname(__file__))
|
||||
import zulip_openshift_config as config
|
||||
|
||||
VERSION = '0.1'
|
||||
VERSION = "0.1"
|
||||
|
||||
if config.ZULIP_API_PATH is not None:
|
||||
sys.path.append(config.ZULIP_API_PATH)
|
||||
|
@ -21,7 +21,7 @@ client = zulip.Client(
|
|||
email=config.ZULIP_USER,
|
||||
site=config.ZULIP_SITE,
|
||||
api_key=config.ZULIP_API_KEY,
|
||||
client='ZulipOpenShift/' + VERSION,
|
||||
client="ZulipOpenShift/" + VERSION,
|
||||
)
|
||||
|
||||
|
||||
|
@ -29,19 +29,19 @@ def get_deployment_details() -> Dict[str, str]:
|
|||
# "gear deployments" output example:
|
||||
# Activation time - Deployment ID - Git Ref - Git SHA1
|
||||
# 2017-01-07 15:40:30 -0500 - 9e2b7143 - master - b9ce57c - ACTIVE
|
||||
dep = subprocess.check_output(['gear', 'deployments'], universal_newlines=True).splitlines()[1]
|
||||
splits = dep.split(' - ')
|
||||
dep = subprocess.check_output(["gear", "deployments"], universal_newlines=True).splitlines()[1]
|
||||
splits = dep.split(" - ")
|
||||
|
||||
return dict(
|
||||
app_name=os.environ['OPENSHIFT_APP_NAME'],
|
||||
url=os.environ['OPENSHIFT_APP_DNS'],
|
||||
app_name=os.environ["OPENSHIFT_APP_NAME"],
|
||||
url=os.environ["OPENSHIFT_APP_DNS"],
|
||||
branch=splits[2],
|
||||
commit_id=splits[3],
|
||||
)
|
||||
|
||||
|
||||
def send_bot_message(deployment: Dict[str, str]) -> None:
|
||||
destination = config.deployment_notice_destination(deployment['branch'])
|
||||
destination = config.deployment_notice_destination(deployment["branch"])
|
||||
if destination is None:
|
||||
# No message should be sent
|
||||
return
|
||||
|
@ -49,10 +49,10 @@ def send_bot_message(deployment: Dict[str, str]) -> None:
|
|||
|
||||
client.send_message(
|
||||
{
|
||||
'type': 'stream',
|
||||
'to': destination['stream'],
|
||||
'subject': destination['subject'],
|
||||
'content': message,
|
||||
"type": "stream",
|
||||
"to": destination["stream"],
|
||||
"subject": destination["subject"],
|
||||
"content": message,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
from typing import Dict, Optional, Text
|
||||
|
||||
# Change these values to configure authentication for the plugin
|
||||
ZULIP_USER = 'openshift-bot@example.com'
|
||||
ZULIP_API_KEY = '0123456789abcdef0123456789abcdef'
|
||||
ZULIP_USER = "openshift-bot@example.com"
|
||||
ZULIP_API_KEY = "0123456789abcdef0123456789abcdef"
|
||||
|
||||
# deployment_notice_destination() lets you customize where deployment notices
|
||||
# are sent to with the full power of a Python function.
|
||||
|
@ -20,8 +20,8 @@ ZULIP_API_KEY = '0123456789abcdef0123456789abcdef'
|
|||
# * topic "master"
|
||||
# And similarly for branch "test-post-receive" (for use when testing).
|
||||
def deployment_notice_destination(branch: str) -> Optional[Dict[str, Text]]:
|
||||
if branch in ['master', 'test-post-receive']:
|
||||
return dict(stream='deployments', subject='%s' % (branch,))
|
||||
if branch in ["master", "test-post-receive"]:
|
||||
return dict(stream="deployments", subject="%s" % (branch,))
|
||||
|
||||
# Return None for cases where you don't want a notice sent
|
||||
return None
|
||||
|
@ -39,14 +39,14 @@ def deployment_notice_destination(branch: str) -> Optional[Dict[str, Text]]:
|
|||
# * dep_id = deployment id
|
||||
# * dep_time = deployment timestamp
|
||||
def format_deployment_message(
|
||||
app_name: str = '',
|
||||
url: str = '',
|
||||
branch: str = '',
|
||||
commit_id: str = '',
|
||||
dep_id: str = '',
|
||||
dep_time: str = '',
|
||||
app_name: str = "",
|
||||
url: str = "",
|
||||
branch: str = "",
|
||||
commit_id: str = "",
|
||||
dep_id: str = "",
|
||||
dep_time: str = "",
|
||||
) -> str:
|
||||
return 'Deployed commit `%s` (%s) in [%s](%s)' % (commit_id, branch, app_name, url)
|
||||
return "Deployed commit `%s` (%s) in [%s](%s)" % (commit_id, branch, app_name, url)
|
||||
|
||||
|
||||
## If properly installed, the Zulip API should be in your import
|
||||
|
@ -54,4 +54,4 @@ def format_deployment_message(
|
|||
ZULIP_API_PATH = None # type: Optional[str]
|
||||
|
||||
# Set this to your Zulip server's API URI
|
||||
ZULIP_SITE = 'https://zulip.example.com'
|
||||
ZULIP_SITE = "https://zulip.example.com"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
'''Zulip notification change-commit hook.
|
||||
"""Zulip notification change-commit hook.
|
||||
|
||||
In Perforce, The "change-commit" trigger is fired after a metadata has been
|
||||
created, files have been transferred, and the changelist committed to the depot
|
||||
|
@ -12,7 +12,7 @@ This specific trigger expects command-line arguments in the form:
|
|||
For example:
|
||||
1234 //depot/security/src/
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
import os
|
||||
import os.path
|
||||
|
@ -43,11 +43,11 @@ try:
|
|||
changelist = int(sys.argv[1]) # type: int
|
||||
changeroot = sys.argv[2] # type: str
|
||||
except IndexError:
|
||||
print("Wrong number of arguments.\n\n", end=' ', file=sys.stderr)
|
||||
print("Wrong number of arguments.\n\n", end=" ", file=sys.stderr)
|
||||
print(__doc__, file=sys.stderr)
|
||||
sys.exit(-1)
|
||||
except ValueError:
|
||||
print("First argument must be an integer.\n\n", end=' ', file=sys.stderr)
|
||||
print("First argument must be an integer.\n\n", end=" ", file=sys.stderr)
|
||||
print(__doc__, file=sys.stderr)
|
||||
sys.exit(-1)
|
||||
|
||||
|
@ -79,7 +79,7 @@ if hasattr(config, "P4_WEB"):
|
|||
|
||||
if p4web is not None:
|
||||
# linkify the change number
|
||||
change = '[{change}]({p4web}/{change}?ac=10)'.format(p4web=p4web, change=change)
|
||||
change = "[{change}]({p4web}/{change}?ac=10)".format(p4web=p4web, change=change)
|
||||
|
||||
message = """**{user}** committed revision @{change} to `{path}`.
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ P4_WEB: Optional[str] = None
|
|||
# * stream "depot_subdirectory-commits"
|
||||
# * subject "change_root"
|
||||
def commit_notice_destination(path: Text, changelist: int) -> Optional[Dict[Text, Text]]:
|
||||
dirs = path.split('/')
|
||||
dirs = path.split("/")
|
||||
if len(dirs) >= 4 and dirs[3] not in ("*", "..."):
|
||||
directory = dirs[3]
|
||||
else:
|
||||
|
|
|
@ -21,7 +21,7 @@ import feedparser
|
|||
import zulip
|
||||
|
||||
VERSION = "0.9" # type: str
|
||||
RSS_DATA_DIR = os.path.expanduser(os.path.join('~', '.cache', 'zulip-rss')) # type: str
|
||||
RSS_DATA_DIR = os.path.expanduser(os.path.join("~", ".cache", "zulip-rss")) # type: str
|
||||
OLDNESS_THRESHOLD = 30 # type: int
|
||||
|
||||
usage = """Usage: Send summaries of RSS entries for your favorite feeds to Zulip.
|
||||
|
@ -52,38 +52,38 @@ parser = zulip.add_default_arguments(
|
|||
argparse.ArgumentParser(usage)
|
||||
) # type: argparse.ArgumentParser
|
||||
parser.add_argument(
|
||||
'--stream',
|
||||
dest='stream',
|
||||
help='The stream to which to send RSS messages.',
|
||||
"--stream",
|
||||
dest="stream",
|
||||
help="The stream to which to send RSS messages.",
|
||||
default="rss",
|
||||
action='store',
|
||||
action="store",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--data-dir',
|
||||
dest='data_dir',
|
||||
help='The directory where feed metadata is stored',
|
||||
"--data-dir",
|
||||
dest="data_dir",
|
||||
help="The directory where feed metadata is stored",
|
||||
default=os.path.join(RSS_DATA_DIR),
|
||||
action='store',
|
||||
action="store",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--feed-file',
|
||||
dest='feed_file',
|
||||
help='The file containing a list of RSS feed URLs to follow, one URL per line',
|
||||
"--feed-file",
|
||||
dest="feed_file",
|
||||
help="The file containing a list of RSS feed URLs to follow, one URL per line",
|
||||
default=os.path.join(RSS_DATA_DIR, "rss-feeds"),
|
||||
action='store',
|
||||
action="store",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--unwrap',
|
||||
dest='unwrap',
|
||||
action='store_true',
|
||||
help='Convert word-wrapped paragraphs into single lines',
|
||||
"--unwrap",
|
||||
dest="unwrap",
|
||||
action="store_true",
|
||||
help="Convert word-wrapped paragraphs into single lines",
|
||||
default=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
'--math',
|
||||
dest='math',
|
||||
action='store_true',
|
||||
help='Convert $ to $$ (for KaTeX processing)',
|
||||
"--math",
|
||||
dest="math",
|
||||
action="store_true",
|
||||
help="Convert $ to $$ (for KaTeX processing)",
|
||||
default=False,
|
||||
)
|
||||
|
||||
|
@ -137,7 +137,7 @@ class MLStripper(HTMLParser):
|
|||
self.fed.append(data)
|
||||
|
||||
def get_data(self) -> str:
|
||||
return ''.join(self.fed)
|
||||
return "".join(self.fed)
|
||||
|
||||
|
||||
def strip_tags(html: str) -> str:
|
||||
|
@ -155,13 +155,13 @@ def compute_entry_hash(entry: Dict[str, Any]) -> str:
|
|||
def unwrap_text(body: str) -> str:
|
||||
# Replace \n by space if it is preceded and followed by a non-\n.
|
||||
# Example: '\na\nb\nc\n\nd\n' -> '\na b c\n\nd\n'
|
||||
return re.sub('(?<=[^\n])\n(?=[^\n])', ' ', body)
|
||||
return re.sub("(?<=[^\n])\n(?=[^\n])", " ", body)
|
||||
|
||||
|
||||
def elide_subject(subject: str) -> str:
|
||||
MAX_TOPIC_LENGTH = 60
|
||||
if len(subject) > MAX_TOPIC_LENGTH:
|
||||
subject = subject[: MAX_TOPIC_LENGTH - 3].rstrip() + '...'
|
||||
subject = subject[: MAX_TOPIC_LENGTH - 3].rstrip() + "..."
|
||||
return subject
|
||||
|
||||
|
||||
|
@ -178,7 +178,7 @@ def send_zulip(entry: Any, feed_name: str) -> Dict[str, Any]:
|
|||
) # type: str
|
||||
|
||||
if opts.math:
|
||||
content = content.replace('$', '$$')
|
||||
content = content.replace("$", "$$")
|
||||
|
||||
message = {
|
||||
"type": "stream",
|
||||
|
|
|
@ -43,7 +43,7 @@ entry = svn.log(path, revision_end=pysvn.Revision(pysvn.opt_revision_kind.number
|
|||
0
|
||||
] # type: Dict[Text, Any]
|
||||
message = "**{}** committed revision r{} to `{}`.\n\n> {}".format(
|
||||
entry['author'], rev, path.split('/')[-1], entry['revprops']['svn:log']
|
||||
entry["author"], rev, path.split("/")[-1], entry["revprops"]["svn:log"]
|
||||
) # type: Text
|
||||
|
||||
destination = config.commit_notice_destination(path, rev) # type: Optional[Dict[Text, Text]]
|
||||
|
|
|
@ -19,7 +19,7 @@ ZULIP_API_KEY = "0123456789abcdef0123456789abcdef"
|
|||
# * stream "commits"
|
||||
# * topic "branch_name"
|
||||
def commit_notice_destination(path: Text, commit: Text) -> Optional[Dict[Text, Text]]:
|
||||
repo = path.split('/')[-1]
|
||||
repo = path.split("/")[-1]
|
||||
if repo not in ["evil-master-plan", "my-super-secret-repository"]:
|
||||
return dict(stream="commits", subject="%s" % (repo,))
|
||||
|
||||
|
|
|
@ -100,24 +100,24 @@ class ZulipPlugin(Component):
|
|||
|
||||
content = "%s updated %s" % (author, markdown_ticket_url(ticket))
|
||||
if comment:
|
||||
content += ' with comment: %s\n\n' % (markdown_block(comment),)
|
||||
content += " with comment: %s\n\n" % (markdown_block(comment),)
|
||||
else:
|
||||
content += ":\n\n"
|
||||
field_changes = []
|
||||
for key, value in old_values.items():
|
||||
if key == "description":
|
||||
content += '- Changed %s from %s\n\nto %s' % (
|
||||
content += "- Changed %s from %s\n\nto %s" % (
|
||||
key,
|
||||
markdown_block(value),
|
||||
markdown_block(ticket.values.get(key)),
|
||||
)
|
||||
elif old_values.get(key) == "":
|
||||
field_changes.append('%s: => **%s**' % (key, ticket.values.get(key)))
|
||||
field_changes.append("%s: => **%s**" % (key, ticket.values.get(key)))
|
||||
elif ticket.values.get(key) == "":
|
||||
field_changes.append('%s: **%s** => ""' % (key, old_values.get(key)))
|
||||
else:
|
||||
field_changes.append(
|
||||
'%s: **%s** => **%s**' % (key, old_values.get(key), ticket.values.get(key))
|
||||
"%s: **%s** => **%s**" % (key, old_values.get(key), ticket.values.get(key))
|
||||
)
|
||||
content += ", ".join(field_changes)
|
||||
|
||||
|
|
|
@ -25,22 +25,22 @@ def get_model_id(options):
|
|||
|
||||
"""
|
||||
|
||||
trello_api_url = 'https://api.trello.com/1/board/{}'.format(options.trello_board_id)
|
||||
trello_api_url = "https://api.trello.com/1/board/{}".format(options.trello_board_id)
|
||||
|
||||
params = {
|
||||
'key': options.trello_api_key,
|
||||
'token': options.trello_token,
|
||||
"key": options.trello_api_key,
|
||||
"token": options.trello_token,
|
||||
}
|
||||
|
||||
trello_response = requests.get(trello_api_url, params=params)
|
||||
|
||||
if trello_response.status_code != 200:
|
||||
print('Error: Can\'t get the idModel. Please check the configuration')
|
||||
print("Error: Can't get the idModel. Please check the configuration")
|
||||
sys.exit(1)
|
||||
|
||||
board_info_json = trello_response.json()
|
||||
|
||||
return board_info_json['id']
|
||||
return board_info_json["id"]
|
||||
|
||||
|
||||
def get_webhook_id(options, id_model):
|
||||
|
@ -55,27 +55,27 @@ def get_webhook_id(options, id_model):
|
|||
|
||||
"""
|
||||
|
||||
trello_api_url = 'https://api.trello.com/1/webhooks/'
|
||||
trello_api_url = "https://api.trello.com/1/webhooks/"
|
||||
|
||||
data = {
|
||||
'key': options.trello_api_key,
|
||||
'token': options.trello_token,
|
||||
'description': 'Webhook for Zulip integration (From Trello {} to Zulip)'.format(
|
||||
"key": options.trello_api_key,
|
||||
"token": options.trello_token,
|
||||
"description": "Webhook for Zulip integration (From Trello {} to Zulip)".format(
|
||||
options.trello_board_name,
|
||||
),
|
||||
'callbackURL': options.zulip_webhook_url,
|
||||
'idModel': id_model,
|
||||
"callbackURL": options.zulip_webhook_url,
|
||||
"idModel": id_model,
|
||||
}
|
||||
|
||||
trello_response = requests.post(trello_api_url, data=data)
|
||||
|
||||
if trello_response.status_code != 200:
|
||||
print('Error: Can\'t create the Webhook:', trello_response.text)
|
||||
print("Error: Can't create the Webhook:", trello_response.text)
|
||||
sys.exit(1)
|
||||
|
||||
webhook_info_json = trello_response.json()
|
||||
|
||||
return webhook_info_json['id']
|
||||
return webhook_info_json["id"]
|
||||
|
||||
|
||||
def create_webhook(options):
|
||||
|
@ -88,20 +88,20 @@ def create_webhook(options):
|
|||
"""
|
||||
|
||||
# first, we need to get the idModel
|
||||
print('Getting Trello idModel for the {} board...'.format(options.trello_board_name))
|
||||
print("Getting Trello idModel for the {} board...".format(options.trello_board_name))
|
||||
|
||||
id_model = get_model_id(options)
|
||||
|
||||
if id_model:
|
||||
print('Success! The idModel is', id_model)
|
||||
print("Success! The idModel is", id_model)
|
||||
|
||||
id_webhook = get_webhook_id(options, id_model)
|
||||
|
||||
if id_webhook:
|
||||
print('Success! The webhook ID is', id_webhook)
|
||||
print("Success! The webhook ID is", id_webhook)
|
||||
|
||||
print(
|
||||
'Success! The webhook for the {} Trello board was successfully created.'.format(
|
||||
"Success! The webhook for the {} Trello board was successfully created.".format(
|
||||
options.trello_board_name
|
||||
)
|
||||
)
|
||||
|
@ -118,36 +118,36 @@ at <https://zulip.com/integrations/doc/trello>.
|
|||
"""
|
||||
|
||||
parser = argparse.ArgumentParser(description=description)
|
||||
parser.add_argument('--trello-board-name', required=True, help='The Trello board name.')
|
||||
parser.add_argument("--trello-board-name", required=True, help="The Trello board name.")
|
||||
parser.add_argument(
|
||||
'--trello-board-id',
|
||||
"--trello-board-id",
|
||||
required=True,
|
||||
help=('The Trello board short ID. Can usually be found ' 'in the URL of the Trello board.'),
|
||||
help=("The Trello board short ID. Can usually be found " "in the URL of the Trello board."),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--trello-api-key',
|
||||
"--trello-api-key",
|
||||
required=True,
|
||||
help=(
|
||||
'Visit https://trello.com/1/appkey/generate to generate '
|
||||
'an APPLICATION_KEY (need to be logged into Trello).'
|
||||
"Visit https://trello.com/1/appkey/generate to generate "
|
||||
"an APPLICATION_KEY (need to be logged into Trello)."
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--trello-token',
|
||||
"--trello-token",
|
||||
required=True,
|
||||
help=(
|
||||
'Visit https://trello.com/1/appkey/generate and under '
|
||||
'`Developer API Keys`, click on `Token` and generate '
|
||||
'a Trello access token.'
|
||||
"Visit https://trello.com/1/appkey/generate and under "
|
||||
"`Developer API Keys`, click on `Token` and generate "
|
||||
"a Trello access token."
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--zulip-webhook-url', required=True, help='The webhook URL that Trello will query.'
|
||||
"--zulip-webhook-url", required=True, help="The webhook URL that Trello will query."
|
||||
)
|
||||
|
||||
options = parser.parse_args()
|
||||
create_webhook(options)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
@ -69,32 +69,32 @@ access token" as well. Fill in the values displayed.
|
|||
|
||||
|
||||
def write_config(config: ConfigParser, configfile_path: str) -> None:
|
||||
with open(configfile_path, 'w') as configfile:
|
||||
with open(configfile_path, "w") as configfile:
|
||||
config.write(configfile)
|
||||
|
||||
|
||||
parser = zulip.add_default_arguments(argparse.ArgumentParser("Fetch tweets from Twitter."))
|
||||
parser.add_argument(
|
||||
'--instructions',
|
||||
action='store_true',
|
||||
help='Show instructions for the twitter bot setup and exit',
|
||||
"--instructions",
|
||||
action="store_true",
|
||||
help="Show instructions for the twitter bot setup and exit",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--limit-tweets', default=15, type=int, help='Maximum number of tweets to send at once'
|
||||
"--limit-tweets", default=15, type=int, help="Maximum number of tweets to send at once"
|
||||
)
|
||||
parser.add_argument('--search', dest='search_terms', help='Terms to search on', action='store')
|
||||
parser.add_argument("--search", dest="search_terms", help="Terms to search on", action="store")
|
||||
parser.add_argument(
|
||||
'--stream',
|
||||
dest='stream',
|
||||
help='The stream to which to send tweets',
|
||||
"--stream",
|
||||
dest="stream",
|
||||
help="The stream to which to send tweets",
|
||||
default="twitter",
|
||||
action='store',
|
||||
action="store",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--twitter-name', dest='twitter_name', help='Twitter username to poll new tweets from"'
|
||||
"--twitter-name", dest="twitter_name", help='Twitter username to poll new tweets from"'
|
||||
)
|
||||
parser.add_argument('--excluded-terms', dest='excluded_terms', help='Terms to exclude tweets on')
|
||||
parser.add_argument('--excluded-users', dest='excluded_users', help='Users to exclude tweets on')
|
||||
parser.add_argument("--excluded-terms", dest="excluded_terms", help="Terms to exclude tweets on")
|
||||
parser.add_argument("--excluded-users", dest="excluded_users", help="Users to exclude tweets on")
|
||||
|
||||
opts = parser.parse_args()
|
||||
|
||||
|
@ -103,15 +103,15 @@ if opts.instructions:
|
|||
sys.exit()
|
||||
|
||||
if all([opts.search_terms, opts.twitter_name]):
|
||||
parser.error('You must only specify either a search term or a username.')
|
||||
parser.error("You must only specify either a search term or a username.")
|
||||
if opts.search_terms:
|
||||
client_type = 'ZulipTwitterSearch/'
|
||||
client_type = "ZulipTwitterSearch/"
|
||||
CONFIGFILE_INTERNAL = os.path.expanduser("~/.zulip_twitterrc_fetchsearch")
|
||||
elif opts.twitter_name:
|
||||
client_type = 'ZulipTwitter/'
|
||||
client_type = "ZulipTwitter/"
|
||||
CONFIGFILE_INTERNAL = os.path.expanduser("~/.zulip_twitteruserrc_fetchuser")
|
||||
else:
|
||||
parser.error('You must either specify a search term or a username.')
|
||||
parser.error("You must either specify a search term or a username.")
|
||||
|
||||
try:
|
||||
config = ConfigParser()
|
||||
|
@ -119,10 +119,10 @@ try:
|
|||
config_internal = ConfigParser()
|
||||
config_internal.read(CONFIGFILE_INTERNAL)
|
||||
|
||||
consumer_key = config.get('twitter', 'consumer_key')
|
||||
consumer_secret = config.get('twitter', 'consumer_secret')
|
||||
access_token_key = config.get('twitter', 'access_token_key')
|
||||
access_token_secret = config.get('twitter', 'access_token_secret')
|
||||
consumer_key = config.get("twitter", "consumer_key")
|
||||
consumer_secret = config.get("twitter", "consumer_secret")
|
||||
access_token_key = config.get("twitter", "access_token_key")
|
||||
access_token_secret = config.get("twitter", "access_token_secret")
|
||||
except (NoSectionError, NoOptionError):
|
||||
parser.error("Please provide a ~/.zulip_twitterrc")
|
||||
|
||||
|
@ -130,17 +130,17 @@ if not all([consumer_key, consumer_secret, access_token_key, access_token_secret
|
|||
parser.error("Please provide a ~/.zulip_twitterrc")
|
||||
|
||||
try:
|
||||
since_id = config_internal.getint('twitter', 'since_id')
|
||||
since_id = config_internal.getint("twitter", "since_id")
|
||||
except (NoOptionError, NoSectionError):
|
||||
since_id = 0
|
||||
try:
|
||||
previous_twitter_name = config_internal.get('twitter', 'twitter_name')
|
||||
previous_twitter_name = config_internal.get("twitter", "twitter_name")
|
||||
except (NoOptionError, NoSectionError):
|
||||
previous_twitter_name = ''
|
||||
previous_twitter_name = ""
|
||||
try:
|
||||
previous_search_terms = config_internal.get('twitter', 'search_terms')
|
||||
previous_search_terms = config_internal.get("twitter", "search_terms")
|
||||
except (NoOptionError, NoSectionError):
|
||||
previous_search_terms = ''
|
||||
previous_search_terms = ""
|
||||
|
||||
try:
|
||||
import twitter
|
||||
|
@ -242,17 +242,17 @@ for status in statuses[::-1][: opts.limit_tweets]:
|
|||
|
||||
ret = client.send_message(message)
|
||||
|
||||
if ret['result'] == 'error':
|
||||
if ret["result"] == "error":
|
||||
# If sending failed (e.g. no such stream), abort and retry next time
|
||||
print("Error sending message to zulip: %s" % ret['msg'])
|
||||
print("Error sending message to zulip: %s" % ret["msg"])
|
||||
break
|
||||
else:
|
||||
since_id = status.id
|
||||
|
||||
if 'twitter' not in config_internal.sections():
|
||||
config_internal.add_section('twitter')
|
||||
config_internal.set('twitter', 'since_id', str(since_id))
|
||||
config_internal.set('twitter', 'search_terms', str(opts.search_terms))
|
||||
config_internal.set('twitter', 'twitter_name', str(opts.twitter_name))
|
||||
if "twitter" not in config_internal.sections():
|
||||
config_internal.add_section("twitter")
|
||||
config_internal.set("twitter", "since_id", str(since_id))
|
||||
config_internal.set("twitter", "search_terms", str(opts.search_terms))
|
||||
config_internal.set("twitter", "twitter_name", str(opts.twitter_name))
|
||||
|
||||
write_config(config_internal, CONFIGFILE_INTERNAL)
|
||||
|
|
|
@ -13,12 +13,12 @@ import zephyr
|
|||
import zulip
|
||||
|
||||
parser = optparse.OptionParser()
|
||||
parser.add_option('--verbose', dest='verbose', default=False, action='store_true')
|
||||
parser.add_option('--site', dest='site', default=None, action='store')
|
||||
parser.add_option('--sharded', default=False, action='store_true')
|
||||
parser.add_option("--verbose", dest="verbose", default=False, action="store_true")
|
||||
parser.add_option("--site", dest="site", default=None, action="store")
|
||||
parser.add_option("--sharded", default=False, action="store_true")
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
mit_user = 'tabbott/extra@ATHENA.MIT.EDU'
|
||||
mit_user = "tabbott/extra@ATHENA.MIT.EDU"
|
||||
|
||||
zulip_client = zulip.Client(verbose=True, client="ZulipMonitoring/0.1", site=options.site)
|
||||
|
||||
|
@ -116,11 +116,11 @@ def send_zephyr(zwrite_args: List[str], content: str) -> bool:
|
|||
# Subscribe to Zulip
|
||||
try:
|
||||
res = zulip_client.register(event_types=["message"])
|
||||
if 'error' in res['result']:
|
||||
if "error" in res["result"]:
|
||||
logging.error("Error subscribing to Zulips!")
|
||||
logging.error(res['msg'])
|
||||
logging.error(res["msg"])
|
||||
print_status_and_exit(1)
|
||||
queue_id, last_event_id = (res['queue_id'], res['last_event_id'])
|
||||
queue_id, last_event_id = (res["queue_id"], res["last_event_id"])
|
||||
except Exception:
|
||||
logger.exception("Unexpected error subscribing to Zulips")
|
||||
print_status_and_exit(1)
|
||||
|
@ -129,9 +129,9 @@ except Exception:
|
|||
zephyr_subs_to_add = []
|
||||
for (stream, test) in test_streams:
|
||||
if stream == "message":
|
||||
zephyr_subs_to_add.append((stream, 'personal', mit_user))
|
||||
zephyr_subs_to_add.append((stream, "personal", mit_user))
|
||||
else:
|
||||
zephyr_subs_to_add.append((stream, '*', '*'))
|
||||
zephyr_subs_to_add.append((stream, "*", "*"))
|
||||
|
||||
actually_subscribed = False
|
||||
for tries in range(10):
|
||||
|
@ -263,11 +263,11 @@ logger.info("Starting receiving messages!")
|
|||
|
||||
# receive zulips
|
||||
res = zulip_client.get_events(queue_id=queue_id, last_event_id=last_event_id)
|
||||
if 'error' in res['result']:
|
||||
if "error" in res["result"]:
|
||||
logging.error("Error receiving Zulips!")
|
||||
logging.error(res['msg'])
|
||||
logging.error(res["msg"])
|
||||
print_status_and_exit(1)
|
||||
messages = [event['message'] for event in res['events']]
|
||||
messages = [event["message"] for event in res["events"]]
|
||||
logger.info("Finished receiving Zulip messages!")
|
||||
|
||||
receive_zephyrs()
|
||||
|
@ -296,7 +296,7 @@ def process_keys(content_list: List[str]) -> Tuple[Dict[str, int], Set[str], Set
|
|||
# The h_foo variables are about the messages we _received_ in Zulip
|
||||
# The z_foo variables are about the messages we _received_ in Zephyr
|
||||
h_contents = [message["content"] for message in messages]
|
||||
z_contents = [notice.message.split('\0')[1] for notice in notices]
|
||||
z_contents = [notice.message.split("\0")[1] for notice in notices]
|
||||
(h_key_counts, h_missing_z, h_missing_h, h_duplicates, h_success) = process_keys(h_contents)
|
||||
(z_key_counts, z_missing_z, z_missing_h, z_duplicates, z_success) = process_keys(z_contents)
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import os
|
|||
import sys
|
||||
import unicodedata
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'api'))
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "api"))
|
||||
import zulip
|
||||
|
||||
|
||||
|
@ -18,20 +18,20 @@ def write_public_streams() -> None:
|
|||
# normalization and then lower-casing server-side
|
||||
canonical_cls = unicodedata.normalize("NFKC", stream_name).lower()
|
||||
if canonical_cls in [
|
||||
'security',
|
||||
'login',
|
||||
'network',
|
||||
'ops',
|
||||
'user_locate',
|
||||
'mit',
|
||||
'moof',
|
||||
'wsmonitor',
|
||||
'wg_ctl',
|
||||
'winlogger',
|
||||
'hm_ctl',
|
||||
'hm_stat',
|
||||
'zephyr_admin',
|
||||
'zephyr_ctl',
|
||||
"security",
|
||||
"login",
|
||||
"network",
|
||||
"ops",
|
||||
"user_locate",
|
||||
"mit",
|
||||
"moof",
|
||||
"wsmonitor",
|
||||
"wg_ctl",
|
||||
"winlogger",
|
||||
"hm_ctl",
|
||||
"hm_stat",
|
||||
"zephyr_admin",
|
||||
"zephyr_ctl",
|
||||
]:
|
||||
# These zephyr classes cannot be subscribed to by us, due
|
||||
# to MIT's Zephyr access control settings
|
||||
|
|
|
@ -39,8 +39,8 @@ def to_zulip_username(zephyr_username: str) -> str:
|
|||
(user, realm) = (zephyr_username, "ATHENA.MIT.EDU")
|
||||
if realm.upper() == "ATHENA.MIT.EDU":
|
||||
# Hack to make ctl's fake username setup work :)
|
||||
if user.lower() == 'golem':
|
||||
user = 'ctl'
|
||||
if user.lower() == "golem":
|
||||
user = "ctl"
|
||||
return user.lower() + "@mit.edu"
|
||||
return user.lower() + "|" + realm.upper() + "@mit.edu"
|
||||
|
||||
|
@ -49,10 +49,10 @@ def to_zephyr_username(zulip_username: str) -> str:
|
|||
(user, realm) = zulip_username.split("@")
|
||||
if "|" not in user:
|
||||
# Hack to make ctl's fake username setup work :)
|
||||
if user.lower() == 'ctl':
|
||||
user = 'golem'
|
||||
if user.lower() == "ctl":
|
||||
user = "golem"
|
||||
return user.lower() + "@ATHENA.MIT.EDU"
|
||||
match_user = re.match(r'([a-zA-Z0-9_]+)\|(.+)', user)
|
||||
match_user = re.match(r"([a-zA-Z0-9_]+)\|(.+)", user)
|
||||
if not match_user:
|
||||
raise Exception("Could not parse Zephyr realm for cross-realm user %s" % (zulip_username,))
|
||||
return match_user.group(1).lower() + "@" + match_user.group(2).upper()
|
||||
|
@ -85,14 +85,14 @@ def unwrap_lines(body: str) -> str:
|
|||
previous_line = lines[0]
|
||||
for line in lines[1:]:
|
||||
line = line.rstrip()
|
||||
if re.match(r'^\W', line, flags=re.UNICODE) and re.match(
|
||||
r'^\W', previous_line, flags=re.UNICODE
|
||||
if re.match(r"^\W", line, flags=re.UNICODE) and re.match(
|
||||
r"^\W", previous_line, flags=re.UNICODE
|
||||
):
|
||||
result += previous_line + "\n"
|
||||
elif (
|
||||
line == ""
|
||||
or previous_line == ""
|
||||
or re.match(r'^\W', line, flags=re.UNICODE)
|
||||
or re.match(r"^\W", line, flags=re.UNICODE)
|
||||
or different_paragraph(previous_line, line)
|
||||
):
|
||||
# Use 2 newlines to separate sections so that we
|
||||
|
@ -122,31 +122,31 @@ def send_zulip(zeph: ZephyrDict) -> Dict[str, Any]:
|
|||
message = {}
|
||||
if options.forward_class_messages:
|
||||
message["forged"] = "yes"
|
||||
message['type'] = zeph['type']
|
||||
message['time'] = zeph['time']
|
||||
message['sender'] = to_zulip_username(zeph['sender'])
|
||||
message["type"] = zeph["type"]
|
||||
message["time"] = zeph["time"]
|
||||
message["sender"] = to_zulip_username(zeph["sender"])
|
||||
if "subject" in zeph:
|
||||
# Truncate the subject to the current limit in Zulip. No
|
||||
# need to do this for stream names, since we're only
|
||||
# subscribed to valid stream names.
|
||||
message["subject"] = zeph["subject"][:60]
|
||||
if zeph['type'] == 'stream':
|
||||
if zeph["type"] == "stream":
|
||||
# Forward messages sent to -c foo -i bar to stream bar subject "instance"
|
||||
if zeph["stream"] == "message":
|
||||
message['to'] = zeph['subject'].lower()
|
||||
message['subject'] = "instance %s" % (zeph['subject'],)
|
||||
message["to"] = zeph["subject"].lower()
|
||||
message["subject"] = "instance %s" % (zeph["subject"],)
|
||||
elif zeph["stream"] == "tabbott-test5":
|
||||
message['to'] = zeph['subject'].lower()
|
||||
message['subject'] = "test instance %s" % (zeph['subject'],)
|
||||
message["to"] = zeph["subject"].lower()
|
||||
message["subject"] = "test instance %s" % (zeph["subject"],)
|
||||
else:
|
||||
message["to"] = zeph["stream"]
|
||||
else:
|
||||
message["to"] = zeph["recipient"]
|
||||
message['content'] = unwrap_lines(zeph['content'])
|
||||
message["content"] = unwrap_lines(zeph["content"])
|
||||
|
||||
if options.test_mode and options.site == DEFAULT_SITE:
|
||||
logger.debug("Message is: %s" % (str(message),))
|
||||
return {'result': "success"}
|
||||
return {"result": "success"}
|
||||
|
||||
return zulip_client.send_message(message)
|
||||
|
||||
|
@ -311,13 +311,13 @@ def parse_zephyr_body(zephyr_data: str, notice_format: str) -> Tuple[str, str]:
|
|||
try:
|
||||
(zsig, body) = zephyr_data.split("\x00", 1)
|
||||
if (
|
||||
notice_format == 'New transaction [$1] entered in $2\nFrom: $3 ($5)\nSubject: $4'
|
||||
or notice_format == 'New transaction [$1] entered in $2\nFrom: $3\nSubject: $4'
|
||||
notice_format == "New transaction [$1] entered in $2\nFrom: $3 ($5)\nSubject: $4"
|
||||
or notice_format == "New transaction [$1] entered in $2\nFrom: $3\nSubject: $4"
|
||||
):
|
||||
# Logic based off of owl_zephyr_get_message in barnowl
|
||||
fields = body.split('\x00')
|
||||
fields = body.split("\x00")
|
||||
if len(fields) == 5:
|
||||
body = 'New transaction [%s] entered in %s\nFrom: %s (%s)\nSubject: %s' % (
|
||||
body = "New transaction [%s] entered in %s\nFrom: %s (%s)\nSubject: %s" % (
|
||||
fields[0],
|
||||
fields[1],
|
||||
fields[2],
|
||||
|
@ -327,7 +327,7 @@ def parse_zephyr_body(zephyr_data: str, notice_format: str) -> Tuple[str, str]:
|
|||
except ValueError:
|
||||
(zsig, body) = ("", zephyr_data)
|
||||
# Clean body of any null characters, since they're invalid in our protocol.
|
||||
body = body.replace('\x00', '')
|
||||
body = body.replace("\x00", "")
|
||||
return (zsig, body)
|
||||
|
||||
|
||||
|
@ -350,8 +350,8 @@ def parse_crypt_table(zephyr_class: str, instance: str) -> Optional[str]:
|
|||
continue
|
||||
groups = match.groupdict()
|
||||
if (
|
||||
groups['class'].lower() == zephyr_class
|
||||
and 'keypath' in groups
|
||||
groups["class"].lower() == zephyr_class
|
||||
and "keypath" in groups
|
||||
and groups.get("algorithm") == "AES"
|
||||
):
|
||||
return groups["keypath"]
|
||||
|
@ -453,23 +453,23 @@ def process_notice(notice: "zephyr.ZNotice", log: Optional[IO[str]]) -> None:
|
|||
|
||||
zeph: ZephyrDict
|
||||
zeph = {
|
||||
'time': str(notice.time),
|
||||
'sender': notice.sender,
|
||||
'zsig': zsig, # logged here but not used by app
|
||||
'content': body,
|
||||
"time": str(notice.time),
|
||||
"sender": notice.sender,
|
||||
"zsig": zsig, # logged here but not used by app
|
||||
"content": body,
|
||||
}
|
||||
if is_huddle:
|
||||
zeph['type'] = 'private'
|
||||
zeph['recipient'] = huddle_recipients
|
||||
zeph["type"] = "private"
|
||||
zeph["recipient"] = huddle_recipients
|
||||
elif is_personal:
|
||||
assert notice.recipient is not None
|
||||
zeph['type'] = 'private'
|
||||
zeph['recipient'] = to_zulip_username(notice.recipient)
|
||||
zeph["type"] = "private"
|
||||
zeph["recipient"] = to_zulip_username(notice.recipient)
|
||||
else:
|
||||
zeph['type'] = 'stream'
|
||||
zeph['stream'] = zephyr_class
|
||||
zeph["type"] = "stream"
|
||||
zeph["stream"] = zephyr_class
|
||||
if notice.instance.strip() != "":
|
||||
zeph['subject'] = notice.instance
|
||||
zeph["subject"] = notice.instance
|
||||
else:
|
||||
zeph["subject"] = '(instance "%s")' % (notice.instance,)
|
||||
|
||||
|
@ -489,7 +489,7 @@ def process_notice(notice: "zephyr.ZNotice", log: Optional[IO[str]]) -> None:
|
|||
"Received a message on %s/%s from %s..." % (zephyr_class, notice.instance, notice.sender)
|
||||
)
|
||||
if log is not None:
|
||||
log.write(json.dumps(zeph) + '\n')
|
||||
log.write(json.dumps(zeph) + "\n")
|
||||
log.flush()
|
||||
|
||||
if os.fork() == 0:
|
||||
|
@ -593,7 +593,7 @@ def zephyr_to_zulip(options: optparse.Values) -> None:
|
|||
zeph["subject"] = zeph["instance"]
|
||||
logger.info(
|
||||
"sending saved message to %s from %s..."
|
||||
% (zeph.get('stream', zeph.get('recipient')), zeph['sender'])
|
||||
% (zeph.get("stream", zeph.get("recipient")), zeph["sender"])
|
||||
)
|
||||
send_zulip(zeph)
|
||||
except Exception:
|
||||
|
@ -603,7 +603,7 @@ def zephyr_to_zulip(options: optparse.Values) -> None:
|
|||
logger.info("Successfully initialized; Starting receive loop.")
|
||||
|
||||
if options.resend_log_path is not None:
|
||||
with open(options.resend_log_path, 'a') as log:
|
||||
with open(options.resend_log_path, "a") as log:
|
||||
process_loop(log)
|
||||
else:
|
||||
process_loop(None)
|
||||
|
@ -700,10 +700,10 @@ Feedback button or at support@zulip.com."""
|
|||
]
|
||||
|
||||
# Hack to make ctl's fake username setup work :)
|
||||
if message['type'] == "stream" and zulip_account_email == "ctl@mit.edu":
|
||||
if message["type"] == "stream" and zulip_account_email == "ctl@mit.edu":
|
||||
zwrite_args.extend(["-S", "ctl"])
|
||||
|
||||
if message['type'] == "stream":
|
||||
if message["type"] == "stream":
|
||||
zephyr_class = message["display_recipient"]
|
||||
instance = message["subject"]
|
||||
|
||||
|
@ -725,11 +725,11 @@ Feedback button or at support@zulip.com."""
|
|||
zephyr_class = "message"
|
||||
zwrite_args.extend(["-c", zephyr_class, "-i", instance])
|
||||
logger.info("Forwarding message to class %s, instance %s" % (zephyr_class, instance))
|
||||
elif message['type'] == "private":
|
||||
if len(message['display_recipient']) == 1:
|
||||
elif message["type"] == "private":
|
||||
if len(message["display_recipient"]) == 1:
|
||||
recipient = to_zephyr_username(message["display_recipient"][0]["email"])
|
||||
recipients = [recipient]
|
||||
elif len(message['display_recipient']) == 2:
|
||||
elif len(message["display_recipient"]) == 2:
|
||||
recipient = ""
|
||||
for r in message["display_recipient"]:
|
||||
if r["email"].lower() != zulip_account_email.lower():
|
||||
|
@ -1085,62 +1085,62 @@ def configure_logger(logger: logging.Logger, direction_name: Optional[str]) -> N
|
|||
def parse_args() -> Tuple[optparse.Values, List[str]]:
|
||||
parser = optparse.OptionParser()
|
||||
parser.add_option(
|
||||
'--forward-class-messages', default=False, help=optparse.SUPPRESS_HELP, action='store_true'
|
||||
"--forward-class-messages", default=False, help=optparse.SUPPRESS_HELP, action="store_true"
|
||||
)
|
||||
parser.add_option('--shard', help=optparse.SUPPRESS_HELP)
|
||||
parser.add_option('--noshard', default=False, help=optparse.SUPPRESS_HELP, action='store_true')
|
||||
parser.add_option('--resend-log', dest='logs_to_resend', help=optparse.SUPPRESS_HELP)
|
||||
parser.add_option('--enable-resend-log', dest='resend_log_path', help=optparse.SUPPRESS_HELP)
|
||||
parser.add_option('--log-path', dest='log_path', help=optparse.SUPPRESS_HELP)
|
||||
parser.add_option("--shard", help=optparse.SUPPRESS_HELP)
|
||||
parser.add_option("--noshard", default=False, help=optparse.SUPPRESS_HELP, action="store_true")
|
||||
parser.add_option("--resend-log", dest="logs_to_resend", help=optparse.SUPPRESS_HELP)
|
||||
parser.add_option("--enable-resend-log", dest="resend_log_path", help=optparse.SUPPRESS_HELP)
|
||||
parser.add_option("--log-path", dest="log_path", help=optparse.SUPPRESS_HELP)
|
||||
parser.add_option(
|
||||
'--stream-file-path',
|
||||
dest='stream_file_path',
|
||||
"--stream-file-path",
|
||||
dest="stream_file_path",
|
||||
default="/home/zulip/public_streams",
|
||||
help=optparse.SUPPRESS_HELP,
|
||||
)
|
||||
parser.add_option(
|
||||
'--no-forward-personals',
|
||||
dest='forward_personals',
|
||||
"--no-forward-personals",
|
||||
dest="forward_personals",
|
||||
help=optparse.SUPPRESS_HELP,
|
||||
default=True,
|
||||
action='store_false',
|
||||
action="store_false",
|
||||
)
|
||||
parser.add_option(
|
||||
'--forward-mail-zephyrs',
|
||||
dest='forward_mail_zephyrs',
|
||||
"--forward-mail-zephyrs",
|
||||
dest="forward_mail_zephyrs",
|
||||
help=optparse.SUPPRESS_HELP,
|
||||
default=False,
|
||||
action='store_true',
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_option(
|
||||
'--no-forward-from-zulip',
|
||||
"--no-forward-from-zulip",
|
||||
default=True,
|
||||
dest='forward_from_zulip',
|
||||
dest="forward_from_zulip",
|
||||
help=optparse.SUPPRESS_HELP,
|
||||
action='store_false',
|
||||
action="store_false",
|
||||
)
|
||||
parser.add_option('--verbose', default=False, help=optparse.SUPPRESS_HELP, action='store_true')
|
||||
parser.add_option('--sync-subscriptions', default=False, action='store_true')
|
||||
parser.add_option('--ignore-expired-tickets', default=False, action='store_true')
|
||||
parser.add_option('--site', default=DEFAULT_SITE, help=optparse.SUPPRESS_HELP)
|
||||
parser.add_option('--on-startup-command', default=None, help=optparse.SUPPRESS_HELP)
|
||||
parser.add_option('--user', default=os.environ["USER"], help=optparse.SUPPRESS_HELP)
|
||||
parser.add_option("--verbose", default=False, help=optparse.SUPPRESS_HELP, action="store_true")
|
||||
parser.add_option("--sync-subscriptions", default=False, action="store_true")
|
||||
parser.add_option("--ignore-expired-tickets", default=False, action="store_true")
|
||||
parser.add_option("--site", default=DEFAULT_SITE, help=optparse.SUPPRESS_HELP)
|
||||
parser.add_option("--on-startup-command", default=None, help=optparse.SUPPRESS_HELP)
|
||||
parser.add_option("--user", default=os.environ["USER"], help=optparse.SUPPRESS_HELP)
|
||||
parser.add_option(
|
||||
'--stamp-path',
|
||||
"--stamp-path",
|
||||
default="/afs/athena.mit.edu/user/t/a/tabbott/for_friends",
|
||||
help=optparse.SUPPRESS_HELP,
|
||||
)
|
||||
parser.add_option('--session-path', default=None, help=optparse.SUPPRESS_HELP)
|
||||
parser.add_option('--nagios-class', default=None, help=optparse.SUPPRESS_HELP)
|
||||
parser.add_option('--nagios-path', default=None, help=optparse.SUPPRESS_HELP)
|
||||
parser.add_option("--session-path", default=None, help=optparse.SUPPRESS_HELP)
|
||||
parser.add_option("--nagios-class", default=None, help=optparse.SUPPRESS_HELP)
|
||||
parser.add_option("--nagios-path", default=None, help=optparse.SUPPRESS_HELP)
|
||||
parser.add_option(
|
||||
'--use-sessions', default=False, action='store_true', help=optparse.SUPPRESS_HELP
|
||||
"--use-sessions", default=False, action="store_true", help=optparse.SUPPRESS_HELP
|
||||
)
|
||||
parser.add_option(
|
||||
'--test-mode', default=False, help=optparse.SUPPRESS_HELP, action='store_true'
|
||||
"--test-mode", default=False, help=optparse.SUPPRESS_HELP, action="store_true"
|
||||
)
|
||||
parser.add_option(
|
||||
'--api-key-file', default=os.path.join(os.environ["HOME"], "Private", ".humbug-api-key")
|
||||
"--api-key-file", default=os.path.join(os.environ["HOME"], "Private", ".humbug-api-key")
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
@ -1235,7 +1235,7 @@ or specify the --api-key-file option."""
|
|||
# Personals mirror on behalf of another user.
|
||||
pgrep_query = "%s.*--user=%s" % (pgrep_query, options.user)
|
||||
proc = subprocess.Popen(
|
||||
['pgrep', '-U', os.environ["USER"], "-f", pgrep_query],
|
||||
["pgrep", "-U", os.environ["USER"], "-f", pgrep_query],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
|
|
|
@ -15,7 +15,7 @@ def version() -> str:
|
|||
version_line = next(
|
||||
itertools.dropwhile(lambda x: not x.startswith("__version__"), in_handle)
|
||||
)
|
||||
version = version_line.split('=')[-1].strip().replace('"', '')
|
||||
version = version_line.split("=")[-1].strip().replace('"', "")
|
||||
return version
|
||||
|
||||
|
||||
|
@ -28,50 +28,50 @@ def recur_expand(target_root: Any, dir: Any) -> Generator[Tuple[str, List[str]],
|
|||
|
||||
# We should be installable with either setuptools or distutils.
|
||||
package_info = dict(
|
||||
name='zulip',
|
||||
name="zulip",
|
||||
version=version(),
|
||||
description='Bindings for the Zulip message API',
|
||||
description="Bindings for the Zulip message API",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
author='Zulip Open Source Project',
|
||||
author_email='zulip-devel@googlegroups.com',
|
||||
author="Zulip Open Source Project",
|
||||
author_email="zulip-devel@googlegroups.com",
|
||||
classifiers=[
|
||||
'Development Status :: 4 - Beta',
|
||||
'Environment :: Web Environment',
|
||||
'Intended Audience :: Developers',
|
||||
'License :: OSI Approved :: Apache Software License',
|
||||
'Topic :: Communications :: Chat',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Programming Language :: Python :: 3.8',
|
||||
'Programming Language :: Python :: 3.9',
|
||||
"Development Status :: 4 - Beta",
|
||||
"Environment :: Web Environment",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: Apache Software License",
|
||||
"Topic :: Communications :: Chat",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
],
|
||||
python_requires='>=3.6',
|
||||
url='https://www.zulip.org/',
|
||||
python_requires=">=3.6",
|
||||
url="https://www.zulip.org/",
|
||||
project_urls={
|
||||
"Source": "https://github.com/zulip/python-zulip-api/",
|
||||
"Documentation": "https://zulip.com/api",
|
||||
},
|
||||
data_files=list(recur_expand('share/zulip', 'integrations')),
|
||||
data_files=list(recur_expand("share/zulip", "integrations")),
|
||||
include_package_data=True,
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'zulip-send=zulip.send:main',
|
||||
'zulip-api-examples=zulip.api_examples:main',
|
||||
'zulip-matrix-bridge=integrations.bridge_with_matrix.matrix_bridge:main',
|
||||
'zulip-api=zulip.cli:cli',
|
||||
"console_scripts": [
|
||||
"zulip-send=zulip.send:main",
|
||||
"zulip-api-examples=zulip.api_examples:main",
|
||||
"zulip-matrix-bridge=integrations.bridge_with_matrix.matrix_bridge:main",
|
||||
"zulip-api=zulip.cli:cli",
|
||||
],
|
||||
},
|
||||
package_data={'zulip': ["py.typed"]},
|
||||
package_data={"zulip": ["py.typed"]},
|
||||
) # type: Dict[str, Any]
|
||||
|
||||
setuptools_info = dict(
|
||||
install_requires=[
|
||||
'requests[security]>=0.12.1',
|
||||
'matrix_client',
|
||||
'distro',
|
||||
'click',
|
||||
"requests[security]>=0.12.1",
|
||||
"matrix_client",
|
||||
"distro",
|
||||
"click",
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -79,7 +79,7 @@ try:
|
|||
from setuptools import find_packages, setup
|
||||
|
||||
package_info.update(setuptools_info)
|
||||
package_info['packages'] = find_packages(exclude=['tests'])
|
||||
package_info["packages"] = find_packages(exclude=["tests"])
|
||||
|
||||
except ImportError:
|
||||
from distutils.core import setup
|
||||
|
@ -89,12 +89,12 @@ except ImportError:
|
|||
try:
|
||||
import requests
|
||||
|
||||
assert LooseVersion(requests.__version__) >= LooseVersion('0.12.1')
|
||||
assert LooseVersion(requests.__version__) >= LooseVersion("0.12.1")
|
||||
except (ImportError, AssertionError):
|
||||
print("requests >=0.12.1 is not installed", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
package_info['packages'] = ['zulip']
|
||||
package_info["packages"] = ["zulip"]
|
||||
|
||||
|
||||
setup(**package_info)
|
||||
|
|
|
@ -15,8 +15,8 @@ class TestDefaultArguments(TestCase):
|
|||
def test_invalid_arguments(self) -> None:
|
||||
parser = zulip.add_default_arguments(argparse.ArgumentParser(usage="lorem ipsum"))
|
||||
with self.assertRaises(SystemExit) as cm:
|
||||
with patch('sys.stderr', new=io.StringIO()) as mock_stderr:
|
||||
parser.parse_args(['invalid argument'])
|
||||
with patch("sys.stderr", new=io.StringIO()) as mock_stderr:
|
||||
parser.parse_args(["invalid argument"])
|
||||
self.assertEqual(cm.exception.code, 2)
|
||||
# Assert that invalid arguments exit with printing the full usage (non-standard behavior)
|
||||
self.assertTrue(
|
||||
|
@ -32,20 +32,20 @@ Zulip API configuration:
|
|||
)
|
||||
)
|
||||
|
||||
@patch('os.path.exists', return_value=False)
|
||||
@patch("os.path.exists", return_value=False)
|
||||
def test_config_path_with_tilde(self, mock_os_path_exists: bool) -> None:
|
||||
parser = zulip.add_default_arguments(argparse.ArgumentParser(usage="lorem ipsum"))
|
||||
test_path = '~/zuliprc'
|
||||
args = parser.parse_args(['--config-file', test_path])
|
||||
test_path = "~/zuliprc"
|
||||
args = parser.parse_args(["--config-file", test_path])
|
||||
with self.assertRaises(ZulipError) as cm:
|
||||
zulip.init_from_options(args)
|
||||
expanded_test_path = os.path.abspath(os.path.expanduser(test_path))
|
||||
self.assertEqual(
|
||||
str(cm.exception),
|
||||
'api_key or email not specified and '
|
||||
'file {} does not exist'.format(expanded_test_path),
|
||||
"api_key or email not specified and "
|
||||
"file {} does not exist".format(expanded_test_path),
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
@ -9,17 +9,17 @@ import zulip
|
|||
class TestHashUtilDecode(TestCase):
|
||||
def test_hash_util_decode(self) -> None:
|
||||
tests = [
|
||||
('topic', 'topic'),
|
||||
('.2Edot', '.dot'),
|
||||
('.23stream.20name', '#stream name'),
|
||||
('(no.20topic)', '(no topic)'),
|
||||
('.3Cstrong.3Ebold.3C.2Fstrong.3E', '<strong>bold</strong>'),
|
||||
('.3Asome_emoji.3A', ':some_emoji:'),
|
||||
("topic", "topic"),
|
||||
(".2Edot", ".dot"),
|
||||
(".23stream.20name", "#stream name"),
|
||||
("(no.20topic)", "(no topic)"),
|
||||
(".3Cstrong.3Ebold.3C.2Fstrong.3E", "<strong>bold</strong>"),
|
||||
(".3Asome_emoji.3A", ":some_emoji:"),
|
||||
]
|
||||
for encoded_string, decoded_string in tests:
|
||||
with self.subTest(encoded_string=encoded_string):
|
||||
self.assertEqual(zulip.hash_util_decode(encoded_string), decoded_string)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -11,11 +11,11 @@ def main() -> None:
|
|||
Prints the path to the Zulip API example scripts."""
|
||||
parser = argparse.ArgumentParser(usage=usage)
|
||||
parser.add_argument(
|
||||
'script_name', nargs='?', default='', help='print path to the script <script_name>'
|
||||
"script_name", nargs="?", default="", help="print path to the script <script_name>"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
zulip_path = os.path.abspath(os.path.dirname(zulip.__file__))
|
||||
examples_path = os.path.abspath(os.path.join(zulip_path, 'examples', args.script_name))
|
||||
examples_path = os.path.abspath(os.path.join(zulip_path, "examples", args.script_name))
|
||||
if os.path.isdir(examples_path) or (args.script_name and os.path.isfile(examples_path)):
|
||||
print(examples_path)
|
||||
else:
|
||||
|
@ -26,5 +26,5 @@ Prints the path to the Zulip API example scripts."""
|
|||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
@ -14,17 +14,17 @@ Example: alert-words remove banana
|
|||
|
||||
|
||||
parser = zulip.add_default_arguments(argparse.ArgumentParser(usage=usage))
|
||||
parser.add_argument('operation', choices=['get', 'add', 'remove'], type=str)
|
||||
parser.add_argument('words', type=str, nargs='*')
|
||||
parser.add_argument("operation", choices=["get", "add", "remove"], type=str)
|
||||
parser.add_argument("words", type=str, nargs="*")
|
||||
options = parser.parse_args()
|
||||
|
||||
client = zulip.init_from_options(options)
|
||||
|
||||
if options.operation == 'get':
|
||||
if options.operation == "get":
|
||||
result = client.get_alert_words()
|
||||
elif options.operation == 'add':
|
||||
elif options.operation == "add":
|
||||
result = client.add_alert_words(options.words)
|
||||
elif options.operation == 'remove':
|
||||
elif options.operation == "remove":
|
||||
result = client.remove_alert_words(options.words)
|
||||
|
||||
print(result)
|
||||
|
|
|
@ -15,10 +15,10 @@ Specify your Zulip API credentials and server in a ~/.zuliprc file or using the
|
|||
import zulip
|
||||
|
||||
parser = zulip.add_default_arguments(argparse.ArgumentParser(usage=usage))
|
||||
parser.add_argument('--new-email', required=True)
|
||||
parser.add_argument('--new-password', required=True)
|
||||
parser.add_argument('--new-full-name', required=True)
|
||||
parser.add_argument('--new-short-name', required=True)
|
||||
parser.add_argument("--new-email", required=True)
|
||||
parser.add_argument("--new-password", required=True)
|
||||
parser.add_argument("--new-full-name", required=True)
|
||||
parser.add_argument("--new-short-name", required=True)
|
||||
options = parser.parse_args()
|
||||
|
||||
client = zulip.init_from_options(options)
|
||||
|
@ -26,10 +26,10 @@ client = zulip.init_from_options(options)
|
|||
print(
|
||||
client.create_user(
|
||||
{
|
||||
'email': options.new_email,
|
||||
'password': options.new_password,
|
||||
'full_name': options.new_full_name,
|
||||
'short_name': options.new_short_name,
|
||||
"email": options.new_email,
|
||||
"password": options.new_password,
|
||||
"full_name": options.new_full_name,
|
||||
"short_name": options.new_short_name,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
|
@ -13,7 +13,7 @@ Example: delete-message 42
|
|||
|
||||
|
||||
parser = zulip.add_default_arguments(argparse.ArgumentParser(usage=usage))
|
||||
parser.add_argument('message_id', type=int)
|
||||
parser.add_argument("message_id", type=int)
|
||||
options = parser.parse_args()
|
||||
|
||||
client = zulip.init_from_options(options)
|
||||
|
|
|
@ -11,7 +11,7 @@ Example: delete-stream 42
|
|||
|
||||
|
||||
parser = zulip.add_default_arguments(argparse.ArgumentParser(usage=usage))
|
||||
parser.add_argument('stream_id', type=int)
|
||||
parser.add_argument("stream_id", type=int)
|
||||
options = parser.parse_args()
|
||||
|
||||
client = zulip.init_from_options(options)
|
||||
|
|
|
@ -15,9 +15,9 @@ Specify your Zulip API credentials and server in a ~/.zuliprc file or using the
|
|||
import zulip
|
||||
|
||||
parser = zulip.add_default_arguments(argparse.ArgumentParser(usage=usage))
|
||||
parser.add_argument('--message-id', type=int, required=True)
|
||||
parser.add_argument('--subject', default="")
|
||||
parser.add_argument('--content', default="")
|
||||
parser.add_argument("--message-id", type=int, required=True)
|
||||
parser.add_argument("--subject", default="")
|
||||
parser.add_argument("--content", default="")
|
||||
options = parser.parse_args()
|
||||
|
||||
client = zulip.init_from_options(options)
|
||||
|
|
|
@ -19,12 +19,12 @@ def quote(string: str) -> str:
|
|||
|
||||
|
||||
parser = zulip.add_default_arguments(argparse.ArgumentParser(usage=usage))
|
||||
parser.add_argument('--stream-id', type=int, required=True)
|
||||
parser.add_argument('--description')
|
||||
parser.add_argument('--new-name')
|
||||
parser.add_argument('--private', action='store_true')
|
||||
parser.add_argument('--announcement-only', action='store_true')
|
||||
parser.add_argument('--history-public-to-subscribers', action='store_true')
|
||||
parser.add_argument("--stream-id", type=int, required=True)
|
||||
parser.add_argument("--description")
|
||||
parser.add_argument("--new-name")
|
||||
parser.add_argument("--private", action="store_true")
|
||||
parser.add_argument("--announcement-only", action="store_true")
|
||||
parser.add_argument("--history-public-to-subscribers", action="store_true")
|
||||
options = parser.parse_args()
|
||||
|
||||
client = zulip.init_from_options(options)
|
||||
|
@ -32,12 +32,12 @@ client = zulip.init_from_options(options)
|
|||
print(
|
||||
client.update_stream(
|
||||
{
|
||||
'stream_id': options.stream_id,
|
||||
'description': quote(options.description),
|
||||
'new_name': quote(options.new_name),
|
||||
'is_private': options.private,
|
||||
'is_announcement_only': options.announcement_only,
|
||||
'history_public_to_subscribers': options.history_public_to_subscribers,
|
||||
"stream_id": options.stream_id,
|
||||
"description": quote(options.description),
|
||||
"new_name": quote(options.new_name),
|
||||
"is_private": options.private,
|
||||
"is_announcement_only": options.announcement_only,
|
||||
"history_public_to_subscribers": options.history_public_to_subscribers,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
|
@ -13,11 +13,11 @@ and store them in JSON format.
|
|||
Example: get-history --stream announce --topic important"""
|
||||
|
||||
parser = zulip.add_default_arguments(argparse.ArgumentParser(usage=usage))
|
||||
parser.add_argument('--stream', required=True, help="The stream name to get the history")
|
||||
parser.add_argument('--topic', help="The topic name to get the history")
|
||||
parser.add_argument("--stream", required=True, help="The stream name to get the history")
|
||||
parser.add_argument("--topic", help="The topic name to get the history")
|
||||
parser.add_argument(
|
||||
'--filename',
|
||||
default='history.json',
|
||||
"--filename",
|
||||
default="history.json",
|
||||
help="The file name to store the fetched \
|
||||
history.\n Default 'history.json'",
|
||||
)
|
||||
|
@ -25,19 +25,19 @@ options = parser.parse_args()
|
|||
|
||||
client = zulip.init_from_options(options)
|
||||
|
||||
narrow = [{'operator': 'stream', 'operand': options.stream}]
|
||||
narrow = [{"operator": "stream", "operand": options.stream}]
|
||||
if options.topic:
|
||||
narrow.append({'operator': 'topic', 'operand': options.topic})
|
||||
narrow.append({"operator": "topic", "operand": options.topic})
|
||||
|
||||
request = {
|
||||
# Initially we have the anchor as 0, so that it starts fetching
|
||||
# from the oldest message in the narrow
|
||||
'anchor': 0,
|
||||
'num_before': 0,
|
||||
'num_after': 1000,
|
||||
'narrow': narrow,
|
||||
'client_gravatar': False,
|
||||
'apply_markdown': False,
|
||||
"anchor": 0,
|
||||
"num_before": 0,
|
||||
"num_after": 1000,
|
||||
"narrow": narrow,
|
||||
"client_gravatar": False,
|
||||
"apply_markdown": False,
|
||||
}
|
||||
|
||||
all_messages = [] # type: List[Dict[str, Any]]
|
||||
|
@ -47,17 +47,17 @@ while not found_newest:
|
|||
result = client.get_messages(request)
|
||||
try:
|
||||
found_newest = result["found_newest"]
|
||||
if result['messages']:
|
||||
if result["messages"]:
|
||||
# Setting the anchor to the next immediate message after the last fetched message.
|
||||
request['anchor'] = result['messages'][-1]['id'] + 1
|
||||
request["anchor"] = result["messages"][-1]["id"] + 1
|
||||
|
||||
all_messages.extend(result["messages"])
|
||||
except KeyError:
|
||||
# Might occur when the request is not returned with a success status
|
||||
print('Error occured: Payload was:')
|
||||
print("Error occured: Payload was:")
|
||||
print(result)
|
||||
quit()
|
||||
|
||||
with open(options.filename, "w+") as f:
|
||||
print('Writing %d messages...' % len(all_messages))
|
||||
print("Writing %d messages..." % len(all_messages))
|
||||
f.write(json.dumps(all_messages))
|
||||
|
|
|
@ -17,13 +17,13 @@ Example: get-messages --use-first-unread-anchor --num-before=5 \\
|
|||
|
||||
|
||||
parser = zulip.add_default_arguments(argparse.ArgumentParser(usage=usage))
|
||||
parser.add_argument('--anchor', type=int)
|
||||
parser.add_argument('--use-first-unread-anchor', action='store_true')
|
||||
parser.add_argument('--num-before', type=int, required=True)
|
||||
parser.add_argument('--num-after', type=int, required=True)
|
||||
parser.add_argument('--client-gravatar', action='store_true')
|
||||
parser.add_argument('--apply-markdown', action='store_true')
|
||||
parser.add_argument('--narrow')
|
||||
parser.add_argument("--anchor", type=int)
|
||||
parser.add_argument("--use-first-unread-anchor", action="store_true")
|
||||
parser.add_argument("--num-before", type=int, required=True)
|
||||
parser.add_argument("--num-after", type=int, required=True)
|
||||
parser.add_argument("--client-gravatar", action="store_true")
|
||||
parser.add_argument("--apply-markdown", action="store_true")
|
||||
parser.add_argument("--narrow")
|
||||
options = parser.parse_args()
|
||||
|
||||
client = zulip.init_from_options(options)
|
||||
|
@ -31,13 +31,13 @@ client = zulip.init_from_options(options)
|
|||
print(
|
||||
client.get_messages(
|
||||
{
|
||||
'anchor': options.anchor,
|
||||
'use_first_unread_anchor': options.use_first_unread_anchor,
|
||||
'num_before': options.num_before,
|
||||
'num_after': options.num_after,
|
||||
'narrow': options.narrow,
|
||||
'client_gravatar': options.client_gravatar,
|
||||
'apply_markdown': options.apply_markdown,
|
||||
"anchor": options.anchor,
|
||||
"use_first_unread_anchor": options.use_first_unread_anchor,
|
||||
"num_before": options.num_before,
|
||||
"num_after": options.num_after,
|
||||
"narrow": options.narrow,
|
||||
"client_gravatar": options.client_gravatar,
|
||||
"apply_markdown": options.apply_markdown,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
|
@ -12,7 +12,7 @@ Example: get-raw-message 42
|
|||
|
||||
|
||||
parser = zulip.add_default_arguments(argparse.ArgumentParser(usage=usage))
|
||||
parser.add_argument('message_id', type=int)
|
||||
parser.add_argument("message_id", type=int)
|
||||
options = parser.parse_args()
|
||||
|
||||
client = zulip.init_from_options(options)
|
||||
|
|
|
@ -10,7 +10,7 @@ Get all the topics for a specific stream.
|
|||
import zulip
|
||||
|
||||
parser = zulip.add_default_arguments(argparse.ArgumentParser(usage=usage))
|
||||
parser.add_argument('--stream-id', required=True)
|
||||
parser.add_argument("--stream-id", required=True)
|
||||
options = parser.parse_args()
|
||||
|
||||
client = zulip.init_from_options(options)
|
||||
|
|
|
@ -10,7 +10,7 @@ Get presence data for another user.
|
|||
import zulip
|
||||
|
||||
parser = zulip.add_default_arguments(argparse.ArgumentParser(usage=usage))
|
||||
parser.add_argument('--email', required=True)
|
||||
parser.add_argument("--email", required=True)
|
||||
options = parser.parse_args()
|
||||
|
||||
client = zulip.init_from_options(options)
|
||||
|
|
|
@ -10,7 +10,7 @@ Example: message-history 42
|
|||
"""
|
||||
|
||||
parser = zulip.add_default_arguments(argparse.ArgumentParser(usage=usage))
|
||||
parser.add_argument('message_id', type=int)
|
||||
parser.add_argument("message_id", type=int)
|
||||
options = parser.parse_args()
|
||||
|
||||
client = zulip.init_from_options(options)
|
||||
|
|
|
@ -11,17 +11,17 @@ Example: mute-topic unmute Denmark party
|
|||
"""
|
||||
|
||||
parser = zulip.add_default_arguments(argparse.ArgumentParser(usage=usage))
|
||||
parser.add_argument('op', choices=['mute', 'unmute'])
|
||||
parser.add_argument('stream')
|
||||
parser.add_argument('topic')
|
||||
parser.add_argument("op", choices=["mute", "unmute"])
|
||||
parser.add_argument("stream")
|
||||
parser.add_argument("topic")
|
||||
options = parser.parse_args()
|
||||
|
||||
client = zulip.init_from_options(options)
|
||||
|
||||
OPERATIONS = {'mute': 'add', 'unmute': 'remove'}
|
||||
OPERATIONS = {"mute": "add", "unmute": "remove"}
|
||||
|
||||
print(
|
||||
client.mute_topic(
|
||||
{'op': OPERATIONS[options.op], 'stream': options.stream, 'topic': options.topic}
|
||||
{"op": OPERATIONS[options.op], "stream": options.stream, "topic": options.topic}
|
||||
)
|
||||
)
|
||||
|
|
|
@ -12,18 +12,18 @@ Example: send-message --type=stream commits --subject="my subject" --message="te
|
|||
Example: send-message user1@example.com user2@example.com
|
||||
"""
|
||||
parser = zulip.add_default_arguments(argparse.ArgumentParser(usage=usage))
|
||||
parser.add_argument('recipients', nargs='+')
|
||||
parser.add_argument('--subject', default='test')
|
||||
parser.add_argument('--message', default='test message')
|
||||
parser.add_argument('--type', default='private')
|
||||
parser.add_argument("recipients", nargs="+")
|
||||
parser.add_argument("--subject", default="test")
|
||||
parser.add_argument("--message", default="test message")
|
||||
parser.add_argument("--type", default="private")
|
||||
options = parser.parse_args()
|
||||
|
||||
client = zulip.init_from_options(options)
|
||||
|
||||
message_data = {
|
||||
'type': options.type,
|
||||
'content': options.message,
|
||||
'subject': options.subject,
|
||||
'to': options.recipients,
|
||||
"type": options.type,
|
||||
"content": options.message,
|
||||
"subject": options.subject,
|
||||
"to": options.recipients,
|
||||
}
|
||||
print(client.send_message(message_data))
|
||||
|
|
|
@ -15,7 +15,7 @@ Specify your Zulip API credentials and server in a ~/.zuliprc file or using the
|
|||
import zulip
|
||||
|
||||
parser = zulip.add_default_arguments(argparse.ArgumentParser(usage=usage))
|
||||
parser.add_argument('--streams', action='store', required=True)
|
||||
parser.add_argument("--streams", action="store", required=True)
|
||||
options = parser.parse_args()
|
||||
|
||||
client = zulip.init_from_options(options)
|
||||
|
|
|
@ -15,7 +15,7 @@ Specify your Zulip API credentials and server in a ~/.zuliprc file or using the
|
|||
import zulip
|
||||
|
||||
parser = zulip.add_default_arguments(argparse.ArgumentParser(usage=usage))
|
||||
parser.add_argument('--streams', action='store', required=True)
|
||||
parser.add_argument("--streams", action="store", required=True)
|
||||
options = parser.parse_args()
|
||||
|
||||
client = zulip.init_from_options(options)
|
||||
|
|
|
@ -12,15 +12,15 @@ Example: update-message-flags remove starred 16 23 42
|
|||
"""
|
||||
|
||||
parser = zulip.add_default_arguments(argparse.ArgumentParser(usage=usage))
|
||||
parser.add_argument('op', choices=['add', 'remove'])
|
||||
parser.add_argument('flag')
|
||||
parser.add_argument('messages', type=int, nargs='+')
|
||||
parser.add_argument("op", choices=["add", "remove"])
|
||||
parser.add_argument("flag")
|
||||
parser.add_argument("messages", type=int, nargs="+")
|
||||
options = parser.parse_args()
|
||||
|
||||
client = zulip.init_from_options(options)
|
||||
|
||||
print(
|
||||
client.update_message_flags(
|
||||
{'op': options.op, 'flag': options.flag, 'messages': options.messages}
|
||||
{"op": options.op, "flag": options.flag, "messages": options.messages}
|
||||
)
|
||||
)
|
||||
|
|
|
@ -8,7 +8,7 @@ import zulip
|
|||
|
||||
|
||||
class StringIO(_StringIO):
|
||||
name = '' # https://github.com/python/typeshed/issues/598
|
||||
name = "" # https://github.com/python/typeshed/issues/598
|
||||
|
||||
|
||||
usage = """upload-file [options]
|
||||
|
@ -22,20 +22,20 @@ If no --file-path is specified, a placeholder text file will be used instead.
|
|||
"""
|
||||
|
||||
parser = zulip.add_default_arguments(argparse.ArgumentParser(usage=usage))
|
||||
parser.add_argument('--file-path', required=True)
|
||||
parser.add_argument("--file-path", required=True)
|
||||
options = parser.parse_args()
|
||||
|
||||
client = zulip.init_from_options(options)
|
||||
|
||||
if options.file_path:
|
||||
file = open(options.file_path, 'rb') # type: IO[Any]
|
||||
file = open(options.file_path, "rb") # type: IO[Any]
|
||||
else:
|
||||
file = StringIO('This is a test file.')
|
||||
file.name = 'test.txt'
|
||||
file = StringIO("This is a test file.")
|
||||
file.name = "test.txt"
|
||||
|
||||
response = client.upload_file(file)
|
||||
|
||||
try:
|
||||
print('File URI: {}'.format(response['uri']))
|
||||
print("File URI: {}".format(response["uri"]))
|
||||
except KeyError:
|
||||
print('Error! API response was: {}'.format(response))
|
||||
print("Error! API response was: {}".format(response))
|
||||
|
|
|
@ -4,7 +4,7 @@ from typing import Any, Dict, List
|
|||
|
||||
import zulip
|
||||
|
||||
welcome_text = 'Hello {}, Welcome to Zulip!\n \
|
||||
welcome_text = "Hello {}, Welcome to Zulip!\n \
|
||||
* The first thing you should do is to install the development environment. \
|
||||
We recommend following the vagrant setup as it is well documented and used \
|
||||
by most of the contributors. If you face any trouble during installation \
|
||||
|
@ -21,7 +21,7 @@ of the main projects you can contribute to are Zulip \
|
|||
a [bot](https://github.com/zulip/zulipbot) that you can contribute to!!\n \
|
||||
* We host our source code on GitHub. If you are not familiar with Git or \
|
||||
GitHub checkout [this](http://zulip.readthedocs.io/en/latest/git-guide.html) \
|
||||
guide. You don\'t have to learn everything but please go through it and learn \
|
||||
guide. You don't have to learn everything but please go through it and learn \
|
||||
the basics. We are here to help you if you are having any trouble. Post your \
|
||||
questions in #git help . \
|
||||
* Once you have completed these steps you can start contributing. You \
|
||||
|
@ -33,55 +33,55 @@ but if you want a bite size issue for mobile or electron feel free post in #mobi
|
|||
or #electron .\n \
|
||||
* Solving the first issue can be difficult. The key is to not give up. If you spend \
|
||||
enough time on the issue you should be able to solve it no matter what.\n \
|
||||
* Use `grep` command when you can\'t figure out what files to change :) For example \
|
||||
* Use `grep` command when you can't figure out what files to change :) For example \
|
||||
if you want know what files to modify in order to change Invite more users to Add \
|
||||
more users which you can see below the user status list, grep for "Invite more \
|
||||
users" in terminal.\n \
|
||||
* If you are stuck with something and can\'t figure out what to do you can ask \
|
||||
more users which you can see below the user status list, grep for \"Invite more \
|
||||
users\" in terminal.\n \
|
||||
* If you are stuck with something and can't figure out what to do you can ask \
|
||||
for help in #development help . But make sure that you tried your best to figure \
|
||||
out the issue by yourself\n \
|
||||
* If you are here for #Outreachy 2017-2018 or #GSoC don\'t worry much about \
|
||||
* If you are here for #Outreachy 2017-2018 or #GSoC don't worry much about \
|
||||
whether you will get selected or not. You will learn a lot contributing to \
|
||||
Zulip in course of next few months and if you do a good job at that you \
|
||||
will get selected too :)\n \
|
||||
* Most important of all welcome to the Zulip family :octopus:'
|
||||
* Most important of all welcome to the Zulip family :octopus:"
|
||||
|
||||
# These streams will cause the message to be sent
|
||||
streams_to_watch = ['new members']
|
||||
streams_to_watch = ["new members"]
|
||||
|
||||
# These streams will cause anyone who sends a message there to be removed from the watchlist
|
||||
streams_to_cancel = ['development help']
|
||||
streams_to_cancel = ["development help"]
|
||||
|
||||
|
||||
def get_watchlist() -> List[Any]:
|
||||
storage = client.get_storage()
|
||||
return list(storage['storage'].values())
|
||||
return list(storage["storage"].values())
|
||||
|
||||
|
||||
def set_watchlist(watchlist: List[str]) -> None:
|
||||
client.update_storage({'storage': dict(enumerate(watchlist))})
|
||||
client.update_storage({"storage": dict(enumerate(watchlist))})
|
||||
|
||||
|
||||
def handle_event(event: Dict[str, Any]) -> None:
|
||||
try:
|
||||
if event['type'] == 'realm_user' and event['op'] == 'add':
|
||||
if event["type"] == "realm_user" and event["op"] == "add":
|
||||
watchlist = get_watchlist()
|
||||
watchlist.append(event['person']['email'])
|
||||
watchlist.append(event["person"]["email"])
|
||||
set_watchlist(watchlist)
|
||||
return
|
||||
if event['type'] == 'message':
|
||||
stream = event['message']['display_recipient']
|
||||
if event["type"] == "message":
|
||||
stream = event["message"]["display_recipient"]
|
||||
if stream not in streams_to_watch and stream not in streams_to_cancel:
|
||||
return
|
||||
watchlist = get_watchlist()
|
||||
if event['message']['sender_email'] in watchlist:
|
||||
watchlist.remove(event['message']['sender_email'])
|
||||
if event["message"]["sender_email"] in watchlist:
|
||||
watchlist.remove(event["message"]["sender_email"])
|
||||
if stream not in streams_to_cancel:
|
||||
client.send_message(
|
||||
{
|
||||
'type': 'private',
|
||||
'to': event['message']['sender_email'],
|
||||
'content': welcome_text.format(event['message']['sender_short_name']),
|
||||
"type": "private",
|
||||
"to": event["message"]["sender_email"],
|
||||
"content": welcome_text.format(event["message"]["sender_short_name"]),
|
||||
}
|
||||
)
|
||||
set_watchlist(watchlist)
|
||||
|
@ -92,7 +92,7 @@ def handle_event(event: Dict[str, Any]) -> None:
|
|||
|
||||
def start_event_handler() -> None:
|
||||
print("Starting event handler...")
|
||||
client.call_on_each_event(handle_event, event_types=['realm_user', 'message'])
|
||||
client.call_on_each_event(handle_event, event_types=["realm_user", "message"])
|
||||
|
||||
|
||||
client = zulip.Client()
|
||||
|
|
|
@ -10,25 +10,25 @@ import zulip
|
|||
|
||||
logging.basicConfig()
|
||||
|
||||
log = logging.getLogger('zulip-send')
|
||||
log = logging.getLogger("zulip-send")
|
||||
|
||||
|
||||
def do_send_message(client: zulip.Client, message_data: Dict[str, Any]) -> bool:
|
||||
'''Sends a message and optionally prints status about the same.'''
|
||||
"""Sends a message and optionally prints status about the same."""
|
||||
|
||||
if message_data['type'] == 'stream':
|
||||
if message_data["type"] == "stream":
|
||||
log.info(
|
||||
'Sending message to stream "%s", subject "%s"... '
|
||||
% (message_data['to'], message_data['subject'])
|
||||
% (message_data["to"], message_data["subject"])
|
||||
)
|
||||
else:
|
||||
log.info('Sending message to %s... ' % (message_data['to'],))
|
||||
log.info("Sending message to %s... " % (message_data["to"],))
|
||||
response = client.send_message(message_data)
|
||||
if response['result'] == 'success':
|
||||
log.info('Message sent.')
|
||||
if response["result"] == "success":
|
||||
log.info("Message sent.")
|
||||
return True
|
||||
else:
|
||||
log.error(response['msg'])
|
||||
log.error(response["msg"])
|
||||
return False
|
||||
|
||||
|
||||
|
@ -46,27 +46,27 @@ def main() -> int:
|
|||
parser = zulip.add_default_arguments(argparse.ArgumentParser(usage=usage))
|
||||
|
||||
parser.add_argument(
|
||||
'recipients', nargs='*', help='email addresses of the recipients of the message'
|
||||
"recipients", nargs="*", help="email addresses of the recipients of the message"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-m', '--message', help='Specifies the message to send, prevents interactive prompting.'
|
||||
"-m", "--message", help="Specifies the message to send, prevents interactive prompting."
|
||||
)
|
||||
|
||||
group = parser.add_argument_group('Stream parameters')
|
||||
group = parser.add_argument_group("Stream parameters")
|
||||
group.add_argument(
|
||||
'-s',
|
||||
'--stream',
|
||||
dest='stream',
|
||||
action='store',
|
||||
help='Allows the user to specify a stream for the message.',
|
||||
"-s",
|
||||
"--stream",
|
||||
dest="stream",
|
||||
action="store",
|
||||
help="Allows the user to specify a stream for the message.",
|
||||
)
|
||||
group.add_argument(
|
||||
'-S',
|
||||
'--subject',
|
||||
dest='subject',
|
||||
action='store',
|
||||
help='Allows the user to specify a subject for the message.',
|
||||
"-S",
|
||||
"--subject",
|
||||
dest="subject",
|
||||
action="store",
|
||||
help="Allows the user to specify a subject for the message.",
|
||||
)
|
||||
|
||||
options = parser.parse_args()
|
||||
|
@ -75,11 +75,11 @@ def main() -> int:
|
|||
logging.getLogger().setLevel(logging.INFO)
|
||||
# Sanity check user data
|
||||
if len(options.recipients) != 0 and (options.stream or options.subject):
|
||||
parser.error('You cannot specify both a username and a stream/subject.')
|
||||
parser.error("You cannot specify both a username and a stream/subject.")
|
||||
if len(options.recipients) == 0 and (bool(options.stream) != bool(options.subject)):
|
||||
parser.error('Stream messages must have a subject')
|
||||
parser.error("Stream messages must have a subject")
|
||||
if len(options.recipients) == 0 and not (options.stream and options.subject):
|
||||
parser.error('You must specify a stream/subject or at least one recipient.')
|
||||
parser.error("You must specify a stream/subject or at least one recipient.")
|
||||
|
||||
client = zulip.init_from_options(options)
|
||||
|
||||
|
@ -88,16 +88,16 @@ def main() -> int:
|
|||
|
||||
if options.stream:
|
||||
message_data = {
|
||||
'type': 'stream',
|
||||
'content': options.message,
|
||||
'subject': options.subject,
|
||||
'to': options.stream,
|
||||
"type": "stream",
|
||||
"content": options.message,
|
||||
"subject": options.subject,
|
||||
"to": options.stream,
|
||||
}
|
||||
else:
|
||||
message_data = {
|
||||
'type': 'private',
|
||||
'content': options.message,
|
||||
'to': options.recipients,
|
||||
"type": "private",
|
||||
"content": options.message,
|
||||
"to": options.recipients,
|
||||
}
|
||||
|
||||
if not do_send_message(client, message_data):
|
||||
|
@ -105,5 +105,5 @@ def main() -> int:
|
|||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue