diff --git a/mmcli.py b/mmcli.py index 82bb2c3..9628035 100755 --- a/mmcli.py +++ b/mmcli.py @@ -9,6 +9,7 @@ import re from time import sleep import threading import mattermost +from parsedt import parse_datetime_to_utc from mmws import MMws @@ -323,6 +324,40 @@ def send(mm_api: mattermost.MMApi, cmdline_args): print(sent) +def status(mm_api: mattermost.MMApi, cmdline_args): + if not cmdline_args.status: + raise ValueError("No status selected") + + until = None + if cmdline_args.until: + if cmdline_args.status != "dnd": + raise ValueError("--until works only with --dnd due to an API limitation") + until = parse_datetime_to_utc(cmdline_args.until) + + # This API endpoint requires the user ID to be passed explicitly in the request body, + # duplicating the info in the URL. But "me" does not suffice here. + my_user_id = mm_api.get_user()["id"] + + mm_api._put(f"/v4/users/me/status", data={ + "user_id": my_user_id, + "status": cmdline_args.status, + "dnd_end_time": int(until.timestamp()) if until else None + }) + + +def customstatus(mm_api: mattermost.MMApi, cmdline_args): + until = parse_datetime_to_utc(cmdline_args.until) if cmdline_args.until else None + + if cmdline_args.text or cmdline_args.emoji: + mm_api._put(f"/v4/users/me/status/custom", data={ + "emoji": cmdline_args.emoji, + "text": cmdline_args.text, + "expires_at": until.isoformat() if until else None + }) + else: + mm_api._delete(f"/v4/users/me/status/custom") + + def tsv_escape(text): return text.replace("\\", "\\\\").replace("\t", r"\t").replace("\n", r"\n") @@ -363,6 +398,8 @@ ACTIONS = { "cat": {"function": cat}, "ls": {"function": ls}, "send": {"function": send}, + "status": {"function": status}, + "customstatus": {"function": customstatus}, } FORMATTERS = { "json", "tsv" } @@ -430,6 +467,19 @@ Hint: JSON output can be filtered on the command line with jq(1). parser_send.add_argument( "--attach", nargs="+", help="filename of file to attach") + parser_status = subparsers.add_parser("status", help="update user status") + parser_status.add_argument("--online", dest="status", action="store_const", const="online", help="Set status to online") + parser_status.add_argument("--away", dest="status", action="store_const", const="away", help="Set status to away") + parser_status.add_argument("--dnd", dest="status", action="store_const", const="dnd", help="Set status to 'do not disturb'") + parser_status.add_argument("--offline", dest="status", action="store_const", const="offline", help="Set status to offline") + parser_status.add_argument("--until", help="Datetime of when to clear the status (only for --dnd)") + + parser_customstatus = subparsers.add_parser("customstatus", + help="update custom user status (emoji and message)") + parser_customstatus.add_argument("--until", help="Datetime of when to clear the custom status") + parser_customstatus.add_argument("--emoji", help="Name of emoji (without colons), e.g. coffee") + parser_customstatus.add_argument("text" , help="Text for the status", nargs="?") + parsed = argparser.parse_args() if not parsed.server: diff --git a/parsedt.py b/parsedt.py new file mode 100644 index 0000000..8d215d6 --- /dev/null +++ b/parsedt.py @@ -0,0 +1,18 @@ +""" +Wrapper around date/time parsing +""" + +import datetime +from dateutil.parser import parse + + +def parse_datetime_to_utc(string) -> datetime.datetime: + parsed = parse(string) + + if parsed.tzinfo is None: + # Convert to timezone aware datetime with the system's timezone + converted = parsed.astimezone() + else: + converted = parsed + + return converted.astimezone(datetime.timezone.utc) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f4c462f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +python-dateutil>=2.8.2,<3.0.0 +mattermost>=5.33.0