Compare commits

..

2 commits

Author SHA1 Message Date
ea433a93c8
Add addreaction and removereaction 2024-11-22 22:46:47 +01:00
456e10305b
Make getting posts more robust 2024-11-22 22:46:08 +01:00

View file

@ -44,6 +44,12 @@ def warn_if_tty(you_can_type="the message objects", write_message_to=sys.stderr)
print(f"Reading from tty. (You can type {you_can_type} below. Or maybe you meant to redirect something to stdin.)", file=write_message_to) print(f"Reading from tty. (You can type {you_can_type} below. Or maybe you meant to redirect something to stdin.)", file=write_message_to)
def get_user_id(mm_api: mattermost.MMApi):
if mm_api._my_user_id is None:
mm_api._my_user_id = mm_api.get_user()["id"]
return mm_api._my_user_id
def get_posts_for_channel(self, channel_id: str, progress=lambda x: None, after=None, **kwargs) -> Iterable[Dict]: def get_posts_for_channel(self, channel_id: str, progress=lambda x: None, after=None, **kwargs) -> Iterable[Dict]:
""" """
@raises ApiException: Passed on from lower layers. @raises ApiException: Passed on from lower layers.
@ -67,13 +73,15 @@ def get_posts_for_channel(self, channel_id: str, progress=lambda x: None, after=
progress(total) progress(total)
if len(order) < per_page: if len(order) < per_page:
break break
page += 1 after = order[-1]
sleep(0.1) sleep(0.1)
else: else:
# All posts in channel: API gives pages with NEWEST messages first, so reverse the order in # All posts in channel: API gives pages with NEWEST messages first, so reverse the order in
# the end (and don't reverse the order of each page separately) # the end (and don't reverse the order of each page separately)
posts = [] posts = []
# To avoid race conditions when new messages are posted while the channel is being fetched
post_ids = set()
while True: while True:
data_page = self._get(f"/v4/channels/{channel_id}/posts", params={"page": page, "per_page": per_page, **kwargs}) data_page = self._get(f"/v4/channels/{channel_id}/posts", params={"page": page, "per_page": per_page, **kwargs})
order = data_page["order"] order = data_page["order"]
@ -81,7 +89,9 @@ def get_posts_for_channel(self, channel_id: str, progress=lambda x: None, after=
posts.extend( posts.extend(
data_page["posts"][post_id] data_page["posts"][post_id]
for post_id in order for post_id in order
if post_id not in post_ids
) )
post_ids |= set(order)
progress(len(posts)) progress(len(posts))
if len(order) < per_page: if len(order) < per_page:
break break
@ -414,16 +424,24 @@ def edit(mm_api: mattermost.MMApi, cmdline_args):
mm_api.patch_post(cmdline_args.msgid, message=new_text) mm_api.patch_post(cmdline_args.msgid, message=new_text)
def addreaction(mm_api: mattermost.MMApi, cmdline_args):
# "me" does not work here, explicit user ID required. (sigh)
mm_api.create_reaction(get_user_id(mm_api), cmdline_args.msgid, cmdline_args.emoji_name)
def removereaction(mm_api: mattermost.MMApi, cmdline_args):
# pylint: disable=protected-access # library recommends this in docs
mm_api._delete(f"/v4/users/me/posts/{cmdline_args.msgid}/reactions/{cmdline_args.emoji_name}")
def status(mm_api: mattermost.MMApi, cmdline_args): def status(mm_api: mattermost.MMApi, cmdline_args):
if not cmdline_args.status: if not cmdline_args.status:
raise ValueError("No status selected") raise ValueError("No status selected")
# 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={ mm_api._put(f"/v4/users/me/status", data={
"user_id": my_user_id, # 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 work here here. (sigh)
"user_id": get_user_id(mm_api),
"status": cmdline_args.status "status": cmdline_args.status
}) })
@ -501,6 +519,8 @@ ACTIONS = {
"send": {"function": send}, "send": {"function": send},
"rm": {"function": rm}, "rm": {"function": rm},
"edit": {"function": edit}, "edit": {"function": edit},
"addreaction": {"function": addreaction},
"removereaction": {"function": removereaction},
"status": {"function": status}, "status": {"function": status},
"customstatus": {"function": customstatus}, "customstatus": {"function": customstatus},
"lastread": {"function": lastread}, "lastread": {"function": lastread},
@ -602,6 +622,16 @@ The input format accepted on stdin is one JSON object per line. The possible fie
parser_edit.add_argument( parser_edit.add_argument(
"--message", help="message; if not provided, message will be expected on stdin") "--message", help="message; if not provided, message will be expected on stdin")
parser_addreaction = subparsers.add_parser(
"addreaction", help="add emoji reaction to a message")
parser_addreaction.add_argument("msgid", help="ID of message")
parser_addreaction.add_argument("emoji_name", help="name of the emoji to add (without colons)")
parser_removereaction = subparsers.add_parser(
"removereaction", help="remove emoji reaction from a message")
parser_removereaction.add_argument("msgid", help="ID of message")
parser_removereaction.add_argument("emoji_name", help="name of the emoji to remove (without colons)")
parser_status = subparsers.add_parser("status", help="update user status") 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("--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("--away", dest="status", action="store_const", const="away", help="Set status to away")