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")
|
||||
if sent:
|
||||
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
|
||||
@@ -52,6 +68,33 @@ Getting last messages sent
|
||||
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
|
||||
=======
|
||||
|
||||
|
112
fbchat/client.py
112
fbchat/client.py
@@ -18,15 +18,14 @@ from random import random, choice
|
||||
from datetime import datetime
|
||||
from bs4 import BeautifulSoup as bs
|
||||
from mimetypes import guess_type
|
||||
|
||||
from .utils import *
|
||||
from .models import *
|
||||
from .stickers import *
|
||||
|
||||
import time
|
||||
# URLs
|
||||
LoginURL ="https://m.facebook.com/login.php?login_attempt=1"
|
||||
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"
|
||||
ThreadSyncURL="https://www.facebook.com/ajax/mercury/thread_sync.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"
|
||||
PingURL ="https://0-channel-proxy-06-ash2.facebook.com/active_ping"
|
||||
UploadURL ="https://upload.facebook.com/ajax/mercury/upload.php"
|
||||
|
||||
UserInfoURL ="https://www.facebook.com/chat/user_info/"
|
||||
|
||||
class Client(object):
|
||||
"""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).
|
||||
|
||||
:param email: Facebook `email` or `id` or `phone number`
|
||||
@@ -85,8 +84,17 @@ class Client(object):
|
||||
|
||||
self._console("Logging in...")
|
||||
|
||||
if not self.login():
|
||||
raise Exception("id or password is wrong")
|
||||
for i in range(1,max_retries+1):
|
||||
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 = []
|
||||
|
||||
@@ -222,38 +230,51 @@ class Client(object):
|
||||
thread_id = None
|
||||
user_id = recipient_id
|
||||
|
||||
messageAndOTID=generateOfflineThreadingID()
|
||||
timestamp = now()
|
||||
date = datetime.now()
|
||||
data = {
|
||||
'client' : self.client,
|
||||
'message_batch[0][action_type]' : 'ma-type:user-generated-message',
|
||||
'message_batch[0][author]' : 'fbid:' + str(self.uid),
|
||||
'message_batch[0][specific_to_list][0]' : 'fbid:' + str(recipient_id),
|
||||
'message_batch[0][specific_to_list][1]' : 'fbid:' + str(self.uid),
|
||||
'message_batch[0][timestamp]' : timestamp,
|
||||
'message_batch[0][timestamp_absolute]' : 'Today',
|
||||
'message_batch[0][timestamp_relative]' : str(date.hour) + ":" + str(date.minute).zfill(2),
|
||||
'message_batch[0][timestamp_time_passed]' : '0',
|
||||
'message_batch[0][is_unread]' : False,
|
||||
'message_batch[0][is_cleared]' : False,
|
||||
'message_batch[0][is_forward]' : False,
|
||||
'message_batch[0][is_filtered_content]' : False,
|
||||
'message_batch[0][is_spoof_warning]' : False,
|
||||
'message_batch[0][source]' : 'source:chat:web',
|
||||
'message_batch[0][source_tags][0]' : 'source:chat',
|
||||
'message_batch[0][body]' : message,
|
||||
'message_batch[0][html_body]' : False,
|
||||
'message_batch[0][ui_push_phase]' : 'V3',
|
||||
'message_batch[0][status]' : '0',
|
||||
'message_batch[0][message_id]' : generateMessageID(self.client_id),
|
||||
'message_batch[0][manual_retry_cnt]' : '0',
|
||||
'message_batch[0][thread_fbid]' : thread_id,
|
||||
'message_batch[0][has_attachment]' : image_id != None,
|
||||
'message_batch[0][other_user_fbid]' : user_id
|
||||
'client': self.client,
|
||||
'action_type' : 'ma-type:user-generated-message',
|
||||
'author' : 'fbid:' + str(self.uid),
|
||||
'timestamp' : timestamp,
|
||||
'timestamp_absolute' : 'Today',
|
||||
'timestamp_relative' : str(date.hour) + ":" + str(date.minute).zfill(2),
|
||||
'timestamp_time_passed' : '0',
|
||||
'is_unread' : False,
|
||||
'is_cleared' : False,
|
||||
'is_forward' : False,
|
||||
'is_filtered_content' : False,
|
||||
'is_filtered_content_bh': False,
|
||||
'is_filtered_content_account': False,
|
||||
'is_filtered_content_quasar': False,
|
||||
'is_filtered_content_invalid_app': False,
|
||||
'is_spoof_warning' : False,
|
||||
'source' : 'source:chat:web',
|
||||
'source_tags[0]' : 'source:chat',
|
||||
'body' : message,
|
||||
'html_body' : False,
|
||||
'ui_push_phase' : 'V3',
|
||||
'status' : '0',
|
||||
'offline_threading_id':messageAndOTID,
|
||||
'message_id' : messageAndOTID,
|
||||
'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:
|
||||
data['message_batch[0][image_ids][0]'] = image_id
|
||||
data['image_ids[0]'] = image_id
|
||||
|
||||
if like:
|
||||
try:
|
||||
@@ -261,9 +282,13 @@ class Client(object):
|
||||
except KeyError:
|
||||
# if user doesn't enter l or m or s, then use the large one
|
||||
sticker = LIKES['l']
|
||||
data["message_batch[0][sticker_id]"] = sticker
|
||||
data["sticker_id"] = sticker
|
||||
|
||||
r = self._post(SendURL, data)
|
||||
|
||||
if self.debug:
|
||||
print(r)
|
||||
print(data)
|
||||
return r.ok
|
||||
|
||||
def sendRemoteImage(self, recipient_id, message=None, message_type='user', image=''):
|
||||
@@ -502,7 +527,7 @@ class Client(object):
|
||||
if 'messageMetadata' in m['delta']:
|
||||
mid = m['delta']['messageMetadata']['messageId']
|
||||
message = m['delta']['body']
|
||||
fbid = m['delta']['messageMetadata']['threadKey']['otherUserFbId']
|
||||
fbid = m['delta']['messageMetadata']['actorFbId']
|
||||
name = None
|
||||
self.on_message(mid, fbid, name, message, m)
|
||||
else:
|
||||
@@ -531,6 +556,23 @@ class Client(object):
|
||||
break
|
||||
except requests.exceptions.Timeout:
|
||||
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):
|
||||
|
@@ -34,3 +34,14 @@ def generateMessageID(client_id=None):
|
||||
k = now()
|
||||
l = int(random() * 4294967295)
|
||||
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