Add thread actions to ThreadABC
This commit is contained in:
@@ -728,112 +728,6 @@ class Client:
|
|||||||
data = {"message_id": mid}
|
data = {"message_id": mid}
|
||||||
j = self._payload_post("/messaging/unsend_message/?dpr=1", data)
|
j = self._payload_post("/messaging/unsend_message/?dpr=1", data)
|
||||||
|
|
||||||
def forward_attachment(self, attachment_id, thread_id=None):
|
|
||||||
"""Forward an attachment.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
attachment_id: Attachment ID to forward
|
|
||||||
thread_id: User/Group ID to send to. See :ref:`intro_threads`
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
FBchatException: If request failed
|
|
||||||
"""
|
|
||||||
data = {
|
|
||||||
"attachment_id": attachment_id,
|
|
||||||
"recipient_map[{}]".format(
|
|
||||||
_util.generate_offline_threading_id()
|
|
||||||
): thread_id,
|
|
||||||
}
|
|
||||||
j = self._payload_post("/mercury/attachments/forward/", data)
|
|
||||||
if not j.get("success"):
|
|
||||||
raise FBchatFacebookError(
|
|
||||||
"Failed forwarding attachment: {}".format(j["error"]),
|
|
||||||
fb_error_message=j["error"],
|
|
||||||
)
|
|
||||||
|
|
||||||
def change_thread_title(self, title, thread_id=None, thread_type=ThreadType.USER):
|
|
||||||
"""Change title of a thread.
|
|
||||||
|
|
||||||
If this is executed on a user thread, this will change the nickname of that
|
|
||||||
user, effectively changing the title.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
title: New group thread title
|
|
||||||
thread_id: Group ID to change title of. See :ref:`intro_threads`
|
|
||||||
thread_type (ThreadType): See :ref:`intro_threads`
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
FBchatException: If request failed
|
|
||||||
"""
|
|
||||||
if thread_type == ThreadType.USER:
|
|
||||||
# The thread is a user, so we change the user's nickname
|
|
||||||
return self.change_nickname(
|
|
||||||
title, thread_id, thread_id=thread_id, thread_type=thread_type
|
|
||||||
)
|
|
||||||
|
|
||||||
data = {"thread_name": title, "thread_id": thread_id}
|
|
||||||
j = self._payload_post("/messaging/set_thread_name/?dpr=1", data)
|
|
||||||
|
|
||||||
def change_nickname(
|
|
||||||
self, nickname, user_id, thread_id=None, thread_type=ThreadType.USER
|
|
||||||
):
|
|
||||||
"""Change the nickname of a user in a thread.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
nickname: New nickname
|
|
||||||
user_id: User that will have their nickname changed
|
|
||||||
thread_id: User/Group ID to change color of. See :ref:`intro_threads`
|
|
||||||
thread_type (ThreadType): See :ref:`intro_threads`
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
FBchatException: If request failed
|
|
||||||
"""
|
|
||||||
data = {
|
|
||||||
"nickname": nickname,
|
|
||||||
"participant_id": user_id,
|
|
||||||
"thread_or_other_fbid": thread_id,
|
|
||||||
}
|
|
||||||
j = self._payload_post(
|
|
||||||
"/messaging/save_thread_nickname/?source=thread_settings&dpr=1", data
|
|
||||||
)
|
|
||||||
|
|
||||||
def change_thread_color(self, color, thread_id=None):
|
|
||||||
"""Change thread color.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
color (ThreadColor): New thread color
|
|
||||||
thread_id: User/Group ID to change color of. See :ref:`intro_threads`
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
FBchatException: If request failed
|
|
||||||
"""
|
|
||||||
data = {
|
|
||||||
"color_choice": color.value if color != ThreadColor.MESSENGER_BLUE else "",
|
|
||||||
"thread_or_other_fbid": thread_id,
|
|
||||||
}
|
|
||||||
j = self._payload_post(
|
|
||||||
"/messaging/save_thread_color/?source=thread_settings&dpr=1", data
|
|
||||||
)
|
|
||||||
|
|
||||||
def change_thread_emoji(self, emoji, thread_id=None):
|
|
||||||
"""Change thread color.
|
|
||||||
|
|
||||||
Note:
|
|
||||||
While changing the emoji, the Facebook web client actually sends multiple
|
|
||||||
different requests, though only this one is required to make the change.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
color: New thread emoji
|
|
||||||
thread_id: User/Group ID to change emoji of. See :ref:`intro_threads`
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
FBchatException: If request failed
|
|
||||||
"""
|
|
||||||
data = {"emoji_choice": emoji, "thread_or_other_fbid": thread_id}
|
|
||||||
j = self._payload_post(
|
|
||||||
"/messaging/save_thread_emoji/?source=thread_settings&dpr=1", data
|
|
||||||
)
|
|
||||||
|
|
||||||
def react_to_message(self, message_id, reaction):
|
def react_to_message(self, message_id, reaction):
|
||||||
"""React to a message, or removes reaction.
|
"""React to a message, or removes reaction.
|
||||||
|
|
||||||
@@ -855,32 +749,6 @@ class Client:
|
|||||||
j = self._payload_post("/webgraphql/mutation", data)
|
j = self._payload_post("/webgraphql/mutation", data)
|
||||||
_util.handle_graphql_errors(j)
|
_util.handle_graphql_errors(j)
|
||||||
|
|
||||||
def create_plan(self, plan, thread_id=None):
|
|
||||||
"""Set a plan.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
plan (Plan): Plan to set
|
|
||||||
thread_id: User/Group ID to send plan to. See :ref:`intro_threads`
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
FBchatException: If request failed
|
|
||||||
"""
|
|
||||||
data = {
|
|
||||||
"event_type": "EVENT",
|
|
||||||
"event_time": _util.datetime_to_seconds(plan.time),
|
|
||||||
"title": plan.title,
|
|
||||||
"thread_id": thread_id,
|
|
||||||
"location_id": plan.location_id or "",
|
|
||||||
"location_name": plan.location or "",
|
|
||||||
"acontext": ACONTEXT,
|
|
||||||
}
|
|
||||||
j = self._payload_post("/ajax/eventreminder/create", data)
|
|
||||||
if "error" in j:
|
|
||||||
raise FBchatFacebookError(
|
|
||||||
"Failed creating plan: {}".format(j["error"]),
|
|
||||||
fb_error_message=j["error"],
|
|
||||||
)
|
|
||||||
|
|
||||||
def edit_plan(self, plan, new_plan):
|
def edit_plan(self, plan, new_plan):
|
||||||
"""Edit a plan.
|
"""Edit a plan.
|
||||||
|
|
||||||
@@ -931,33 +799,6 @@ class Client:
|
|||||||
}
|
}
|
||||||
j = self._payload_post("/ajax/eventreminder/rsvp", data)
|
j = self._payload_post("/ajax/eventreminder/rsvp", data)
|
||||||
|
|
||||||
def create_poll(self, poll, thread_id=None):
|
|
||||||
"""Create poll in a group thread.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
poll (Poll): Poll to create
|
|
||||||
thread_id: User/Group ID to create poll in. See :ref:`intro_threads`
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
FBchatException: If request failed
|
|
||||||
"""
|
|
||||||
# We're using ordered dictionaries, because the Facebook endpoint that parses
|
|
||||||
# the POST parameters is badly implemented, and deals with ordering the options
|
|
||||||
# wrongly. If you can find a way to fix this for the endpoint, or if you find
|
|
||||||
# another endpoint, please do suggest it ;)
|
|
||||||
data = OrderedDict([("question_text", poll.title), ("target_id", thread_id)])
|
|
||||||
|
|
||||||
for i, option in enumerate(poll.options):
|
|
||||||
data["option_text_array[{}]".format(i)] = option.text
|
|
||||||
data["option_is_selected_array[{}]".format(i)] = str(int(option.vote))
|
|
||||||
|
|
||||||
j = self._payload_post("/messaging/group_polling/create_poll/?dpr=1", data)
|
|
||||||
if j.get("status") != "success":
|
|
||||||
raise FBchatFacebookError(
|
|
||||||
"Failed creating poll: {}".format(j.get("errorTitle")),
|
|
||||||
fb_error_message=j.get("errorMessage"),
|
|
||||||
)
|
|
||||||
|
|
||||||
def update_poll_vote(self, poll_id, option_ids=[], new_options=[]):
|
def update_poll_vote(self, poll_id, option_ids=[], new_options=[]):
|
||||||
"""Update a poll vote.
|
"""Update a poll vote.
|
||||||
|
|
||||||
@@ -986,25 +827,6 @@ class Client:
|
|||||||
fb_error_message=j.get("errorMessage"),
|
fb_error_message=j.get("errorMessage"),
|
||||||
)
|
)
|
||||||
|
|
||||||
def set_typing_status(self, status, thread_id=None, thread_type=None):
|
|
||||||
"""Set users typing status in a thread.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
status (TypingStatus): Specify the typing status
|
|
||||||
thread_id: User/Group ID to change status in. See :ref:`intro_threads`
|
|
||||||
thread_type (ThreadType): See :ref:`intro_threads`
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
FBchatException: If request failed
|
|
||||||
"""
|
|
||||||
data = {
|
|
||||||
"typ": status.value,
|
|
||||||
"thread": thread_id,
|
|
||||||
"to": thread_id if thread_type == ThreadType.USER else "",
|
|
||||||
"source": "mercury-chat",
|
|
||||||
}
|
|
||||||
j = self._payload_post("/ajax/messaging/typ.php", data)
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
END SEND METHODS
|
END SEND METHODS
|
||||||
"""
|
"""
|
||||||
@@ -1146,21 +968,6 @@ class Client:
|
|||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def mark_as_spam(self, thread_id=None):
|
|
||||||
"""Mark a thread as spam, and delete it.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
thread_id: User/Group ID to mark as spam. See :ref:`intro_threads`
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
True
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
FBchatException: If request failed
|
|
||||||
"""
|
|
||||||
j = self._payload_post("/ajax/mercury/mark_spam.php?dpr=1", {"id": thread_id})
|
|
||||||
return True
|
|
||||||
|
|
||||||
def delete_messages(self, message_ids):
|
def delete_messages(self, message_ids):
|
||||||
"""Delete specified messages.
|
"""Delete specified messages.
|
||||||
|
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
import abc
|
import abc
|
||||||
import attr
|
import attr
|
||||||
|
import datetime
|
||||||
from ._core import attrs_default, Enum, Image
|
from ._core import attrs_default, Enum, Image
|
||||||
from . import _util, _session
|
from . import _util, _exception, _session
|
||||||
from typing import MutableMapping, Any, Iterable, Tuple
|
from typing import MutableMapping, Any, Iterable, Tuple
|
||||||
|
|
||||||
|
|
||||||
@@ -306,6 +307,144 @@ class ThreadABC(metaclass=abc.ABCMeta):
|
|||||||
yield Attachment(id=i["node"].get("legacy_attachment_id"))
|
yield Attachment(id=i["node"].get("legacy_attachment_id"))
|
||||||
del j[self.id]["message_shared_media"]["edges"][0]
|
del j[self.id]["message_shared_media"]["edges"][0]
|
||||||
|
|
||||||
|
def set_nickname(self, user_id: str, nickname: str):
|
||||||
|
"""Change the nickname of a user in the thread.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id: User that will have their nickname changed
|
||||||
|
nickname: New nickname
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
"nickname": nickname,
|
||||||
|
"participant_id": user_id,
|
||||||
|
"thread_or_other_fbid": self.id,
|
||||||
|
}
|
||||||
|
j = self.session._payload_post(
|
||||||
|
"/messaging/save_thread_nickname/?source=thread_settings&dpr=1", data
|
||||||
|
)
|
||||||
|
|
||||||
|
def set_color(self, color: ThreadColor):
|
||||||
|
"""Change thread color.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
color: New thread color
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
"color_choice": color.value if color != ThreadColor.MESSENGER_BLUE else "",
|
||||||
|
"thread_or_other_fbid": self.id,
|
||||||
|
}
|
||||||
|
j = self.session._payload_post(
|
||||||
|
"/messaging/save_thread_color/?source=thread_settings&dpr=1", data
|
||||||
|
)
|
||||||
|
|
||||||
|
def set_emoji(self, emoji: str):
|
||||||
|
"""Change thread color.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
emoji: New thread emoji
|
||||||
|
"""
|
||||||
|
data = {"emoji_choice": emoji, "thread_or_other_fbid": self.id}
|
||||||
|
# While changing the emoji, the Facebook web client actually sends multiple
|
||||||
|
# different requests, though only this one is required to make the change.
|
||||||
|
j = self.session._payload_post(
|
||||||
|
"/messaging/save_thread_emoji/?source=thread_settings&dpr=1", data
|
||||||
|
)
|
||||||
|
|
||||||
|
def forward_attachment(self, attachment_id):
|
||||||
|
"""Forward an attachment.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
attachment_id: Attachment ID to forward
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
"attachment_id": attachment_id,
|
||||||
|
"recipient_map[{}]".format(_util.generate_offline_threading_id()): self.id,
|
||||||
|
}
|
||||||
|
j = self.session._payload_post("/mercury/attachments/forward/", data)
|
||||||
|
if not j.get("success"):
|
||||||
|
raise _exception.FBchatFacebookError(
|
||||||
|
"Failed forwarding attachment: {}".format(j["error"]),
|
||||||
|
fb_error_message=j["error"],
|
||||||
|
)
|
||||||
|
|
||||||
|
def _set_typing(self, typing):
|
||||||
|
data = {
|
||||||
|
"typ": "1" if typing else "0",
|
||||||
|
"thread": self.id,
|
||||||
|
# TODO: This
|
||||||
|
"to": self.id if thread_type == ThreadType.USER else "",
|
||||||
|
"source": "mercury-chat",
|
||||||
|
}
|
||||||
|
j = self.session._payload_post("/ajax/messaging/typ.php", data)
|
||||||
|
|
||||||
|
def start_typing(self):
|
||||||
|
"""Set the current user to start typing in the thread."""
|
||||||
|
self._set_typing(True)
|
||||||
|
|
||||||
|
def stop_typing(self):
|
||||||
|
"""Set the current user to stop typing in the thread."""
|
||||||
|
self._set_typing(False)
|
||||||
|
|
||||||
|
def create_plan(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
at: datetime.datetime,
|
||||||
|
location_name: str = None,
|
||||||
|
location_id: str = None,
|
||||||
|
):
|
||||||
|
"""Create a new plan.
|
||||||
|
|
||||||
|
# TODO: Arguments
|
||||||
|
|
||||||
|
Args:
|
||||||
|
title: Name of the new plan
|
||||||
|
at: When the plan is for
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
"event_type": "EVENT",
|
||||||
|
"event_time": _util.datetime_to_seconds(at),
|
||||||
|
"title": name,
|
||||||
|
"thread_id": self.id,
|
||||||
|
"location_id": location_id or "",
|
||||||
|
"location_name": location or "",
|
||||||
|
"acontext": ACONTEXT,
|
||||||
|
}
|
||||||
|
j = self.session._payload_post("/ajax/eventreminder/create", data)
|
||||||
|
if "error" in j:
|
||||||
|
raise _exception.FBchatFacebookError(
|
||||||
|
"Failed creating plan: {}".format(j["error"]),
|
||||||
|
fb_error_message=j["error"],
|
||||||
|
)
|
||||||
|
|
||||||
|
def create_poll(self, question: str, options=Iterable[Tuple[str, bool]]):
|
||||||
|
"""Create poll in a thread.
|
||||||
|
|
||||||
|
# TODO: Arguments
|
||||||
|
"""
|
||||||
|
# We're using ordered dictionaries, because the Facebook endpoint that parses
|
||||||
|
# the POST parameters is badly implemented, and deals with ordering the options
|
||||||
|
# wrongly. If you can find a way to fix this for the endpoint, or if you find
|
||||||
|
# another endpoint, please do suggest it ;)
|
||||||
|
data = OrderedDict([("question_text", question), ("target_id", self.id)])
|
||||||
|
|
||||||
|
for i, (text, vote) in enumerate(options):
|
||||||
|
data["option_text_array[{}]".format(i)] = text
|
||||||
|
data["option_is_selected_array[{}]".format(i)] = str(int(vote))
|
||||||
|
|
||||||
|
j = self.session._payload_post(
|
||||||
|
"/messaging/group_polling/create_poll/?dpr=1", data
|
||||||
|
)
|
||||||
|
if j.get("status") != "success":
|
||||||
|
raise _exception.FBchatFacebookError(
|
||||||
|
"Failed creating poll: {}".format(j.get("errorTitle")),
|
||||||
|
fb_error_message=j.get("errorMessage"),
|
||||||
|
)
|
||||||
|
|
||||||
|
def mark_as_spam(self):
|
||||||
|
"""Mark the thread as spam, and delete it."""
|
||||||
|
data = {"id": self.id}
|
||||||
|
j = self.session._payload_post("/ajax/mercury/mark_spam.php?dpr=1", data)
|
||||||
|
|
||||||
def _forced_fetch(self, message_id: str) -> dict:
|
def _forced_fetch(self, message_id: str) -> dict:
|
||||||
params = {
|
params = {
|
||||||
"thread_and_message_id": {"thread_id": self.id, "message_id": message_id}
|
"thread_and_message_id": {"thread_id": self.id, "message_id": message_id}
|
||||||
|
Reference in New Issue
Block a user