Split Message into Message/MessageData
This commit is contained in:
@@ -60,7 +60,7 @@ thread.set_color(fbchat.ThreadColor.MESSENGER_BLUE)
|
|||||||
# Will change the thread emoji to `👍`
|
# Will change the thread emoji to `👍`
|
||||||
thread.set_emoji("👍")
|
thread.set_emoji("👍")
|
||||||
|
|
||||||
# message = fbchat.Message(session=session, id="<message id>")
|
message = fbchat.Message(session=session, id="<message id>")
|
||||||
#
|
|
||||||
# # Will react to a message with a 😍 emoji
|
# Will react to a message with a 😍 emoji
|
||||||
# message.react(fbchat.MessageReaction.LOVE)
|
message.react(fbchat.MessageReaction.LOVE)
|
||||||
|
@@ -1362,18 +1362,15 @@ class Client:
|
|||||||
|
|
||||||
elif d.get("deltaMessageReply"):
|
elif d.get("deltaMessageReply"):
|
||||||
i = d["deltaMessageReply"]
|
i = d["deltaMessageReply"]
|
||||||
|
thread = get_thread(metadata)
|
||||||
metadata = i["message"]["messageMetadata"]
|
metadata = i["message"]["messageMetadata"]
|
||||||
replied_to = Message._from_reply(
|
replied_to = MessageData._from_reply(thread, i["repliedToMessage"])
|
||||||
self.session, i["repliedToMessage"]
|
message = MessageData._from_reply(thread, i["message"], replied_to)
|
||||||
)
|
|
||||||
message = Message._from_reply(
|
|
||||||
self.session, i["message"], replied_to
|
|
||||||
)
|
|
||||||
self.on_message(
|
self.on_message(
|
||||||
mid=message.id,
|
mid=message.id,
|
||||||
author_id=message.author,
|
author_id=message.author,
|
||||||
message_object=message,
|
message_object=message,
|
||||||
thread=get_thread(metadata),
|
thread=thread,
|
||||||
at=message.created_at,
|
at=message.created_at,
|
||||||
metadata=metadata,
|
metadata=metadata,
|
||||||
msg=m,
|
msg=m,
|
||||||
@@ -1381,18 +1378,19 @@ class Client:
|
|||||||
|
|
||||||
# New message
|
# New message
|
||||||
elif delta.get("class") == "NewMessage":
|
elif delta.get("class") == "NewMessage":
|
||||||
|
thread = get_thread(metadata)
|
||||||
self.on_message(
|
self.on_message(
|
||||||
mid=mid,
|
mid=mid,
|
||||||
author_id=author_id,
|
author_id=author_id,
|
||||||
message_object=Message._from_pull(
|
message_object=MessageData._from_pull(
|
||||||
self.session,
|
thread,
|
||||||
delta,
|
delta,
|
||||||
mid=mid,
|
mid=mid,
|
||||||
tags=metadata.get("tags"),
|
tags=metadata.get("tags"),
|
||||||
author=author_id,
|
author=author_id,
|
||||||
created_at=at,
|
created_at=at,
|
||||||
),
|
),
|
||||||
thread=get_thread(metadata),
|
thread=thread,
|
||||||
at=at,
|
at=at,
|
||||||
metadata=metadata,
|
metadata=metadata,
|
||||||
msg=m,
|
msg=m,
|
||||||
|
@@ -79,42 +79,15 @@ class Mention:
|
|||||||
class Message:
|
class Message:
|
||||||
"""Represents a Facebook message."""
|
"""Represents a Facebook message."""
|
||||||
|
|
||||||
# TODO: Make these fields required!
|
#: The thread that this message belongs to.
|
||||||
#: The session to use when making requests.
|
thread = attr.ib(type="_thread.ThreadABC")
|
||||||
session = attr.ib(None, type=_session.Session)
|
#: The message ID.
|
||||||
#: The message ID
|
id = attr.ib(converter=str)
|
||||||
id = attr.ib(None, converter=str)
|
|
||||||
|
|
||||||
#: The actual message
|
@property
|
||||||
text = attr.ib(None)
|
def session(self):
|
||||||
#: A list of `Mention` objects
|
"""The session to use when making requests."""
|
||||||
mentions = attr.ib(factory=list)
|
return self.thread.session
|
||||||
#: A `EmojiSize`. Size of a sent emoji
|
|
||||||
emoji_size = attr.ib(None)
|
|
||||||
#: ID of the sender
|
|
||||||
author = attr.ib(None)
|
|
||||||
#: Datetime of when the message was sent
|
|
||||||
created_at = attr.ib(None)
|
|
||||||
#: Whether the message is read
|
|
||||||
is_read = attr.ib(None)
|
|
||||||
#: A list of people IDs who read the message, works only with `Client.fetch_thread_messages`
|
|
||||||
read_by = attr.ib(factory=list)
|
|
||||||
#: A dictionary with user's IDs as keys, and their `MessageReaction` as values
|
|
||||||
reactions = attr.ib(factory=dict)
|
|
||||||
#: A `Sticker`
|
|
||||||
sticker = attr.ib(None)
|
|
||||||
#: A list of attachments
|
|
||||||
attachments = attr.ib(factory=list)
|
|
||||||
#: A list of `QuickReply`
|
|
||||||
quick_replies = attr.ib(factory=list)
|
|
||||||
#: Whether the message is unsent (deleted for everyone)
|
|
||||||
unsent = attr.ib(False)
|
|
||||||
#: Message ID you want to reply to
|
|
||||||
reply_to_id = attr.ib(None)
|
|
||||||
#: Replied message
|
|
||||||
replied_to = attr.ib(None)
|
|
||||||
#: Whether the message was forwarded
|
|
||||||
forwarded = attr.ib(False)
|
|
||||||
|
|
||||||
def unsend(self):
|
def unsend(self):
|
||||||
"""Unsend the message (removes it for everyone)."""
|
"""Unsend the message (removes it for everyone)."""
|
||||||
@@ -125,7 +98,7 @@ class Message:
|
|||||||
"""React to the message, or removes reaction.
|
"""React to the message, or removes reaction.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
reaction: Reaction emoji to use, if None removes reaction
|
reaction: Reaction emoji to use, if ``None`` removes reaction
|
||||||
"""
|
"""
|
||||||
data = {
|
data = {
|
||||||
"action": "ADD_REACTION" if reaction else "REMOVE_REACTION",
|
"action": "ADD_REACTION" if reaction else "REMOVE_REACTION",
|
||||||
@@ -138,27 +111,22 @@ class Message:
|
|||||||
j = self.session._payload_post("/webgraphql/mutation", data)
|
j = self.session._payload_post("/webgraphql/mutation", data)
|
||||||
_util.handle_graphql_errors(j)
|
_util.handle_graphql_errors(j)
|
||||||
|
|
||||||
@classmethod
|
def fetch(self) -> "MessageData":
|
||||||
def from_fetch(cls, thread, message_id: str) -> "Message":
|
"""Fetch fresh `MessageData` object."""
|
||||||
"""Fetch `Message` object from the given message id.
|
message_info = self.thread._forced_fetch(self.id).get("message")
|
||||||
|
return MessageData._from_graphql(self.thread, message_info)
|
||||||
|
|
||||||
Args:
|
@staticmethod
|
||||||
message_id: Message ID to fetch from
|
def format_mentions(text, *args, **kwargs):
|
||||||
"""
|
|
||||||
message_info = thread._forced_fetch(message_id).get("message")
|
|
||||||
return Message._from_graphql(thread.session, message_info)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def format_mentions(cls, text, *args, **kwargs):
|
|
||||||
"""Like `str.format`, but takes tuples with a thread id and text instead.
|
"""Like `str.format`, but takes tuples with a thread id and text instead.
|
||||||
|
|
||||||
Return a `Message` object, with the formatted string and relevant mentions.
|
Return a tuple, with the formatted string and relevant mentions.
|
||||||
|
|
||||||
>>> Message.format_mentions("Hey {!r}! My name is {}", ("1234", "Peter"), ("4321", "Michael"))
|
>>> Message.format_mentions("Hey {!r}! My name is {}", ("1234", "Peter"), ("4321", "Michael"))
|
||||||
<Message (None): "Hey 'Peter'! My name is Michael", mentions=[<Mention 1234: offset=4 length=7>, <Mention 4321: offset=24 length=7>] emoji_size=None attachments=[]>
|
("Hey 'Peter'! My name is Michael", [<Mention 1234: offset=4 length=7>, <Mention 4321: offset=24 length=7>])
|
||||||
|
|
||||||
>>> Message.format_mentions("Hey {p}! My name is {}", ("1234", "Michael"), p=("4321", "Peter"))
|
>>> Message.format_mentions("Hey {p}! My name is {}", ("1234", "Michael"), p=("4321", "Peter"))
|
||||||
<Message (None): 'Hey Peter! My name is Michael', mentions=[<Mention 4321: offset=4 length=5>, <Mention 1234: offset=22 length=7>] emoji_size=None attachments=[]>
|
('Hey Peter! My name is Michael', [<Mention 4321: offset=4 length=5>, <Mention 1234: offset=22 length=7>])
|
||||||
"""
|
"""
|
||||||
result = ""
|
result = ""
|
||||||
mentions = list()
|
mentions = list()
|
||||||
@@ -196,7 +164,46 @@ class Message:
|
|||||||
)
|
)
|
||||||
offset += len(name)
|
offset += len(name)
|
||||||
|
|
||||||
return cls(text=result, mentions=mentions)
|
return result, mentions
|
||||||
|
|
||||||
|
|
||||||
|
@attrs_default
|
||||||
|
class MessageData(Message):
|
||||||
|
"""Represents data in a Facebook message.
|
||||||
|
|
||||||
|
Inherits `Message`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
#: The actual message
|
||||||
|
text = attr.ib(None)
|
||||||
|
#: A list of `Mention` objects
|
||||||
|
mentions = attr.ib(factory=list)
|
||||||
|
#: A `EmojiSize`. Size of a sent emoji
|
||||||
|
emoji_size = attr.ib(None)
|
||||||
|
#: ID of the sender
|
||||||
|
author = attr.ib(None)
|
||||||
|
#: Datetime of when the message was sent
|
||||||
|
created_at = attr.ib(None)
|
||||||
|
#: Whether the message is read
|
||||||
|
is_read = attr.ib(None)
|
||||||
|
#: A list of people IDs who read the message, works only with `Client.fetch_thread_messages`
|
||||||
|
read_by = attr.ib(factory=list)
|
||||||
|
#: A dictionary with user's IDs as keys, and their `MessageReaction` as values
|
||||||
|
reactions = attr.ib(factory=dict)
|
||||||
|
#: A `Sticker`
|
||||||
|
sticker = attr.ib(None)
|
||||||
|
#: A list of attachments
|
||||||
|
attachments = attr.ib(factory=list)
|
||||||
|
#: A list of `QuickReply`
|
||||||
|
quick_replies = attr.ib(factory=list)
|
||||||
|
#: Whether the message is unsent (deleted for everyone)
|
||||||
|
unsent = attr.ib(False)
|
||||||
|
#: Message ID you want to reply to
|
||||||
|
reply_to_id = attr.ib(None)
|
||||||
|
#: Replied message
|
||||||
|
replied_to = attr.ib(None)
|
||||||
|
#: Whether the message was forwarded
|
||||||
|
forwarded = attr.ib(False)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_forwarded_from_tags(tags):
|
def _get_forwarded_from_tags(tags):
|
||||||
@@ -215,7 +222,7 @@ class Message:
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _from_graphql(cls, session, data, read_receipts=None):
|
def _from_graphql(cls, thread, data, read_receipts=None):
|
||||||
if data.get("message_sender") is None:
|
if data.get("message_sender") is None:
|
||||||
data["message_sender"] = {}
|
data["message_sender"] = {}
|
||||||
if data.get("message") is None:
|
if data.get("message") is None:
|
||||||
@@ -241,7 +248,7 @@ class Message:
|
|||||||
replied_to = cls._from_graphql(data["replied_to_message"]["message"])
|
replied_to = cls._from_graphql(data["replied_to_message"]["message"])
|
||||||
|
|
||||||
return cls(
|
return cls(
|
||||||
session=session,
|
thread=thread,
|
||||||
id=str(data["message_id"]),
|
id=str(data["message_id"]),
|
||||||
text=data["message"].get("text"),
|
text=data["message"].get("text"),
|
||||||
mentions=[
|
mentions=[
|
||||||
@@ -270,7 +277,7 @@ class Message:
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _from_reply(cls, session, data, replied_to=None):
|
def _from_reply(cls, thread, data, replied_to=None):
|
||||||
tags = data["messageMetadata"].get("tags")
|
tags = data["messageMetadata"].get("tags")
|
||||||
metadata = data.get("messageMetadata", {})
|
metadata = data.get("messageMetadata", {})
|
||||||
|
|
||||||
@@ -297,7 +304,7 @@ class Message:
|
|||||||
)
|
)
|
||||||
|
|
||||||
return cls(
|
return cls(
|
||||||
session=session,
|
thread=thread,
|
||||||
id=metadata.get("messageId"),
|
id=metadata.get("messageId"),
|
||||||
text=data.get("body"),
|
text=data.get("body"),
|
||||||
mentions=[
|
mentions=[
|
||||||
@@ -317,9 +324,7 @@ class Message:
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _from_pull(
|
def _from_pull(cls, thread, data, mid, tags, author, created_at):
|
||||||
cls, session, data, mid=None, tags=None, author=None, created_at=None
|
|
||||||
):
|
|
||||||
mentions = []
|
mentions = []
|
||||||
if data.get("data") and data["data"].get("prng"):
|
if data.get("data") and data["data"].get("prng"):
|
||||||
try:
|
try:
|
||||||
@@ -366,7 +371,7 @@ class Message:
|
|||||||
)
|
)
|
||||||
|
|
||||||
return cls(
|
return cls(
|
||||||
session=session,
|
thread=thread,
|
||||||
id=mid,
|
id=mid,
|
||||||
text=data.get("body"),
|
text=data.get("body"),
|
||||||
mentions=mentions,
|
mentions=mentions,
|
||||||
|
@@ -318,8 +318,11 @@ class ThreadABC(metaclass=abc.ABCMeta):
|
|||||||
|
|
||||||
read_receipts = j["message_thread"]["read_receipts"]["nodes"]
|
read_receipts = j["message_thread"]["read_receipts"]["nodes"]
|
||||||
|
|
||||||
|
# TODO: May or may not be a good idea to attach the current thread?
|
||||||
|
# For now, we just create a new thread:
|
||||||
|
thread = self.__class__(session=self.session, id=self.id)
|
||||||
messages = [
|
messages = [
|
||||||
_message.Message._from_graphql(self.session, message, read_receipts)
|
_message.MessageData._from_graphql(thread, message, read_receipts)
|
||||||
for message in j["message_thread"]["messages"]["nodes"]
|
for message in j["message_thread"]["messages"]["nodes"]
|
||||||
]
|
]
|
||||||
messages.reverse()
|
messages.reverse()
|
||||||
|
@@ -4,6 +4,7 @@ from fbchat._message import (
|
|||||||
EmojiSize,
|
EmojiSize,
|
||||||
Mention,
|
Mention,
|
||||||
Message,
|
Message,
|
||||||
|
MessageData,
|
||||||
graphql_to_extensible_attachment,
|
graphql_to_extensible_attachment,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -62,9 +63,9 @@ def test_mention_to_send_data():
|
|||||||
|
|
||||||
|
|
||||||
def test_message_format_mentions():
|
def test_message_format_mentions():
|
||||||
expected = Message(
|
expected = (
|
||||||
text="Hey 'Peter'! My name is Michael",
|
"Hey 'Peter'! My name is Michael",
|
||||||
mentions=[
|
[
|
||||||
Mention(thread_id="1234", offset=4, length=7),
|
Mention(thread_id="1234", offset=4, length=7),
|
||||||
Mention(thread_id="4321", offset=24, length=7),
|
Mention(thread_id="4321", offset=24, length=7),
|
||||||
],
|
],
|
||||||
@@ -78,9 +79,9 @@ def test_message_format_mentions():
|
|||||||
|
|
||||||
|
|
||||||
def test_message_get_forwarded_from_tags():
|
def test_message_get_forwarded_from_tags():
|
||||||
assert not Message._get_forwarded_from_tags(None)
|
assert not MessageData._get_forwarded_from_tags(None)
|
||||||
assert not Message._get_forwarded_from_tags(["hot_emoji_size:unknown"])
|
assert not MessageData._get_forwarded_from_tags(["hot_emoji_size:unknown"])
|
||||||
assert Message._get_forwarded_from_tags(
|
assert MessageData._get_forwarded_from_tags(
|
||||||
["attachment:photo", "inbox", "sent", "source:chat:forward", "tq"]
|
["attachment:photo", "inbox", "sent", "source:chat:forward", "tq"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user