Merge pull request #129 from Dainius14/master
fix inconsistencies, remove some deprecated and unused things
This commit is contained in:
293
fbchat/client.py
293
fbchat/client.py
@@ -12,11 +12,10 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
from uuid import uuid1
|
from uuid import uuid1
|
||||||
import warnings
|
import warnings
|
||||||
from random import random, choice
|
from random import 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
|
||||||
@@ -25,6 +24,13 @@ from .models import *
|
|||||||
from .stickers import *
|
from .stickers import *
|
||||||
import time
|
import time
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
# Python 3 does not have raw_input, whereas Python 2 has and it's more secure
|
||||||
|
try:
|
||||||
|
input = raw_input
|
||||||
|
except NameError:
|
||||||
|
pass
|
||||||
|
|
||||||
# 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"
|
||||||
@@ -52,15 +58,15 @@ facebookEncoding = 'UTF-8'
|
|||||||
# Log settings
|
# Log settings
|
||||||
log = logging.getLogger("client")
|
log = logging.getLogger("client")
|
||||||
|
|
||||||
|
|
||||||
class Client(object):
|
class Client(object):
|
||||||
"""A client for the Facebook Chat (Messenger).
|
"""A client for the Facebook Chat (Messenger).
|
||||||
|
|
||||||
See http://github.com/carpedm20/fbchat for complete
|
See http://github.com/carpedm20/fbchat for complete
|
||||||
documentation for the API.
|
documentation for the API.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, email, password, debug=True, user_agent=None , max_retries=5, do_login=True):
|
def __init__(self, email, password, debug=True, info_log=True, user_agent=None, max_retries=5, do_login=True):
|
||||||
"""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`
|
||||||
@@ -68,12 +74,12 @@ class Client(object):
|
|||||||
|
|
||||||
import fbchat
|
import fbchat
|
||||||
chat = fbchat.Client(email, password)
|
chat = fbchat.Client(email, password)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if do_login and not (email and password):
|
if do_login and not (email and password):
|
||||||
raise Exception("id and password or config is needed")
|
raise Exception("Email and password not found.")
|
||||||
|
|
||||||
|
self.is_def_recipient_set = False
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
self.sticky, self.pool = (None, None)
|
self.sticky, self.pool = (None, None)
|
||||||
self._session = requests.session()
|
self._session = requests.session()
|
||||||
@@ -111,6 +117,8 @@ class Client(object):
|
|||||||
# Configure the logger differently based on the 'debug' parameter
|
# Configure the logger differently based on the 'debug' parameter
|
||||||
if debug:
|
if debug:
|
||||||
logging_level = logging.DEBUG
|
logging_level = logging.DEBUG
|
||||||
|
elif info_log:
|
||||||
|
logging_level = logging.INFO
|
||||||
else:
|
else:
|
||||||
logging_level = logging.WARNING
|
logging_level = logging.WARNING
|
||||||
|
|
||||||
@@ -119,7 +127,7 @@ class Client(object):
|
|||||||
handler.setLevel(logging_level)
|
handler.setLevel(logging_level)
|
||||||
log.addHandler(handler)
|
log.addHandler(handler)
|
||||||
log.setLevel(logging.DEBUG)
|
log.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
if do_login:
|
if do_login:
|
||||||
self.login(email, password, max_retries)
|
self.login(email, password, max_retries)
|
||||||
|
|
||||||
@@ -131,18 +139,17 @@ class Client(object):
|
|||||||
This method shouldn't be used anymore.
|
This method shouldn't be used anymore.
|
||||||
Use the log itself:
|
Use the log itself:
|
||||||
>>> import logging
|
>>> import logging
|
||||||
>>> from fbchat.client import Client, log
|
>>> from fbchat.client import log
|
||||||
>>> log.setLevel(logging.DEBUG)
|
>>> log.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
You can do the same thing by adding the 'debug' argument:
|
You can do the same thing by adding the 'debug' argument:
|
||||||
>>> from fbchat import Client
|
>>> from fbchat import Client
|
||||||
>>> client = Client("...", "...", debug=True)
|
>>> client = Client("...", "...", debug=True)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
"Client._console shouldn't be used. Use 'log.<level>'",
|
"Client._console shouldn't be used. Use 'log.<level>'",
|
||||||
DeprecationWarning)
|
DeprecationWarning)
|
||||||
if self.debug: log.info(msg)
|
log.debug(msg)
|
||||||
|
|
||||||
def _setttstamp(self):
|
def _setttstamp(self):
|
||||||
for i in self.fb_dtsg:
|
for i in self.fb_dtsg:
|
||||||
@@ -150,10 +157,9 @@ class Client(object):
|
|||||||
self.ttstamp += '2'
|
self.ttstamp += '2'
|
||||||
|
|
||||||
def _generatePayload(self, query):
|
def _generatePayload(self, query):
|
||||||
'''
|
"""Adds the following defaults to the payload:
|
||||||
Adds the following defaults to the payload:
|
|
||||||
__rev, __user, __a, ttstamp, fb_dtsg, __req
|
__rev, __user, __a, ttstamp, fb_dtsg, __req
|
||||||
'''
|
"""
|
||||||
payload = self.payloadDefault.copy()
|
payload = self.payloadDefault.copy()
|
||||||
if query:
|
if query:
|
||||||
payload.update(query)
|
payload.update(query)
|
||||||
@@ -221,7 +227,7 @@ class Client(object):
|
|||||||
|
|
||||||
def _login(self):
|
def _login(self):
|
||||||
if not (self.email and self.password):
|
if not (self.email and self.password):
|
||||||
raise Exception("id and password or config is needed")
|
raise Exception("Email and password not found.")
|
||||||
|
|
||||||
soup = bs(self._get(MobileURL).text, "lxml")
|
soup = bs(self._get(MobileURL).text, "lxml")
|
||||||
data = dict((elem['name'], elem['value']) for elem in soup.findAll("input") if elem.has_attr('value') and elem.has_attr('name'))
|
data = dict((elem['name'], elem['value']) for elem in soup.findAll("input") if elem.has_attr('value') and elem.has_attr('name'))
|
||||||
@@ -230,7 +236,7 @@ class Client(object):
|
|||||||
data['login'] = 'Log In'
|
data['login'] = 'Log In'
|
||||||
|
|
||||||
r = self._cleanPost(LoginURL, data)
|
r = self._cleanPost(LoginURL, data)
|
||||||
|
|
||||||
# Usually, 'Checkpoint' will refer to 2FA
|
# Usually, 'Checkpoint' will refer to 2FA
|
||||||
if 'checkpoint' in r.url and 'Enter Security Code to Continue' in r.text:
|
if 'checkpoint' in r.url and 'Enter Security Code to Continue' in r.text:
|
||||||
r = self._2FA(r)
|
r = self._2FA(r)
|
||||||
@@ -244,63 +250,65 @@ class Client(object):
|
|||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _2FA(self,r):
|
def _2FA(self, r):
|
||||||
soup = bs(r.text, "lxml")
|
soup = bs(r.text, "lxml")
|
||||||
data = dict()
|
data = dict()
|
||||||
s = raw_input('Please enter your 2FA code --> ')
|
|
||||||
|
s = input('Please enter your 2FA code --> ')
|
||||||
data['approvals_code'] = s
|
data['approvals_code'] = s
|
||||||
data['fb_dtsg'] = soup.find("input", {'name':'fb_dtsg'})['value']
|
data['fb_dtsg'] = soup.find("input", {'name':'fb_dtsg'})['value']
|
||||||
data['nh'] = soup.find("input", {'name':'nh'})['value']
|
data['nh'] = soup.find("input", {'name':'nh'})['value']
|
||||||
data['submit[Submit Code]'] = 'Submit Code'
|
data['submit[Submit Code]'] = 'Submit Code'
|
||||||
data['codes_submitted'] = 0
|
data['codes_submitted'] = 0
|
||||||
log.info('Submitting 2FA code')
|
log.info('Submitting 2FA code.')
|
||||||
|
|
||||||
r = self._cleanPost(CheckpointURL, data)
|
r = self._cleanPost(CheckpointURL, data)
|
||||||
|
|
||||||
if 'home' in r.url:
|
if 'home' in r.url:
|
||||||
return r
|
return r
|
||||||
|
|
||||||
del(data['approvals_code'])
|
del(data['approvals_code'])
|
||||||
del(data['submit[Submit Code]'])
|
del(data['submit[Submit Code]'])
|
||||||
del(data['codes_submitted'])
|
del(data['codes_submitted'])
|
||||||
|
|
||||||
data['name_action_selected'] = 'save_device'
|
data['name_action_selected'] = 'save_device'
|
||||||
data['submit[Continue]'] = 'Continue'
|
data['submit[Continue]'] = 'Continue'
|
||||||
log.info('Saving browser') #At this stage, we have dtsg, nh, name_action_selected, submit[Continue]
|
log.info('Saving browser.') # At this stage, we have dtsg, nh, name_action_selected, submit[Continue]
|
||||||
r = self._cleanPost(CheckpointURL, data)
|
r = self._cleanPost(CheckpointURL, data)
|
||||||
|
|
||||||
if 'home' in r.url:
|
if 'home' in r.url:
|
||||||
return r
|
return r
|
||||||
|
|
||||||
del(data['name_action_selected'])
|
del(data['name_action_selected'])
|
||||||
log.info('Starting Facebook checkup flow') #At this stage, we have dtsg, nh, submit[Continue]
|
log.info('Starting Facebook checkup flow.') # At this stage, we have dtsg, nh, submit[Continue]
|
||||||
r = self._cleanPost(CheckpointURL, data)
|
r = self._cleanPost(CheckpointURL, data)
|
||||||
|
|
||||||
if 'home' in r.url:
|
if 'home' in r.url:
|
||||||
return r
|
return r
|
||||||
|
|
||||||
del(data['submit[Continue]'])
|
del(data['submit[Continue]'])
|
||||||
data['submit[This was me]'] = 'This Was Me'
|
data['submit[This was me]'] = 'This Was Me'
|
||||||
log.info('Verifying login attempt') #At this stage, we have dtsg, nh, submit[This was me]
|
log.info('Verifying login attempt.') # At this stage, we have dtsg, nh, submit[This was me]
|
||||||
r = self._cleanPost(CheckpointURL, data)
|
r = self._cleanPost(CheckpointURL, data)
|
||||||
|
|
||||||
if 'home' in r.url:
|
if 'home' in r.url:
|
||||||
return r
|
return r
|
||||||
|
|
||||||
del(data['submit[This was me]'])
|
del(data['submit[This was me]'])
|
||||||
data['submit[Continue]'] = 'Continue'
|
data['submit[Continue]'] = 'Continue'
|
||||||
data['name_action_selected'] = 'save_device'
|
data['name_action_selected'] = 'save_device'
|
||||||
log.info('Saving device again') #At this stage, we have dtsg, nh, submit[Continue], name_action_selected
|
log.info('Saving device again.') # At this stage, we have dtsg, nh, submit[Continue], name_action_selected
|
||||||
r = self._cleanPost(CheckpointURL, data)
|
r = self._cleanPost(CheckpointURL, data)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def saveSession(self, sessionfile):
|
def saveSession(self, sessionfile):
|
||||||
"""Dumps the session cookies to (sessionfile).
|
"""Dumps the session cookies to (sessionfile).
|
||||||
WILL OVERWRITE ANY EXISTING FILE
|
WILL OVERWRITE ANY EXISTING FILE
|
||||||
|
|
||||||
:param sessionfile: location of saved session file
|
:param sessionfile: location of saved session file
|
||||||
"""
|
"""
|
||||||
|
|
||||||
log.info('Saving session')
|
log.info('Saving session')
|
||||||
with open(sessionfile, 'w') as f:
|
with open(sessionfile, 'w') as f:
|
||||||
# Grab cookies from current session, and save them as JSON
|
# Grab cookies from current session, and save them as JSON
|
||||||
@@ -308,10 +316,10 @@ class Client(object):
|
|||||||
|
|
||||||
def loadSession(self, sessionfile):
|
def loadSession(self, sessionfile):
|
||||||
"""Loads session cookies from (sessionfile)
|
"""Loads session cookies from (sessionfile)
|
||||||
|
|
||||||
:param sessionfile: location of saved session file
|
:param sessionfile: location of saved session file
|
||||||
"""
|
"""
|
||||||
|
|
||||||
log.info('Loading session')
|
log.info('Loading session')
|
||||||
with open(sessionfile, 'r') as f:
|
with open(sessionfile, 'r') as f:
|
||||||
try:
|
try:
|
||||||
@@ -328,25 +336,27 @@ class Client(object):
|
|||||||
def login(self, email, password, max_retries=5):
|
def login(self, email, password, max_retries=5):
|
||||||
# Logging in
|
# Logging in
|
||||||
log.info("Logging in...")
|
log.info("Logging in...")
|
||||||
|
|
||||||
self.email = email
|
self.email = email
|
||||||
self.password = password
|
self.password = password
|
||||||
|
|
||||||
for i in range(1,max_retries+1):
|
for i in range(1, max_retries+1):
|
||||||
if not self._login():
|
if not self._login():
|
||||||
log.warning("Attempt #{} failed{}".format(i,{True:', retrying'}.get(i<5,'')))
|
log.warning("Attempt #{} failed{}".format(i,{True:', retrying'}.get(i<5,'')))
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
log.info("Login successful")
|
log.info("Login successful.")
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
raise Exception("login failed. Check id/password")
|
raise Exception("Login failed. Check email/password.")
|
||||||
|
|
||||||
def logout(self, timeout=30):
|
def logout(self, timeout=30):
|
||||||
data = {}
|
data = {
|
||||||
data['ref'] = "mb"
|
'ref': "mb",
|
||||||
data['h'] = self.fb_h
|
'h': self.fb_h
|
||||||
|
}
|
||||||
|
|
||||||
payload=self._generatePayload(data)
|
payload=self._generatePayload(data)
|
||||||
r = self._session.get(LogoutURL, headers=self._header, params=payload, timeout=timeout)
|
r = self._session.get(LogoutURL, headers=self._header, params=payload, timeout=timeout)
|
||||||
# reset value
|
# reset value
|
||||||
@@ -356,16 +366,21 @@ class Client(object):
|
|||||||
self.seq = "0"
|
self.seq = "0"
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def listen(self):
|
def setDefaultRecipient(self, recipient_id, is_user=True):
|
||||||
pass
|
"""Sets default recipient to send messages and images to.
|
||||||
|
|
||||||
|
:param recipient_id: the user id or thread id that you want to send a message to
|
||||||
|
:param is_user: determines if the recipient_id is for user or thread
|
||||||
|
"""
|
||||||
|
self.def_recipient_id = recipient_id
|
||||||
|
self.def_is_user = is_user
|
||||||
|
self.is_def_recipient_set = True
|
||||||
|
|
||||||
def _adapt_user_in_chat_to_user_model(self, user_in_chat):
|
def _adapt_user_in_chat_to_user_model(self, user_in_chat):
|
||||||
|
""" Adapts user info from chat to User model acceptable initial dict
|
||||||
|
|
||||||
''' Adapts user info from chat to User model acceptable initial dict
|
|
||||||
|
|
||||||
:param user_in_chat: user info from chat
|
:param user_in_chat: user info from chat
|
||||||
|
|
||||||
|
|
||||||
'dir': None,
|
'dir': None,
|
||||||
'mThumbSrcSmall': None,
|
'mThumbSrcSmall': None,
|
||||||
'is_friend': False,
|
'is_friend': False,
|
||||||
@@ -382,9 +397,7 @@ class Client(object):
|
|||||||
'uri': 'https://www.facebook.com/profile.php?id=100014812758264',
|
'uri': 'https://www.facebook.com/profile.php?id=100014812758264',
|
||||||
'id': '100014812758264',
|
'id': '100014812758264',
|
||||||
'gender': 2
|
'gender': 2
|
||||||
|
"""
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'type': 'user',
|
'type': 'user',
|
||||||
@@ -446,18 +459,24 @@ class Client(object):
|
|||||||
users.append(User(entry))
|
users.append(User(entry))
|
||||||
return users # have bug TypeError: __repr__ returned non-string (type bytes)
|
return users # have bug TypeError: __repr__ returned non-string (type bytes)
|
||||||
|
|
||||||
def send(self, recipient_id, message=None, message_type='user', like=None, image_id=None, add_user_ids=None):
|
def send(self, recipient_id=None, message=None, is_user=True, like=None, image_id=None, add_user_ids=None):
|
||||||
"""Send a message with given thread id
|
"""Send a message with given thread id
|
||||||
|
|
||||||
:param recipient_id: the user id or thread id that you want to send a message to
|
:param recipient_id: the user id or thread id that you want to send a message to
|
||||||
:param message: a text that you want to send
|
:param message: a text that you want to send
|
||||||
:param message_type: determines if the recipient_id is for user or thread
|
:param is_user: determines if the recipient_id is for user or thread
|
||||||
:param like: size of the like sticker you want to send
|
:param like: size of the like sticker you want to send
|
||||||
:param image_id: id for the image to send, gotten from the UploadURL
|
:param image_id: id for the image to send, gotten from the UploadURL
|
||||||
:param add_user_ids: a list of user ids to add to a chat
|
:param add_user_ids: a list of user ids to add to a chat
|
||||||
"""
|
"""
|
||||||
|
|
||||||
messageAndOTID=generateOfflineThreadingID()
|
if self.is_def_recipient_set:
|
||||||
|
recipient_id = self.def_recipient_id
|
||||||
|
is_user = self.def_is_user
|
||||||
|
elif recipient_id is None:
|
||||||
|
raise Exception('Recipient ID is not set.')
|
||||||
|
|
||||||
|
messageAndOTID = generateOfflineThreadingID()
|
||||||
timestamp = now()
|
timestamp = now()
|
||||||
date = datetime.now()
|
date = datetime.now()
|
||||||
data = {
|
data = {
|
||||||
@@ -488,12 +507,12 @@ class Client(object):
|
|||||||
'manual_retry_cnt' : '0',
|
'manual_retry_cnt' : '0',
|
||||||
'signatureID' : getSignatureID()
|
'signatureID' : getSignatureID()
|
||||||
}
|
}
|
||||||
|
|
||||||
if message_type.lower() == 'group':
|
if is_user:
|
||||||
data["thread_fbid"] = recipient_id
|
|
||||||
else:
|
|
||||||
data["other_user_fbid"] = recipient_id
|
data["other_user_fbid"] = recipient_id
|
||||||
|
else:
|
||||||
|
data["thread_fbid"] = recipient_id
|
||||||
|
|
||||||
if add_user_ids:
|
if add_user_ids:
|
||||||
data['action_type'] = 'ma-type:log-message'
|
data['action_type'] = 'ma-type:log-message'
|
||||||
# It's possible to add multiple users
|
# It's possible to add multiple users
|
||||||
@@ -503,7 +522,7 @@ class Client(object):
|
|||||||
else:
|
else:
|
||||||
data['action_type'] = 'ma-type:user-generated-message'
|
data['action_type'] = 'ma-type:user-generated-message'
|
||||||
data['body'] = message
|
data['body'] = message
|
||||||
data['has_attachment'] = image_id != None
|
data['has_attachment'] = image_id is not None
|
||||||
data['specific_to_list[0]'] = 'fbid:' + str(recipient_id)
|
data['specific_to_list[0]'] = 'fbid:' + str(recipient_id)
|
||||||
data['specific_to_list[1]'] = 'fbid:' + str(self.uid)
|
data['specific_to_list[1]'] = 'fbid:' + str(self.uid)
|
||||||
|
|
||||||
@@ -519,10 +538,12 @@ class Client(object):
|
|||||||
data["sticker_id"] = sticker
|
data["sticker_id"] = sticker
|
||||||
|
|
||||||
r = self._post(SendURL, data)
|
r = self._post(SendURL, data)
|
||||||
|
|
||||||
if not r.ok:
|
if r.ok:
|
||||||
return False
|
log.info('Message sent.')
|
||||||
|
else:
|
||||||
|
log.info('Message not sent.')
|
||||||
|
|
||||||
if isinstance(r._content, str) is False:
|
if isinstance(r._content, str) is False:
|
||||||
r._content = r._content.decode(facebookEncoding)
|
r._content = r._content.decode(facebookEncoding)
|
||||||
j = get_json(r._content)
|
j = get_json(r._content)
|
||||||
@@ -535,55 +556,58 @@ class Client(object):
|
|||||||
log.debug("With data {}".format(data))
|
log.debug("With data {}".format(data))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def sendRemoteImage(self, recipient_id, message=None, message_type='user', image=''):
|
|
||||||
|
def sendRemoteImage(self, recipient_id=None, message=None, is_user=True, image=''):
|
||||||
"""Send an image from a URL
|
"""Send an image from a URL
|
||||||
|
|
||||||
:param recipient_id: the user id or thread id that you want to send a message to
|
:param recipient_id: the user id or thread id that you want to send a message to
|
||||||
:param message: a text that you want to send
|
:param message: a text that you want to send
|
||||||
:param message_type: determines if the recipient_id is for user or thread
|
:param is_user: determines if the recipient_id is for user or thread
|
||||||
:param image: URL for an image to download and send
|
:param image: URL for an image to download and send
|
||||||
"""
|
"""
|
||||||
mimetype = guess_type(image)[0]
|
mimetype = guess_type(image)[0]
|
||||||
remote_image = requests.get(image).content
|
remote_image = requests.get(image).content
|
||||||
image_id = self.uploadImage({'file': (image, remote_image, mimetype)})
|
image_id = self.uploadImage({'file': (image, remote_image, mimetype)})
|
||||||
return self.send(recipient_id, message, message_type, None, image_id)
|
return self.send(recipient_id, message, is_user, None, image_id)
|
||||||
|
|
||||||
def sendLocalImage(self, recipient_id, message=None, message_type='user', image=''):
|
def sendLocalImage(self, recipient_id=None, message=None, is_user=True, image=''):
|
||||||
"""Send an image from a file path
|
"""Send an image from a file path
|
||||||
|
|
||||||
:param recipient_id: the user id or thread id that you want to send a message to
|
:param recipient_id: the user id or thread id that you want to send a message to
|
||||||
:param message: a text that you want to send
|
:param message: a text that you want to send
|
||||||
:param message_type: determines if the recipient_id is for user or thread
|
:param is_user: determines if the recipient_id is for user or thread
|
||||||
:param image: path to a local image to send
|
:param image: path to a local image to send
|
||||||
"""
|
"""
|
||||||
mimetype = guess_type(image)[0]
|
mimetype = guess_type(image)[0]
|
||||||
image_id = self.uploadImage({'file': (image, open(image, 'rb'), mimetype)})
|
image_id = self.uploadImage({'file': (image, open(image, 'rb'), mimetype)})
|
||||||
return self.send(recipient_id, message, message_type, None, image_id)
|
return self.send(recipient_id, message, is_user, None, image_id)
|
||||||
|
|
||||||
|
|
||||||
def uploadImage(self, image):
|
def uploadImage(self, image):
|
||||||
"""Upload an image and get the image_id for sending in a message
|
"""Upload an image and get the image_id for sending in a message
|
||||||
|
|
||||||
:param image: a tuple of (file name, data, mime type) to upload to facebook
|
:param image: a tuple of (file name, data, mime type) to upload to facebook
|
||||||
"""
|
"""
|
||||||
|
|
||||||
r = self._postFile(UploadURL, image)
|
r = self._postFile(UploadURL, image)
|
||||||
if isinstance(r._content, str) is False:
|
if isinstance(r._content, str) is False:
|
||||||
r._content = r._content.decode(facebookEncoding)
|
r._content = r._content.decode(facebookEncoding)
|
||||||
# Strip the start and parse out the returned image_id
|
# Strip the start and parse out the returned image_id
|
||||||
return json.loads(r._content[9:])['payload']['metadata'][0]['image_id']
|
return json.loads(r._content[9:])['payload']['metadata'][0]['image_id']
|
||||||
|
|
||||||
def getThreadInfo(self, userID, last_n=20, start=None, thread_type='user'):
|
|
||||||
|
def getThreadInfo(self, userID, last_n=20, start=None, is_user=True):
|
||||||
"""Get the info of one Thread
|
"""Get the info of one Thread
|
||||||
|
|
||||||
:param userID: ID of the user you want the messages from
|
:param userID: ID of the user you want the messages from
|
||||||
:param last_n: (optional) number of retrieved messages from start
|
:param last_n: (optional) number of retrieved messages from start
|
||||||
:param start: (optional) the start index of a thread (Deprecated)
|
:param start: (optional) the start index of a thread (Deprecated)
|
||||||
:param thread_type: (optional) change from 'user' for group threads
|
:param is_user: (optional) determines if the userID is for user or thread
|
||||||
"""
|
"""
|
||||||
|
|
||||||
assert last_n > 0, 'length must be positive integer, got %d' % last_n
|
assert last_n > 0, 'length must be positive integer, got %d' % last_n
|
||||||
assert start is None, '`start` is deprecated, always 0 offset querry is returned'
|
assert start is None, '`start` is deprecated, always 0 offset querry is returned'
|
||||||
data = {}
|
if is_user:
|
||||||
if thread_type == 'user':
|
|
||||||
key = 'user_ids'
|
key = 'user_ids'
|
||||||
else:
|
else:
|
||||||
key = 'thread_fbids'
|
key = 'thread_fbids'
|
||||||
@@ -591,9 +615,9 @@ class Client(object):
|
|||||||
# deprecated
|
# deprecated
|
||||||
# `start` doesn't matter, always returns from the last
|
# `start` doesn't matter, always returns from the last
|
||||||
# data['messages[{}][{}][offset]'.format(key, userID)] = start
|
# data['messages[{}][{}][offset]'.format(key, userID)] = start
|
||||||
data['messages[{}][{}][offset]'.format(key, userID)] = 0
|
data = {'messages[{}][{}][offset]'.format(key, userID): 0,
|
||||||
data['messages[{}][{}][limit]'.format(key, userID)] = last_n
|
'messages[{}][{}][limit]'.format(key, userID): last_n,
|
||||||
data['messages[{}][{}][timestamp]'.format(key, userID)] = now()
|
'messages[{}][{}][timestamp]'.format(key, userID): now()}
|
||||||
|
|
||||||
r = self._post(MessagesURL, query=data)
|
r = self._post(MessagesURL, query=data)
|
||||||
if not r.ok or len(r.text) == 0:
|
if not r.ok or len(r.text) == 0:
|
||||||
@@ -613,13 +637,11 @@ class Client(object):
|
|||||||
"""Get thread list of your facebook account.
|
"""Get thread list of your facebook account.
|
||||||
|
|
||||||
:param start: the start index of a thread
|
:param start: the start index of a thread
|
||||||
:param end: (optional) the last index of a thread
|
:param length: (optional) the length of a thread
|
||||||
"""
|
"""
|
||||||
|
|
||||||
assert length < 21, '`length` is deprecated, max. last 20 threads are returned'
|
assert length < 21, '`length` is deprecated, max. last 20 threads are returned'
|
||||||
|
|
||||||
timestamp = now()
|
|
||||||
date = datetime.now()
|
|
||||||
data = {
|
data = {
|
||||||
'client' : self.client,
|
'client' : self.client,
|
||||||
'inbox[offset]' : start,
|
'inbox[offset]' : start,
|
||||||
@@ -659,7 +681,7 @@ class Client(object):
|
|||||||
'client': 'mercury_sync',
|
'client': 'mercury_sync',
|
||||||
'folders[0]': 'inbox',
|
'folders[0]': 'inbox',
|
||||||
'last_action_timestamp': now() - 60*1000
|
'last_action_timestamp': now() - 60*1000
|
||||||
#'last_action_timestamp': 0
|
# 'last_action_timestamp': 0
|
||||||
}
|
}
|
||||||
|
|
||||||
r = self._post(ThreadSyncURL, form)
|
r = self._post(ThreadSyncURL, form)
|
||||||
@@ -674,17 +696,22 @@ class Client(object):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def markAsDelivered(self, userID, threadID):
|
def markAsDelivered(self, userID, threadID):
|
||||||
data = {"message_ids[0]": threadID}
|
data = {
|
||||||
data["thread_ids[%s][0]"%userID] = threadID
|
"message_ids[0]": threadID,
|
||||||
|
"thread_ids[%s][0]" % userID: threadID
|
||||||
|
}
|
||||||
|
|
||||||
r = self._post(DeliveredURL, data)
|
r = self._post(DeliveredURL, data)
|
||||||
return r.ok
|
return r.ok
|
||||||
|
|
||||||
|
|
||||||
def markAsRead(self, userID):
|
def markAsRead(self, userID):
|
||||||
data = {
|
data = {
|
||||||
"watermarkTimestamp": now(),
|
"watermarkTimestamp": now(),
|
||||||
"shouldSendReadReceipt": True
|
"shouldSendReadReceipt": True,
|
||||||
|
"ids[%s]" % userID: True
|
||||||
}
|
}
|
||||||
data["ids[%s]"%userID] = True
|
|
||||||
r = self._post(ReadStatusURL, data)
|
r = self._post(ReadStatusURL, data)
|
||||||
return r.ok
|
return r.ok
|
||||||
|
|
||||||
@@ -693,6 +720,7 @@ class Client(object):
|
|||||||
r = self._post(MarkSeenURL, {"seen_timestamp": 0})
|
r = self._post(MarkSeenURL, {"seen_timestamp": 0})
|
||||||
return r.ok
|
return r.ok
|
||||||
|
|
||||||
|
|
||||||
def friend_connect(self, friend_id):
|
def friend_connect(self, friend_id):
|
||||||
data = {
|
data = {
|
||||||
"to_friend": friend_id,
|
"to_friend": friend_id,
|
||||||
@@ -701,9 +729,6 @@ class Client(object):
|
|||||||
|
|
||||||
r = self._post(ConnectURL, data)
|
r = self._post(ConnectURL, data)
|
||||||
|
|
||||||
if self.debug:
|
|
||||||
print(r)
|
|
||||||
print(data)
|
|
||||||
return r.ok
|
return r.ok
|
||||||
|
|
||||||
|
|
||||||
@@ -722,10 +747,9 @@ class Client(object):
|
|||||||
|
|
||||||
|
|
||||||
def _getSticky(self):
|
def _getSticky(self):
|
||||||
'''
|
"""Call pull api to get sticky and pool parameter,
|
||||||
Call pull api to get sticky and pool parameter,
|
|
||||||
newer api needs these parameter to work.
|
newer api needs these parameter to work.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"msgs_recv": 0,
|
"msgs_recv": 0,
|
||||||
@@ -745,9 +769,7 @@ class Client(object):
|
|||||||
|
|
||||||
|
|
||||||
def _pullMessage(self, sticky, pool):
|
def _pullMessage(self, sticky, pool):
|
||||||
'''
|
"""Call pull api with seq value to get message data."""
|
||||||
Call pull api with seq value to get message data.
|
|
||||||
'''
|
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"msgs_recv": 0,
|
"msgs_recv": 0,
|
||||||
@@ -765,10 +787,9 @@ class Client(object):
|
|||||||
|
|
||||||
|
|
||||||
def _parseMessage(self, content):
|
def _parseMessage(self, content):
|
||||||
'''
|
"""Get message and author name from content.
|
||||||
Get message and author name from content.
|
|
||||||
May contains multiple messages in the content.
|
May contains multiple messages in the content.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if 'ms' not in content: return
|
if 'ms' not in content: return
|
||||||
|
|
||||||
@@ -878,14 +899,14 @@ class Client(object):
|
|||||||
|
|
||||||
def fbidStrip(_fbid):
|
def fbidStrip(_fbid):
|
||||||
# Stripping of `fbid:` from author_id
|
# Stripping of `fbid:` from author_id
|
||||||
if type(_fbid) == int:
|
if type(_fbid) == int:
|
||||||
return _fbid
|
return _fbid
|
||||||
|
|
||||||
if type(_fbid) == str and 'fbid:' in _fbid:
|
if type(_fbid) == str and 'fbid:' in _fbid:
|
||||||
return int(_fbid[5:])
|
return int(_fbid[5:])
|
||||||
|
|
||||||
user_ids = [fbidStrip(uid) for uid in user_ids]
|
user_ids = [fbidStrip(uid) for uid in user_ids]
|
||||||
|
|
||||||
|
|
||||||
data = {"ids[{}]".format(i):uid for i,uid in enumerate(user_ids)}
|
data = {"ids[{}]".format(i):uid for i,uid in enumerate(user_ids)}
|
||||||
r = self._post(UserInfoURL, data)
|
r = self._post(UserInfoURL, data)
|
||||||
@@ -898,39 +919,36 @@ class Client(object):
|
|||||||
|
|
||||||
def remove_user_from_chat(self, threadID, userID):
|
def remove_user_from_chat(self, threadID, userID):
|
||||||
"""Remove user (userID) from group chat (threadID)
|
"""Remove user (userID) from group chat (threadID)
|
||||||
|
|
||||||
:param threadID: group chat id
|
:param threadID: group chat id
|
||||||
:param userID: user id to remove from chat
|
:param userID: user id to remove from chat
|
||||||
"""
|
"""
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"uid" : userID,
|
"uid" : userID,
|
||||||
"tid" : threadID
|
"tid" : threadID
|
||||||
}
|
}
|
||||||
|
|
||||||
r = self._post(RemoveUserURL, data)
|
|
||||||
|
|
||||||
self._console(r)
|
r = self._post(RemoveUserURL, data)
|
||||||
self._console(data)
|
|
||||||
|
|
||||||
return r.ok
|
return r.ok
|
||||||
|
|
||||||
def add_users_to_chat(self, threadID, userID):
|
def add_users_to_chat(self, threadID, userID):
|
||||||
"""Add user (userID) to group chat (threadID)
|
"""Add user (userID) to group chat (threadID)
|
||||||
|
|
||||||
:param threadID: group chat id
|
:param threadID: group chat id
|
||||||
:param userID: user id to add to chat
|
:param userID: user id to add to chat
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.send(threadID, message_type='group', add_user_ids=[userID])
|
return self.send(threadID, is_user=False, add_user_ids=[userID])
|
||||||
|
|
||||||
def changeThreadTitle(self, threadID, newTitle):
|
def changeThreadTitle(self, threadID, newTitle):
|
||||||
"""Change title of a group conversation
|
"""Change title of a group conversation
|
||||||
|
|
||||||
:param threadID: group chat id
|
:param threadID: group chat id
|
||||||
:param newTitle: new group chat title
|
:param newTitle: new group chat title
|
||||||
"""
|
"""
|
||||||
|
|
||||||
messageAndOTID = generateOfflineThreadingID()
|
messageAndOTID = generateOfflineThreadingID()
|
||||||
timestamp = now()
|
timestamp = now()
|
||||||
date = datetime.now()
|
date = datetime.now()
|
||||||
@@ -964,76 +982,57 @@ class Client(object):
|
|||||||
|
|
||||||
r = self._post(SendURL, data)
|
r = self._post(SendURL, data)
|
||||||
|
|
||||||
self._console(r)
|
|
||||||
self._console(data)
|
|
||||||
|
|
||||||
return r.ok
|
return r.ok
|
||||||
|
|
||||||
|
|
||||||
def on_message_new(self, mid, author_id, message, metadata, recipient_id, thread_type):
|
def on_message_new(self, mid, author_id, message, metadata, recipient_id, thread_type):
|
||||||
'''
|
"""subclass Client and override this method to add custom behavior on event
|
||||||
subclass Client and override this method to add custom behavior on event
|
|
||||||
|
This version of on_message recieves recipient_id and thread_type.
|
||||||
This version of on_message recieves recipient_id and thread_type. For backwards compatability, this data is sent directly to the old on_message.
|
For backwards compatability, this data is sent directly to the old on_message.
|
||||||
'''
|
"""
|
||||||
self.on_message(mid, author_id, None, message, metadata)
|
self.on_message(mid, author_id, None, message, metadata)
|
||||||
|
|
||||||
|
|
||||||
def on_message(self, mid, author_id, author_name, message, metadata):
|
def on_message(self, mid, author_id, author_name, message, metadata):
|
||||||
'''
|
"""subclass Client and override this method to add custom behavior on event"""
|
||||||
subclass Client and override this method to add custom behavior on event
|
|
||||||
'''
|
|
||||||
self.markAsDelivered(author_id, mid)
|
self.markAsDelivered(author_id, mid)
|
||||||
self.markAsRead(author_id)
|
self.markAsRead(author_id)
|
||||||
log.info("%s said: %s"%(author_name, message))
|
log.info("%s said: %s" % (author_name, message))
|
||||||
|
|
||||||
|
|
||||||
def on_friend_request(self, from_id):
|
def on_friend_request(self, from_id):
|
||||||
'''
|
"""subclass Client and override this method to add custom behavior on event"""
|
||||||
subclass Client and override this method to add custom behavior on event
|
log.info("Friend request from %s." % from_id)
|
||||||
'''
|
|
||||||
log.info("friend request from %s"%from_id)
|
|
||||||
|
|
||||||
|
|
||||||
def on_typing(self, author_id):
|
def on_typing(self, author_id):
|
||||||
'''
|
"""subclass Client and override this method to add custom behavior on event"""
|
||||||
subclass Client and override this method to add custom behavior on event
|
|
||||||
'''
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def on_read(self, author, reader, time):
|
def on_read(self, author, reader, time):
|
||||||
'''
|
"""subclass Client and override this method to add custom behavior on event"""
|
||||||
subclass Client and override this method to add custom behavior on event
|
|
||||||
'''
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def on_people_added(self, user_ids, actor_id, thread_id):
|
def on_people_added(self, user_ids, actor_id, thread_id):
|
||||||
'''
|
"""subclass Client and override this method to add custom behavior on event"""
|
||||||
subclass Client and override this method to add custom behavior on event
|
|
||||||
'''
|
|
||||||
log.info("User(s) {} was added to {} by {}".format(repr(user_ids), thread_id, actor_id))
|
log.info("User(s) {} was added to {} by {}".format(repr(user_ids), thread_id, actor_id))
|
||||||
|
|
||||||
|
|
||||||
def on_person_removed(self, user_id, actor_id, thread_id):
|
def on_person_removed(self, user_id, actor_id, thread_id):
|
||||||
'''
|
"""subclass Client and override this method to add custom behavior on event"""
|
||||||
subclass Client and override this method to add custom behavior on event
|
|
||||||
'''
|
|
||||||
log.info("User {} was removed from {} by {}".format(user_id, thread_id, actor_id))
|
log.info("User {} was removed from {} by {}".format(user_id, thread_id, actor_id))
|
||||||
|
|
||||||
|
|
||||||
def on_inbox(self, viewer, unseen, unread, other_unseen, other_unread, timestamp):
|
def on_inbox(self, viewer, unseen, unread, other_unseen, other_unread, timestamp):
|
||||||
'''
|
"""subclass Client and override this method to add custom behavior on event"""
|
||||||
subclass Client and override this method to add custom behavior on event
|
|
||||||
'''
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def on_message_error(self, exception, message):
|
def on_message_error(self, exception, message):
|
||||||
'''
|
"""subclass Client and override this method to add custom behavior on event"""
|
||||||
subclass Client and override this method to add custom behavior on event
|
|
||||||
'''
|
|
||||||
log.warning("Exception:\n{}".format(exception))
|
log.warning("Exception:\n{}".format(exception))
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user