Update client.py
This commit is contained in:
160
fbchat/client.py
160
fbchat/client.py
@@ -11,6 +11,7 @@ from .utils import *
|
|||||||
from .models import *
|
from .models import *
|
||||||
from .graphql import *
|
from .graphql import *
|
||||||
import time
|
import time
|
||||||
|
import ast
|
||||||
try:
|
try:
|
||||||
from urllib.parse import urlparse, parse_qs
|
from urllib.parse import urlparse, parse_qs
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@@ -185,6 +186,25 @@ class Client(object):
|
|||||||
"""
|
"""
|
||||||
return self.graphql_requests(query)[0]
|
return self.graphql_requests(query)[0]
|
||||||
|
|
||||||
|
def _forcedFetch(self, thread_id, mid):
|
||||||
|
full_data = {
|
||||||
|
"av": self.uid,
|
||||||
|
"batch_name": "MessengerMessageDFFFetcher",
|
||||||
|
"queries": """{
|
||||||
|
"o0": {
|
||||||
|
"doc_id": "1768656253222505",
|
||||||
|
"query_params": {
|
||||||
|
"thread_and_message_id": {
|
||||||
|
"thread_id": "%s",
|
||||||
|
"message_id": "%s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}""" % (thread_id, mid)
|
||||||
|
}
|
||||||
|
r = self._post(self.req_url.GRAPHQL, full_data, fix_request=False, as_json=True)
|
||||||
|
return ast.literal_eval(get_decoded_r(r).replace("\n","").replace("\r","").replace(" ","").replace("}{", ",").replace(":null", ":None").replace("true", "True").replace("false", "False"))
|
||||||
|
|
||||||
"""
|
"""
|
||||||
END INTERNAL REQUEST METHODS
|
END INTERNAL REQUEST METHODS
|
||||||
"""
|
"""
|
||||||
@@ -1066,7 +1086,7 @@ class Client(object):
|
|||||||
data['action_type'] = 'ma-type:log-message'
|
data['action_type'] = 'ma-type:log-message'
|
||||||
data['log_message_type'] = 'log:subscribe'
|
data['log_message_type'] = 'log:subscribe'
|
||||||
|
|
||||||
if type(user_ids) is not list:
|
if not isinstance(user_ids, list):
|
||||||
user_ids = [user_ids]
|
user_ids = [user_ids]
|
||||||
|
|
||||||
# Make list of users unique
|
# Make list of users unique
|
||||||
@@ -1098,6 +1118,25 @@ class Client(object):
|
|||||||
|
|
||||||
j = self._post(self.req_url.REMOVE_USER, data, fix_request=True, as_json=True)
|
j = self._post(self.req_url.REMOVE_USER, data, fix_request=True, as_json=True)
|
||||||
|
|
||||||
|
def _adminStatus(self, admin_ids, admin, thread_id=None):
|
||||||
|
thread_id, thread_type = self._getThread(thread_id, None)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"add": str(admin).lower(),
|
||||||
|
"thread_fbid": thread_id
|
||||||
|
}
|
||||||
|
|
||||||
|
if not isinstance(admin_ids, list):
|
||||||
|
admin_ids = [admin_ids]
|
||||||
|
|
||||||
|
# Make list of admins unique
|
||||||
|
admin_ids = set(admin_ids)
|
||||||
|
|
||||||
|
for i, admin_id in enumerate(admin_ids):
|
||||||
|
data['admin_ids[' + str(i) + ']'] = str(admin_id)
|
||||||
|
|
||||||
|
j = self._post(self.req_url.SAVE_ADMINS, data, fix_request=True, as_json=True)
|
||||||
|
|
||||||
def addGroupAdmins(self, admin_ids, thread_id=None):
|
def addGroupAdmins(self, admin_ids, thread_id=None):
|
||||||
"""
|
"""
|
||||||
Sets specifed user as a group admin.
|
Sets specifed user as a group admin.
|
||||||
@@ -1106,49 +1145,17 @@ class Client(object):
|
|||||||
:param thread_id: Group ID to remove people from. See :ref:`intro_threads`
|
:param thread_id: Group ID to remove people from. See :ref:`intro_threads`
|
||||||
:raises: FBchatException if request failed
|
:raises: FBchatException if request failed
|
||||||
"""
|
"""
|
||||||
thread_id, thread_type = self._getThread(thread_id, None)
|
self._adminStatus(admin_ids, True, thread_id)
|
||||||
|
|
||||||
data = {
|
|
||||||
"add": "true",
|
|
||||||
"thread_fbid": thread_id
|
|
||||||
}
|
|
||||||
|
|
||||||
if type(admin_ids) is not list:
|
|
||||||
admin_ids = [admin_ids]
|
|
||||||
|
|
||||||
# Make list of admins unique
|
|
||||||
admin_ids = set(admin_ids)
|
|
||||||
|
|
||||||
for i, admin_id in enumerate(admin_ids):
|
|
||||||
data['admin_ids[' + str(i) + ']'] = str(admin_id)
|
|
||||||
|
|
||||||
j = self._post(self.req_url.SAVE_ADMINS, data, fix_request=True, as_json=True)
|
|
||||||
|
|
||||||
def removeGroupAdmins(self, admin_ids, thread_id=None):
|
def removeGroupAdmins(self, admin_ids, thread_id=None):
|
||||||
"""
|
"""
|
||||||
Removes group admin from specifed user.
|
Removes admin status from specifed user.
|
||||||
|
|
||||||
:param admin_ids: One or more user IDs to remove admin
|
:param admin_ids: One or more user IDs to remove admin
|
||||||
:param thread_id: Group ID to remove people from. See :ref:`intro_threads`
|
:param thread_id: Group ID to remove people from. See :ref:`intro_threads`
|
||||||
:raises: FBchatException if request failed
|
:raises: FBchatException if request failed
|
||||||
"""
|
"""
|
||||||
thread_id, thread_type = self._getThread(thread_id, None)
|
self._adminStatus(admin_ids, False, thread_id)
|
||||||
|
|
||||||
data = {
|
|
||||||
"add": "false",
|
|
||||||
"thread_fbid": thread_id
|
|
||||||
}
|
|
||||||
|
|
||||||
if type(admin_ids) is not list:
|
|
||||||
admin_ids = [admin_ids]
|
|
||||||
|
|
||||||
# Make list of admins unique
|
|
||||||
admin_ids = set(admin_ids)
|
|
||||||
|
|
||||||
for i, admin_id in enumerate(admin_ids):
|
|
||||||
data['admin_ids[' + str(i) + ']'] = str(admin_id)
|
|
||||||
|
|
||||||
j = self._post(self.req_url.SAVE_ADMINS, data, fix_request=True, as_json=True)
|
|
||||||
|
|
||||||
def changeGroupApprovalMode(self, approval_mode, thread_id=None):
|
def changeGroupApprovalMode(self, approval_mode, thread_id=None):
|
||||||
"""
|
"""
|
||||||
@@ -1430,7 +1437,7 @@ class Client(object):
|
|||||||
r = self._post(self.req_url.DELIVERED, data)
|
r = self._post(self.req_url.DELIVERED, data)
|
||||||
return r.ok
|
return r.ok
|
||||||
|
|
||||||
def markAsRead(self, thread_id):
|
def markAsRead(self, thread_id=None):
|
||||||
"""
|
"""
|
||||||
Mark a thread as read
|
Mark a thread as read
|
||||||
All messages inside the thread will be marked as read
|
All messages inside the thread will be marked as read
|
||||||
@@ -1439,6 +1446,7 @@ class Client(object):
|
|||||||
:return: Whether the request was successful
|
:return: Whether the request was successful
|
||||||
:raises: FBchatException if request failed
|
:raises: FBchatException if request failed
|
||||||
"""
|
"""
|
||||||
|
thread_id, thread_type = self._getThread(thread_id, None)
|
||||||
data = {
|
data = {
|
||||||
"ids[%s]" % thread_id: 'true',
|
"ids[%s]" % thread_id: 'true',
|
||||||
"watermarkTimestamp": now(),
|
"watermarkTimestamp": now(),
|
||||||
@@ -1517,7 +1525,7 @@ class Client(object):
|
|||||||
r = self._post(self.req_url.UNBLOCK_USER, data)
|
r = self._post(self.req_url.UNBLOCK_USER, data)
|
||||||
return r.ok
|
return r.ok
|
||||||
|
|
||||||
def moveThread(self, location, thread_id=None):
|
def moveThreads(self, location, thread_ids=None):
|
||||||
"""
|
"""
|
||||||
Moves the thread to specifed location
|
Moves the thread to specifed location
|
||||||
|
|
||||||
@@ -1528,6 +1536,12 @@ class Client(object):
|
|||||||
:return: Whether the request was successful
|
:return: Whether the request was successful
|
||||||
:raises: FBchatException if request failed
|
:raises: FBchatException if request failed
|
||||||
"""
|
"""
|
||||||
|
if not isinstance(thread_ids, list):
|
||||||
|
thread_ids = [thread_ids]
|
||||||
|
|
||||||
|
# Make list of admins unique
|
||||||
|
thread_ids = set(thread_ids)
|
||||||
|
|
||||||
if location == ThreadLocation.PENDING:
|
if location == ThreadLocation.PENDING:
|
||||||
location = ThreadLocation.OTHER
|
location = ThreadLocation.OTHER
|
||||||
if location in ThreadLocation:
|
if location in ThreadLocation:
|
||||||
@@ -1535,19 +1549,18 @@ class Client(object):
|
|||||||
else:
|
else:
|
||||||
raise FBchatUserError('"location" must be a value of ThreadLocation')
|
raise FBchatUserError('"location" must be a value of ThreadLocation')
|
||||||
if location == ThreadLocation.ARCHIVED:
|
if location == ThreadLocation.ARCHIVED:
|
||||||
data_archive = {
|
data_archive = dict()
|
||||||
"ids[{}]".format(thread_id): 'true'
|
data_unpin = dict()
|
||||||
}
|
for thread_id in thread_ids:
|
||||||
|
data_archive["ids[{}]".format(thread_id)] = 'true'
|
||||||
|
data_unpin["ids[{}]".format(thread_id)] = 'false'
|
||||||
r_archive = self._post(self.req_url.ARCHIVED_STATUS, data_archive)
|
r_archive = self._post(self.req_url.ARCHIVED_STATUS, data_archive)
|
||||||
data_unpin = {
|
|
||||||
"ids[{}]".format(thread_id): 'false'
|
|
||||||
}
|
|
||||||
r_unpin = self._post(self.req_url.PINNED_STATUS, data_unpin)
|
r_unpin = self._post(self.req_url.PINNED_STATUS, data_unpin)
|
||||||
return r_archive.ok and r_unpin.ok
|
return r_archive.ok and r_unpin.ok
|
||||||
else:
|
else:
|
||||||
data = {
|
data = dict()
|
||||||
"{}[0]".format(loc_str): thread_id
|
for i, thread_id in enumerate(thread_ids):
|
||||||
}
|
data["{}[{}]".format(loc_str, i)] = thread_id
|
||||||
r = self._post(self.req_url.MOVE_THREAD, data)
|
r = self._post(self.req_url.MOVE_THREAD, data)
|
||||||
return r.ok
|
return r.ok
|
||||||
|
|
||||||
@@ -1667,11 +1680,18 @@ class Client(object):
|
|||||||
self.onTitleChange(mid=mid, author_id=author_id, new_title=new_title, thread_id=thread_id,
|
self.onTitleChange(mid=mid, author_id=author_id, new_title=new_title, thread_id=thread_id,
|
||||||
thread_type=thread_type, ts=ts, metadata=metadata, msg=m)
|
thread_type=thread_type, ts=ts, metadata=metadata, msg=m)
|
||||||
|
|
||||||
# Thread image change
|
# Forced fetch
|
||||||
elif delta.get("class") == "ForcedFetch":
|
elif delta.get("class") == "ForcedFetch":
|
||||||
mid = delta.get("messageId")
|
mid = delta.get("messageId")
|
||||||
thread_id = str(delta['threadKey']['threadFbId'])
|
thread_id = str(delta['threadKey']['threadFbId'])
|
||||||
self.onImageChange(mid=mid, thread_id=thread_id)
|
fetch_info = self._forcedFetch(thread_id, mid)
|
||||||
|
fetch_data = fetch_info["o0"]["data"]["message"]
|
||||||
|
author_id = fetch_data["message_sender"]["id"]
|
||||||
|
ts = fetch_data["timestamp_precise"]
|
||||||
|
if fetch_data.get("__typename") == "ThreadImageMessage":
|
||||||
|
# Thread image change
|
||||||
|
image_id = fetch_data["image_with_metadata"]["legacy_attachment_id"]
|
||||||
|
self.onImageChange(mid=mid, image_id=image_id, author_id=author_id, thread_id=thread_id, ts=ts)
|
||||||
|
|
||||||
# Nickname change
|
# Nickname change
|
||||||
elif delta_type == "change_thread_nickname":
|
elif delta_type == "change_thread_nickname":
|
||||||
@@ -1732,6 +1752,21 @@ class Client(object):
|
|||||||
|
|
||||||
# thread_id, thread_type = getThreadIdAndThreadType(delta)
|
# thread_id, thread_type = getThreadIdAndThreadType(delta)
|
||||||
self.onMarkedSeen(threads=threads, seen_ts=seen_ts, ts=delivered_ts, metadata=delta, msg=m)
|
self.onMarkedSeen(threads=threads, seen_ts=seen_ts, ts=delivered_ts, metadata=delta, msg=m)
|
||||||
|
|
||||||
|
# Game played
|
||||||
|
elif delta.get("type") == "instant_game_update":
|
||||||
|
game_id = delta["untypedData"]["game_id"]
|
||||||
|
game_name = delta["untypedData"]["game_name"]
|
||||||
|
score = delta["untypedData"].get("score")
|
||||||
|
if score is not None:
|
||||||
|
score = int(score)
|
||||||
|
leaderboard = delta["untypedData"].get("leaderboard")
|
||||||
|
if leaderboard is not None:
|
||||||
|
leaderboard = ast.literal_eval(leaderboard.replace(":null",":None"))["scores"]
|
||||||
|
thread_id, thread_type = getThreadIdAndThreadType(metadata)
|
||||||
|
self.onGamePlayed(mid=mid, author_id=author_id, game_id=game_id, game_name=game_name,
|
||||||
|
score=score, leaderboard=leaderboard, thread_id=thread_id,
|
||||||
|
thread_type=thread_type, ts=ts, metadata=metadata, msg=m)
|
||||||
|
|
||||||
# New message
|
# New message
|
||||||
elif delta.get("class") == "NewMessage":
|
elif delta.get("class") == "NewMessage":
|
||||||
@@ -2016,16 +2051,17 @@ class Client(object):
|
|||||||
log.info("Title change from {} in {} ({}): {}".format(author_id, thread_id, thread_type.name, new_title))
|
log.info("Title change from {} in {} ({}): {}".format(author_id, thread_id, thread_type.name, new_title))
|
||||||
|
|
||||||
|
|
||||||
def onImageChange(self, mid=None, thread_id=None):
|
def onImageChange(self, mid=None, image_id=None, author_id=None, thread_id=None, ts=None):
|
||||||
"""
|
"""
|
||||||
.. todo::
|
|
||||||
Add author_id and image_id
|
|
||||||
Called when the client is listening, and somebody changes the image of a thread
|
Called when the client is listening, and somebody changes the image of a thread
|
||||||
|
|
||||||
:param mid: The action ID
|
:param mid: The action ID
|
||||||
|
:param image_id: The ID of the new image
|
||||||
|
:param author_id: The ID of the person who changed the image
|
||||||
:param thread_id: Thread ID that the action was sent to. See :ref:`intro_threads`
|
:param thread_id: Thread ID that the action was sent to. See :ref:`intro_threads`
|
||||||
|
:param ts: A timestamp of the action
|
||||||
"""
|
"""
|
||||||
log.info("Image change in {}".format(author_id, thread_id))
|
log.info("{} changed thread image in {}".format(author_id, thread_id))
|
||||||
|
|
||||||
|
|
||||||
def onNicknameChange(self, mid=None, author_id=None, changed_for=None, new_nickname=None, thread_id=None, thread_type=ThreadType.USER, ts=None, metadata=None, msg=None):
|
def onNicknameChange(self, mid=None, author_id=None, changed_for=None, new_nickname=None, thread_id=None, thread_type=ThreadType.USER, ts=None, metadata=None, msg=None):
|
||||||
@@ -2195,6 +2231,24 @@ class Client(object):
|
|||||||
:type thread_type: models.ThreadType
|
:type thread_type: models.ThreadType
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def onGamePlayed(self, mid=None, author_id=None, game_id=None, game_name=None, score=None, leaderboard=None, thread_id=None, thread_type=None, ts=None, metadata=None, msg=None):
|
||||||
|
"""
|
||||||
|
Called when the client is listening, and somebody plays a game
|
||||||
|
|
||||||
|
:param mid: The action ID
|
||||||
|
:param author_id: The ID of the person who played the game
|
||||||
|
:param game_id: The ID of the game
|
||||||
|
:param game_name: Name of the game
|
||||||
|
:param score: Score obtained in the game
|
||||||
|
:param leaderboard: Actual leaderboard of the game in the thread
|
||||||
|
:param thread_type: Type of thread that the action was sent to. See :ref:`intro_threads`
|
||||||
|
:param ts: A timestamp of the action
|
||||||
|
:param metadata: Extra metadata about the action
|
||||||
|
:param msg: A full set of the data recieved
|
||||||
|
:type thread_type: models.ThreadType
|
||||||
|
"""
|
||||||
|
log.info("{} played \"{}\" in {} ({})".format(author_id, game_name, thread_id, thread_type.name))
|
||||||
|
|
||||||
def onQprimer(self, ts=None, msg=None):
|
def onQprimer(self, ts=None, msg=None):
|
||||||
"""
|
"""
|
||||||
|
Reference in New Issue
Block a user