Merge pull request #56 from thekindlyone/master
Fixed #55 , send fail due to api change.
This commit is contained in:
43
README.rst
43
README.rst
@@ -38,6 +38,22 @@ Sending a Message
|
|||||||
sent = client.send(friend.uid, "Your Message")
|
sent = client.send(friend.uid, "Your Message")
|
||||||
if sent:
|
if sent:
|
||||||
print("Message sent successfully!")
|
print("Message sent successfully!")
|
||||||
|
# IMAGES
|
||||||
|
client.sendLocalImage(friend.uid,message='<message text>',image='<path/to/image/file>') # send local image
|
||||||
|
imgurl = "http://i.imgur.com/LDQ2ITV.jpg"
|
||||||
|
client.sendRemoteImage(friend.uid,message='<message text>', image=imgurl) # send image from image url
|
||||||
|
|
||||||
|
|
||||||
|
Getting user info from user id
|
||||||
|
==============================
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
friend1 = client.getUsers('<friend name 1>')[0]
|
||||||
|
friend2 = client.getUsers('<friend name 2>')[0]
|
||||||
|
friend1_info = client.getUserInfo(friend1.uid) # returns dict with details
|
||||||
|
both_info = client.getUserInfo(friend1.uid,friend2.uid) # query both together, returns list of dicts
|
||||||
|
friend1_name = friend1_info['name']
|
||||||
|
|
||||||
|
|
||||||
Getting last messages sent
|
Getting last messages sent
|
||||||
@@ -52,6 +68,33 @@ Getting last messages sent
|
|||||||
print(message.body)
|
print(message.body)
|
||||||
|
|
||||||
|
|
||||||
|
Example Echobot
|
||||||
|
===============
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import fbchat
|
||||||
|
#subclass fbchat.Client and override required methods
|
||||||
|
class EchoBot(fbchat.Client):
|
||||||
|
|
||||||
|
def __init__(self,email, password, debug=True, user_agent=None):
|
||||||
|
fbchat.Client.__init__(self,email, password, debug, user_agent)
|
||||||
|
|
||||||
|
def on_message(self, mid, author_id, author_name, message, metadata):
|
||||||
|
self.markAsDelivered(author_id, mid) #mark delivered
|
||||||
|
self.markAsRead(author_id) #mark read
|
||||||
|
|
||||||
|
print("%s said: %s"%(author_id, message))
|
||||||
|
|
||||||
|
#if you are not the author, echo
|
||||||
|
if str(author_id) != str(self.uid):
|
||||||
|
self.send(author_id,message)
|
||||||
|
|
||||||
|
bot = EchoBot("<email>", "<password>")
|
||||||
|
bot.listen()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Authors
|
Authors
|
||||||
=======
|
=======
|
||||||
|
|
||||||
|
112
fbchat/client.py
112
fbchat/client.py
@@ -18,15 +18,14 @@ from random import random, choice
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from bs4 import BeautifulSoup as bs
|
from bs4 import BeautifulSoup as bs
|
||||||
from mimetypes import guess_type
|
from mimetypes import guess_type
|
||||||
|
|
||||||
from .utils import *
|
from .utils import *
|
||||||
from .models import *
|
from .models import *
|
||||||
from .stickers import *
|
from .stickers import *
|
||||||
|
import time
|
||||||
# URLs
|
# URLs
|
||||||
LoginURL ="https://m.facebook.com/login.php?login_attempt=1"
|
LoginURL ="https://m.facebook.com/login.php?login_attempt=1"
|
||||||
SearchURL ="https://www.facebook.com/ajax/typeahead/search.php"
|
SearchURL ="https://www.facebook.com/ajax/typeahead/search.php"
|
||||||
SendURL ="https://www.facebook.com/ajax/mercury/send_messages.php"
|
SendURL ="https://www.facebook.com/messaging/send/"
|
||||||
ThreadsURL ="https://www.facebook.com/ajax/mercury/threadlist_info.php"
|
ThreadsURL ="https://www.facebook.com/ajax/mercury/threadlist_info.php"
|
||||||
ThreadSyncURL="https://www.facebook.com/ajax/mercury/thread_sync.php"
|
ThreadSyncURL="https://www.facebook.com/ajax/mercury/thread_sync.php"
|
||||||
MessagesURL ="https://www.facebook.com/ajax/mercury/thread_info.php"
|
MessagesURL ="https://www.facebook.com/ajax/mercury/thread_info.php"
|
||||||
@@ -38,7 +37,7 @@ MobileURL ="https://m.facebook.com/"
|
|||||||
StickyURL ="https://0-edge-chat.facebook.com/pull"
|
StickyURL ="https://0-edge-chat.facebook.com/pull"
|
||||||
PingURL ="https://0-channel-proxy-06-ash2.facebook.com/active_ping"
|
PingURL ="https://0-channel-proxy-06-ash2.facebook.com/active_ping"
|
||||||
UploadURL ="https://upload.facebook.com/ajax/mercury/upload.php"
|
UploadURL ="https://upload.facebook.com/ajax/mercury/upload.php"
|
||||||
|
UserInfoURL ="https://www.facebook.com/chat/user_info/"
|
||||||
|
|
||||||
class Client(object):
|
class Client(object):
|
||||||
"""A client for the Facebook Chat (Messenger).
|
"""A client for the Facebook Chat (Messenger).
|
||||||
@@ -48,7 +47,7 @@ class Client(object):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, email, password, debug=True, user_agent=None):
|
def __init__(self, email, password, debug=True, user_agent=None , max_retries=5):
|
||||||
"""A client for the Facebook Chat (Messenger).
|
"""A client for the Facebook Chat (Messenger).
|
||||||
|
|
||||||
:param email: Facebook `email` or `id` or `phone number`
|
:param email: Facebook `email` or `id` or `phone number`
|
||||||
@@ -85,8 +84,17 @@ class Client(object):
|
|||||||
|
|
||||||
self._console("Logging in...")
|
self._console("Logging in...")
|
||||||
|
|
||||||
if not self.login():
|
for i in range(1,max_retries+1):
|
||||||
raise Exception("id or password is wrong")
|
if not self.login():
|
||||||
|
self._console("Attempt #{} failed{}".format(i,{True:', retrying'}.get(i<5,'')))
|
||||||
|
time.sleep(1)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
self._console("login successful")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise Exception("login failed. Check id/password")
|
||||||
|
|
||||||
|
|
||||||
self.threads = []
|
self.threads = []
|
||||||
|
|
||||||
@@ -222,38 +230,51 @@ class Client(object):
|
|||||||
thread_id = None
|
thread_id = None
|
||||||
user_id = recipient_id
|
user_id = recipient_id
|
||||||
|
|
||||||
|
messageAndOTID=generateOfflineThreadingID()
|
||||||
timestamp = now()
|
timestamp = now()
|
||||||
date = datetime.now()
|
date = datetime.now()
|
||||||
data = {
|
data = {
|
||||||
'client' : self.client,
|
'client': self.client,
|
||||||
'message_batch[0][action_type]' : 'ma-type:user-generated-message',
|
'action_type' : 'ma-type:user-generated-message',
|
||||||
'message_batch[0][author]' : 'fbid:' + str(self.uid),
|
'author' : 'fbid:' + str(self.uid),
|
||||||
'message_batch[0][specific_to_list][0]' : 'fbid:' + str(recipient_id),
|
'timestamp' : timestamp,
|
||||||
'message_batch[0][specific_to_list][1]' : 'fbid:' + str(self.uid),
|
'timestamp_absolute' : 'Today',
|
||||||
'message_batch[0][timestamp]' : timestamp,
|
'timestamp_relative' : str(date.hour) + ":" + str(date.minute).zfill(2),
|
||||||
'message_batch[0][timestamp_absolute]' : 'Today',
|
'timestamp_time_passed' : '0',
|
||||||
'message_batch[0][timestamp_relative]' : str(date.hour) + ":" + str(date.minute).zfill(2),
|
'is_unread' : False,
|
||||||
'message_batch[0][timestamp_time_passed]' : '0',
|
'is_cleared' : False,
|
||||||
'message_batch[0][is_unread]' : False,
|
'is_forward' : False,
|
||||||
'message_batch[0][is_cleared]' : False,
|
'is_filtered_content' : False,
|
||||||
'message_batch[0][is_forward]' : False,
|
'is_filtered_content_bh': False,
|
||||||
'message_batch[0][is_filtered_content]' : False,
|
'is_filtered_content_account': False,
|
||||||
'message_batch[0][is_spoof_warning]' : False,
|
'is_filtered_content_quasar': False,
|
||||||
'message_batch[0][source]' : 'source:chat:web',
|
'is_filtered_content_invalid_app': False,
|
||||||
'message_batch[0][source_tags][0]' : 'source:chat',
|
'is_spoof_warning' : False,
|
||||||
'message_batch[0][body]' : message,
|
'source' : 'source:chat:web',
|
||||||
'message_batch[0][html_body]' : False,
|
'source_tags[0]' : 'source:chat',
|
||||||
'message_batch[0][ui_push_phase]' : 'V3',
|
'body' : message,
|
||||||
'message_batch[0][status]' : '0',
|
'html_body' : False,
|
||||||
'message_batch[0][message_id]' : generateMessageID(self.client_id),
|
'ui_push_phase' : 'V3',
|
||||||
'message_batch[0][manual_retry_cnt]' : '0',
|
'status' : '0',
|
||||||
'message_batch[0][thread_fbid]' : thread_id,
|
'offline_threading_id':messageAndOTID,
|
||||||
'message_batch[0][has_attachment]' : image_id != None,
|
'message_id' : messageAndOTID,
|
||||||
'message_batch[0][other_user_fbid]' : user_id
|
'threading_id':generateMessageID(self.client_id),
|
||||||
|
'ephemeral_ttl_mode:': '0',
|
||||||
|
'manual_retry_cnt' : '0',
|
||||||
|
'signatureID' : getSignatureID(),
|
||||||
|
'has_attachment' : image_id != None,
|
||||||
|
'other_user_fbid' : recipient_id,
|
||||||
|
'specific_to_list[0]' : 'fbid:' + str(recipient_id),
|
||||||
|
'specific_to_list[1]' : 'fbid:' + str(self.uid),
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if image_id:
|
if image_id:
|
||||||
data['message_batch[0][image_ids][0]'] = image_id
|
data['image_ids[0]'] = image_id
|
||||||
|
|
||||||
if like:
|
if like:
|
||||||
try:
|
try:
|
||||||
@@ -261,9 +282,13 @@ class Client(object):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
# if user doesn't enter l or m or s, then use the large one
|
# if user doesn't enter l or m or s, then use the large one
|
||||||
sticker = LIKES['l']
|
sticker = LIKES['l']
|
||||||
data["message_batch[0][sticker_id]"] = sticker
|
data["sticker_id"] = sticker
|
||||||
|
|
||||||
r = self._post(SendURL, data)
|
r = self._post(SendURL, data)
|
||||||
|
|
||||||
|
if self.debug:
|
||||||
|
print(r)
|
||||||
|
print(data)
|
||||||
return r.ok
|
return r.ok
|
||||||
|
|
||||||
def sendRemoteImage(self, recipient_id, message=None, message_type='user', image=''):
|
def sendRemoteImage(self, recipient_id, message=None, message_type='user', image=''):
|
||||||
@@ -502,7 +527,7 @@ class Client(object):
|
|||||||
if 'messageMetadata' in m['delta']:
|
if 'messageMetadata' in m['delta']:
|
||||||
mid = m['delta']['messageMetadata']['messageId']
|
mid = m['delta']['messageMetadata']['messageId']
|
||||||
message = m['delta']['body']
|
message = m['delta']['body']
|
||||||
fbid = m['delta']['messageMetadata']['threadKey']['otherUserFbId']
|
fbid = m['delta']['messageMetadata']['actorFbId']
|
||||||
name = None
|
name = None
|
||||||
self.on_message(mid, fbid, name, message, m)
|
self.on_message(mid, fbid, name, message, m)
|
||||||
else:
|
else:
|
||||||
@@ -532,6 +557,23 @@ class Client(object):
|
|||||||
except requests.exceptions.Timeout:
|
except requests.exceptions.Timeout:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def getUserInfo(self,*user_ids):
|
||||||
|
"""Get user info from id. Unordered.
|
||||||
|
|
||||||
|
:param user_ids: one or more user id(s) to query
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = {"ids[{}]".format(i):user_id for i,user_id in enumerate(user_ids)}
|
||||||
|
r = self._post(UserInfoURL, data)
|
||||||
|
info = get_json(r.text)
|
||||||
|
full_data= [details for profile,details in info['payload']['profiles'].items()]
|
||||||
|
if len(full_data)==1:
|
||||||
|
full_data=full_data[0]
|
||||||
|
return full_data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def on_message(self, mid, author_id, author_name, message, metadata):
|
def on_message(self, mid, author_id, author_name, message, metadata):
|
||||||
'''
|
'''
|
||||||
|
@@ -34,3 +34,14 @@ def generateMessageID(client_id=None):
|
|||||||
k = now()
|
k = now()
|
||||||
l = int(random() * 4294967295)
|
l = int(random() * 4294967295)
|
||||||
return ("<%s:%s-%s@mail.projektitan.com>" % (k, l, client_id));
|
return ("<%s:%s-%s@mail.projektitan.com>" % (k, l, client_id));
|
||||||
|
|
||||||
|
def getSignatureID():
|
||||||
|
return hex(int(random() * 2147483648))
|
||||||
|
|
||||||
|
def generateOfflineThreadingID() :
|
||||||
|
ret = now()
|
||||||
|
value = int(random() * 4294967295);
|
||||||
|
string = ("0000000000000000000000" + bin(value))[-22:]
|
||||||
|
msgs = bin(ret) + string
|
||||||
|
return str(int(msgs,2))
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user