Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
0761116335 | ||
|
86d7220126 | ||
|
e175ec791c | ||
|
e54be7583a | ||
|
fdf64597ec | ||
|
064707ac23 | ||
|
b9b4d57b25 | ||
|
b4618739f3 | ||
|
22c6c82c0e | ||
|
19c875c18a | ||
|
12bbc0058c |
@@ -1,5 +1,5 @@
|
|||||||
[bumpversion]
|
[bumpversion]
|
||||||
current_version = 1.9.4
|
current_version = 1.9.7
|
||||||
commit = True
|
commit = True
|
||||||
tag = True
|
tag = True
|
||||||
|
|
||||||
|
@@ -67,5 +67,5 @@ print("thread's type: {}".format(thread.type))
|
|||||||
|
|
||||||
# Print image url for 20 last images from thread.
|
# Print image url for 20 last images from thread.
|
||||||
images = client.fetchThreadImages("<thread id>")
|
images = client.fetchThreadImages("<thread id>")
|
||||||
for image in islice(image, 20):
|
for image in islice(images, 20):
|
||||||
print(image.large_preview_url)
|
print(image.large_preview_url)
|
||||||
|
@@ -13,7 +13,7 @@ from ._client import Client
|
|||||||
from ._util import log # TODO: Remove this (from examples too)
|
from ._util import log # TODO: Remove this (from examples too)
|
||||||
|
|
||||||
__title__ = "fbchat"
|
__title__ = "fbchat"
|
||||||
__version__ = "1.9.4"
|
__version__ = "1.9.7"
|
||||||
__description__ = "Facebook Chat (Messenger) for Python"
|
__description__ = "Facebook Chat (Messenger) for Python"
|
||||||
|
|
||||||
__copyright__ = "Copyright 2015 - 2019 by Taehoon Kim"
|
__copyright__ = "Copyright 2015 - 2019 by Taehoon Kim"
|
||||||
|
@@ -2271,7 +2271,17 @@ class Client(object):
|
|||||||
elif delta_class == "ForcedFetch":
|
elif delta_class == "ForcedFetch":
|
||||||
mid = delta.get("messageId")
|
mid = delta.get("messageId")
|
||||||
if mid is None:
|
if mid is None:
|
||||||
self.onUnknownMesssageType(msg=delta)
|
if delta["threadKey"] is not None:
|
||||||
|
# Looks like the whole delta is metadata in this case
|
||||||
|
thread_id, thread_type = getThreadIdAndThreadType(delta)
|
||||||
|
self.onPendingMessage(
|
||||||
|
thread_id=thread_id,
|
||||||
|
thread_type=thread_type,
|
||||||
|
metadata=delta,
|
||||||
|
msg=delta,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.onUnknownMesssageType(msg=delta)
|
||||||
else:
|
else:
|
||||||
thread_id = str(delta["threadKey"]["threadFbId"])
|
thread_id = str(delta["threadKey"]["threadFbId"])
|
||||||
fetch_info = self._forcedFetch(thread_id, mid)
|
fetch_info = self._forcedFetch(thread_id, mid)
|
||||||
@@ -2727,6 +2737,14 @@ class Client(object):
|
|||||||
msg=delta,
|
msg=delta,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# New pending message
|
||||||
|
elif delta_class == "ThreadFolder" and delta.get("folder") == "FOLDER_PENDING":
|
||||||
|
# Looks like the whole delta is metadata in this case
|
||||||
|
thread_id, thread_type = getThreadIdAndThreadType(delta)
|
||||||
|
self.onPendingMessage(
|
||||||
|
thread_id=thread_id, thread_type=thread_type, metadata=delta, msg=delta
|
||||||
|
)
|
||||||
|
|
||||||
# Unknown message type
|
# Unknown message type
|
||||||
else:
|
else:
|
||||||
self.onUnknownMesssageType(msg=delta)
|
self.onUnknownMesssageType(msg=delta)
|
||||||
@@ -2949,6 +2967,21 @@ class Client(object):
|
|||||||
"""
|
"""
|
||||||
log.info("{} from {} in {}".format(message_object, thread_id, thread_type.name))
|
log.info("{} from {} in {}".format(message_object, thread_id, thread_type.name))
|
||||||
|
|
||||||
|
def onPendingMessage(
|
||||||
|
self, thread_id=None, thread_type=None, metadata=None, msg=None
|
||||||
|
):
|
||||||
|
"""Called when the client is listening, and somebody that isn't
|
||||||
|
connected with you on either Facebook or Messenger sends a message.
|
||||||
|
After that, you need to use fetchThreadList to actually read the message.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
thread_id: Thread ID that the message was sent to. See :ref:`intro_threads`
|
||||||
|
thread_type (ThreadType): Type of thread that the message was sent to. See :ref:`intro_threads`
|
||||||
|
metadata: Extra metadata about the message
|
||||||
|
msg: A full set of the data received
|
||||||
|
"""
|
||||||
|
log.info("New pending message from {}".format(thread_id))
|
||||||
|
|
||||||
def onColorChange(
|
def onColorChange(
|
||||||
self,
|
self,
|
||||||
mid=None,
|
mid=None,
|
||||||
|
@@ -79,6 +79,8 @@ class Mqtt(object):
|
|||||||
log.exception("Failed parsing MQTT data on %s as JSON", message.topic)
|
log.exception("Failed parsing MQTT data on %s as JSON", message.topic)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
log.debug("MQTT payload: %s, %s", message.topic, j)
|
||||||
|
|
||||||
if message.topic == "/t_ms":
|
if message.topic == "/t_ms":
|
||||||
# Update sync_token when received
|
# Update sync_token when received
|
||||||
# This is received in the first message after we've created a messenger
|
# This is received in the first message after we've created a messenger
|
||||||
@@ -86,18 +88,31 @@ class Mqtt(object):
|
|||||||
if "syncToken" in j and "firstDeltaSeqId" in j:
|
if "syncToken" in j and "firstDeltaSeqId" in j:
|
||||||
self._sync_token = j["syncToken"]
|
self._sync_token = j["syncToken"]
|
||||||
self._sequence_id = j["firstDeltaSeqId"]
|
self._sequence_id = j["firstDeltaSeqId"]
|
||||||
|
return
|
||||||
|
|
||||||
# Update last sequence id when received
|
# Update last sequence id when received
|
||||||
if "lastIssuedSeqId" in j:
|
if "lastIssuedSeqId" in j:
|
||||||
self._sequence_id = j["lastIssuedSeqId"]
|
self._sequence_id = j["lastIssuedSeqId"]
|
||||||
|
|
||||||
if "errorCode" in j:
|
if "errorCode" in j:
|
||||||
# Known types: ERROR_QUEUE_OVERFLOW | ERROR_QUEUE_NOT_FOUND
|
error = j["errorCode"]
|
||||||
# 'F\xfa\x84\x8c\x85\xf8\xbc-\x88 FB_PAGES_INSUFFICIENT_PERMISSION\x00'
|
# TODO: 'F\xfa\x84\x8c\x85\xf8\xbc-\x88 FB_PAGES_INSUFFICIENT_PERMISSION\x00'
|
||||||
log.error("MQTT error code %s received", j["errorCode"])
|
if error in ("ERROR_QUEUE_NOT_FOUND", "ERROR_QUEUE_OVERFLOW"):
|
||||||
# TODO: Consider resetting the sync_token and sequence ID here?
|
# ERROR_QUEUE_NOT_FOUND means that the queue was deleted, since too
|
||||||
|
# much time passed, or that it was simply missing
|
||||||
log.debug("MQTT payload: %s, %s", message.topic, j)
|
# ERROR_QUEUE_OVERFLOW means that the sequence id was too small, so
|
||||||
|
# the desired events could not be retrieved
|
||||||
|
log.error(
|
||||||
|
"The MQTT listener was disconnected for too long,"
|
||||||
|
" events may have been lost"
|
||||||
|
)
|
||||||
|
self._sync_token = None
|
||||||
|
self._sequence_id = self._fetch_sequence_id(self._state)
|
||||||
|
self._messenger_queue_publish()
|
||||||
|
# TODO: Signal to the user that they should reload their data!
|
||||||
|
return
|
||||||
|
log.error("MQTT error code %s received", error)
|
||||||
|
return
|
||||||
|
|
||||||
# Call the external callback
|
# Call the external callback
|
||||||
self._on_message(message.topic, j)
|
self._on_message(message.topic, j)
|
||||||
@@ -115,11 +130,10 @@ class Mqtt(object):
|
|||||||
log.debug("Fetching MQTT sequence ID")
|
log.debug("Fetching MQTT sequence ID")
|
||||||
# Same request as in `Client.fetchThreadList`
|
# Same request as in `Client.fetchThreadList`
|
||||||
(j,) = state._graphql_requests(_graphql.from_doc_id("1349387578499440", params))
|
(j,) = state._graphql_requests(_graphql.from_doc_id("1349387578499440", params))
|
||||||
try:
|
sequence_id = j["viewer"]["message_threads"]["sync_sequence_id"]
|
||||||
return int(j["viewer"]["message_threads"]["sync_sequence_id"])
|
if not sequence_id:
|
||||||
except (KeyError, ValueError):
|
raise _exception.FBchatNotLoggedIn("Failed fetching sequence id")
|
||||||
# TODO: Proper exceptions
|
return int(sequence_id)
|
||||||
raise
|
|
||||||
|
|
||||||
def _on_connect_handler(self, client, userdata, flags, rc):
|
def _on_connect_handler(self, client, userdata, flags, rc):
|
||||||
if rc == 21:
|
if rc == 21:
|
||||||
@@ -129,6 +143,9 @@ class Mqtt(object):
|
|||||||
if rc != 0:
|
if rc != 0:
|
||||||
return # Don't try to send publish if the connection failed
|
return # Don't try to send publish if the connection failed
|
||||||
|
|
||||||
|
self._messenger_queue_publish()
|
||||||
|
|
||||||
|
def _messenger_queue_publish(self):
|
||||||
# configure receiving messages.
|
# configure receiving messages.
|
||||||
payload = {
|
payload = {
|
||||||
"sync_api_version": 10,
|
"sync_api_version": 10,
|
||||||
@@ -171,6 +188,10 @@ class Mqtt(object):
|
|||||||
"/br_sr",
|
"/br_sr",
|
||||||
# Response to /br_sr
|
# Response to /br_sr
|
||||||
"/sr_res",
|
"/sr_res",
|
||||||
|
# Data about user-to-user calls
|
||||||
|
# TODO: Investigate the response from this! (A bunch of binary data)
|
||||||
|
# "/t_rtc",
|
||||||
|
# TODO: Find out what this does!
|
||||||
# TODO: Investigate the response from this! (A bunch of binary data)
|
# TODO: Investigate the response from this! (A bunch of binary data)
|
||||||
# "/t_p",
|
# "/t_p",
|
||||||
# TODO: Find out what this does!
|
# TODO: Find out what this does!
|
||||||
@@ -186,7 +207,6 @@ class Mqtt(object):
|
|||||||
"/messaging_events",
|
"/messaging_events",
|
||||||
"/orca_message_notifications",
|
"/orca_message_notifications",
|
||||||
"/pp",
|
"/pp",
|
||||||
"/t_rtc",
|
|
||||||
"/webrtc_response",
|
"/webrtc_response",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -266,6 +286,8 @@ class Mqtt(object):
|
|||||||
# This error is wrongly classified
|
# This error is wrongly classified
|
||||||
# See https://github.com/eclipse/paho.mqtt.python/issues/340
|
# See https://github.com/eclipse/paho.mqtt.python/issues/340
|
||||||
log.warning("Connection error, retrying")
|
log.warning("Connection error, retrying")
|
||||||
|
elif rc == paho.mqtt.client.MQTT_ERR_CONN_REFUSED:
|
||||||
|
raise _exception.FBchatNotLoggedIn("MQTT connection refused")
|
||||||
else:
|
else:
|
||||||
err = paho.mqtt.client.error_string(rc)
|
err = paho.mqtt.client.error_string(rc)
|
||||||
log.error("MQTT Error: %s", err)
|
log.error("MQTT Error: %s", err)
|
||||||
|
@@ -27,6 +27,8 @@ def find_input_fields(html):
|
|||||||
def session_factory(user_agent=None):
|
def session_factory(user_agent=None):
|
||||||
session = requests.session()
|
session = requests.session()
|
||||||
session.headers["Referer"] = "https://www.facebook.com"
|
session.headers["Referer"] = "https://www.facebook.com"
|
||||||
|
session.headers["Accept"] = "text/html"
|
||||||
|
|
||||||
# TODO: Deprecate setting the user agent manually
|
# TODO: Deprecate setting the user agent manually
|
||||||
session.headers["User-Agent"] = user_agent or random.choice(_util.USER_AGENTS)
|
session.headers["User-Agent"] = user_agent or random.choice(_util.USER_AGENTS)
|
||||||
return session
|
return session
|
||||||
|
Reference in New Issue
Block a user