This commit is contained in:
Midgard 2021-03-14 03:39:22 +01:00
parent ccb3d06be5
commit 6a35ad2554
Signed by: midgard
GPG key ID: 511C112F1331BBB4

View file

@ -31,16 +31,13 @@ def yes_no(x):
return "yes" if x else "no" return "yes" if x else "no"
def get_posts_for_channel(self, channel_id: str, **kwargs) -> Generator[Dict, None, None]: def get_posts_for_channel(self, channel_id: str, progress=lambda x: None, **kwargs) -> Generator[Dict, None, None]:
""" """
Generator: Get a page of posts in a channel. Use the query parameters to modify the behaviour of this endpoint. @raises ApiException: Passed on from lower layers.
@param channel_id: The channel ID to iterate over.
Raises:
ApiException: Passed on from lower layers.
""" """
page = 0 page = 0
posts = []
while True: while True:
data_page = self._get(f"/v4/channels/{channel_id}/posts", params={"page":str(page), "per_page":200, **kwargs}) data_page = self._get(f"/v4/channels/{channel_id}/posts", params={"page":str(page), "per_page":200, **kwargs})
@ -48,11 +45,15 @@ def get_posts_for_channel(self, channel_id: str, **kwargs) -> Generator[Dict, No
break break
page += 1 page += 1
for order in data_page["order"]: posts.extend(data_page["posts"][order] for order in data_page["order"])
yield data_page["posts"][order] progress(len(posts))
sleep(0.1) sleep(0.1)
# Mattermost gives newest first, so reverse order
posts.reverse()
return posts
ID_PREFIX = "id:" ID_PREFIX = "id:"
@ -105,15 +106,17 @@ def login(mm_api, parsed):
f"TOTP token provided: {yes_no(parsed.totp)}", f"TOTP token provided: {yes_no(parsed.totp)}",
file=sys.stderr) file=sys.stderr)
mm_api.login(parsed.user, parsed.password, parsed.totp) mm_api.login(parsed.user, parsed.password, parsed.totp)
return mm_api._bearer
if parsed.format == "json":
print(json.dumps({"token": mm_api._bearer}))
elif parsed.format == "tsv":
print(mm_api._bearer)
else:
assert False
def cat(mm_api: mattermost.MMApi, parsed): def cat(mm_api: mattermost.MMApi, parsed):
# FIXME Wrong order
channels = [ channels = [
resolve_team_channel(mm_api, query) resolve_team_channel(mm_api, query)
for query in parsed.channels for query in parsed.channels
@ -135,9 +138,40 @@ def cat(mm_api: mattermost.MMApi, parsed):
def attribute(key_value): def attribute(key_value):
return key_value return key_value
for post in get_posts_for_channel(mm_api, channel["id"], after=parsed.after): posts = get_posts_for_channel(mm_api, channel["id"], after=parsed.after)
for post in posts:
print(post_str(attribute, post, parsed)) print(post_str(attribute, post, parsed))
print(parsed.after)
def send(mm_api: mattermost.MMApi, parsed):
read_stdin = parsed.message is None or parsed.channel is None
team, channel = resolve_team_channel(mm_api, parsed.channel) if parsed.channel is not None else (None, None)
if read_stdin:
if sys.stdin.isatty():
print("Reading from tty. (You can type the message objects below. Or maybe you meant to redirect something to stdin.)", file=sys.stderr)
for line in sys.stdin:
msg = json.loads(line)
if "channel_id" in msg:
channel_id = msg["channel_id"]
elif "channel" in msg:
_, local_channel = resolve_team_channel(mm_api, msg["channel"])
channel_id = local_channel["id"]
elif channel is not None:
channel_id = channel["id"]
else:
print(f"Illegal message, missing channel: {line.strip()}", file=sys.stderr)
raise ValueError("Illegal message, missing channel")
sent = mm_api.create_post(channel_id, msg["message"], props={"from_mmcli": "true"}, filepaths=msg.get("attachments", msg.get("attachments")))
print(sent)
else:
sent = mm_api.create_post(channel["id"], parsed.message, props={"from_mmcli": "true"}, filepaths=parsed.attach)
print(sent)
def post_str(attribute, post, parsed): def post_str(attribute, post, parsed):
@ -152,14 +186,16 @@ def post_str(attribute, post, parsed):
if parsed.format == "tsv": if parsed.format == "tsv":
msg = obj.get("message", "").replace("\\", "\\\\").replace("\t", r"\t").replace("\n", r"\n") msg = obj.get("message", "").replace("\\", "\\\\").replace("\t", r"\t").replace("\n", r"\n")
return f"{obj['id']}\t{obj['create_at']}\t{obj.get('username') or obj['user_id']}\t{msg}" return f"{obj['id']}\t{obj['create_at']}\t{obj.get('username') or obj['user_id']}\t{msg}"
assert False
ACTIONS = { ACTIONS = {
"login": {"function": login, "accesstoken_required": False}, "login": {"function": login, "accesstoken_required": False},
"cat": {"function": cat}, "cat": {"function": cat},
"send": {"function": send},
} }
FORMATTERS = { "json", "tsv", "csv" } FORMATTERS = { "json", "tsv" }
ENVVAR_SERVER = "MM_SERVER" ENVVAR_SERVER = "MM_SERVER"
ENVVAR_USERNAME = "MM_USERNAME" ENVVAR_USERNAME = "MM_USERNAME"
@ -200,13 +236,26 @@ Hint: JSON output can be filtered on the command line with jq(1).
parser_login.add_argument("--password", default=os.getenv(ENVVAR_PASSWORD)) parser_login.add_argument("--password", default=os.getenv(ENVVAR_PASSWORD))
parser_login.add_argument("--totp", default=os.getenv(ENVVAR_TOTP)) parser_login.add_argument("--totp", default=os.getenv(ENVVAR_TOTP))
parser_cat = subparsers.add_parser("cat", help="list messages in channel(s)") # TODO support multiple channels
parser_cat.add_argument( # parser_cat = subparsers.add_parser("cat", help="list messages in channel(s)")
"channels", nargs="+", help="URL names of team and channel: '<team>/<channel>'") # parser_cat.add_argument(
# "channels", nargs="+", help="URL names of team and channel: '<team>/<channel>'")
parser_cat = subparsers.add_parser("cat", help="list messages in channel")
parser_cat.add_argument("channel", help="URL names of team and channel: '<team>/<channel>'")
# ---
parser_cat.add_argument("--after", help="all after post with ID") parser_cat.add_argument("--after", help="all after post with ID")
parser_cat.add_argument("--since", help="all after timestamp") parser_cat.add_argument("--since", help="all after timestamp")
parser_cat.add_argument("-f", "--follow", help="keep running, printing new posts as they come in") parser_cat.add_argument("-f", "--follow", help="keep running, printing new posts as they come in")
parser_send = subparsers.add_parser("send", help="send message(s)")
parser_send.add_argument(
"--channel", help="URL names of team and channel: '<team>/<channel>'; if not provided, "
"messages must be provided on stdin and each must specify channel")
parser_send.add_argument(
"--message", help="message; if not provided, messages will be expected on stdin")
parser_send.add_argument(
"--attach", nargs="+", help="filename of file to attach")
parsed = argparser.parse_args() parsed = argparser.parse_args()
if not parsed.server: if not parsed.server: