Add a logger (from 'logging') to log events.
This can be configured externally, to be decided by the developer. Remove 'print's and calls to the 'Client._console' method.
This commit is contained in:
@@ -13,7 +13,9 @@
|
|||||||
|
|
||||||
import requests
|
import requests
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
from uuid import uuid1
|
from uuid import uuid1
|
||||||
|
import warnings
|
||||||
from random import random, choice
|
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
|
||||||
@@ -40,6 +42,9 @@ 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/"
|
UserInfoURL ="https://www.facebook.com/chat/user_info/"
|
||||||
|
|
||||||
|
# Log settings
|
||||||
|
log = logging.getLogger("client")
|
||||||
|
|
||||||
class Client(object):
|
class Client(object):
|
||||||
"""A client for the Facebook Chat (Messenger).
|
"""A client for the Facebook Chat (Messenger).
|
||||||
|
|
||||||
@@ -83,15 +88,28 @@ class Client(object):
|
|||||||
'Connection' : 'keep-alive',
|
'Connection' : 'keep-alive',
|
||||||
}
|
}
|
||||||
|
|
||||||
self._console("Logging in...")
|
# Configure the logger differently based on the 'debug' parameter
|
||||||
|
if debug:
|
||||||
|
logging_level = logging.DEBUG
|
||||||
|
else:
|
||||||
|
logging_level = logging.WARNING
|
||||||
|
|
||||||
|
# Creates the console handler
|
||||||
|
handler = logging.StreamHandler()
|
||||||
|
handler.setLevel(logging_level)
|
||||||
|
log.addHandler(handler)
|
||||||
|
log.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
# Logging in
|
||||||
|
log.info("Logging in...")
|
||||||
|
|
||||||
for i in range(1,max_retries+1):
|
for i in range(1,max_retries+1):
|
||||||
if not self.login():
|
if not self.login():
|
||||||
self._console("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:
|
||||||
self._console("login successful")
|
log.info("Login successful")
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
raise Exception("login failed. Check id/password")
|
raise Exception("login failed. Check id/password")
|
||||||
@@ -100,7 +118,23 @@ class Client(object):
|
|||||||
self.threads = []
|
self.threads = []
|
||||||
|
|
||||||
def _console(self, msg):
|
def _console(self, msg):
|
||||||
if self.debug: print(msg)
|
"""Assumes an INFO level and log it.
|
||||||
|
|
||||||
|
This method shouldn't be used anymore.
|
||||||
|
Use the log itself:
|
||||||
|
>>> import logging
|
||||||
|
>>> from fbchat.client import Client, log
|
||||||
|
>>> log.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
You can do the same thing by addint the 'debug' argument:
|
||||||
|
>>> from fbchat import Client
|
||||||
|
>>> client = Client("...", "...", debug=True)
|
||||||
|
|
||||||
|
"""
|
||||||
|
warnings.warn(
|
||||||
|
"Client._console shouldn't be used. Use 'log.<level>'",
|
||||||
|
DeprecationWarning)
|
||||||
|
if self.debug: log.info(msg)
|
||||||
|
|
||||||
def _setttstamp(self):
|
def _setttstamp(self):
|
||||||
for i in self.fb_dtsg:
|
for i in self.fb_dtsg:
|
||||||
@@ -131,12 +165,12 @@ class Client(object):
|
|||||||
def _cleanPost(self, url, query=None, timeout=30):
|
def _cleanPost(self, url, query=None, timeout=30):
|
||||||
self.req_counter += 1
|
self.req_counter += 1
|
||||||
return self._session.post(url, headers=self._header, data=query, timeout=timeout)
|
return self._session.post(url, headers=self._header, data=query, timeout=timeout)
|
||||||
|
|
||||||
def _postFile(self, url, files=None, timeout=30):
|
def _postFile(self, url, files=None, timeout=30):
|
||||||
payload=self._generatePayload(None)
|
payload=self._generatePayload(None)
|
||||||
return self._session.post(url, data=payload, timeout=timeout, files=files)
|
return self._session.post(url, data=payload, timeout=timeout, files=files)
|
||||||
|
|
||||||
|
|
||||||
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("id and password or config is needed")
|
||||||
@@ -266,12 +300,12 @@ class Client(object):
|
|||||||
'has_attachment' : image_id != None,
|
'has_attachment' : image_id != None,
|
||||||
'other_user_fbid' : recipient_id,
|
'other_user_fbid' : recipient_id,
|
||||||
'specific_to_list[0]' : 'fbid:' + str(recipient_id),
|
'specific_to_list[0]' : 'fbid:' + str(recipient_id),
|
||||||
'specific_to_list[1]' : 'fbid:' + str(self.uid),
|
'specific_to_list[1]' : 'fbid:' + str(self.uid),
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if image_id:
|
if image_id:
|
||||||
@@ -287,14 +321,13 @@ class Client(object):
|
|||||||
|
|
||||||
r = self._post(SendURL, data)
|
r = self._post(SendURL, data)
|
||||||
|
|
||||||
if self.debug:
|
log.debug("Sending {}".format(r))
|
||||||
print(r)
|
log.debug("With data {}".format(data))
|
||||||
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=''):
|
||||||
"""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 message_type: determines if the recipient_id is for user or thread
|
||||||
@@ -304,10 +337,10 @@ class Client(object):
|
|||||||
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, message_type, None, image_id)
|
||||||
|
|
||||||
def sendLocalImage(self, recipient_id, message=None, message_type='user', image=''):
|
def sendLocalImage(self, recipient_id, message=None, message_type='user', 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 message_type: determines if the recipient_id is for user or thread
|
||||||
@@ -316,16 +349,16 @@ class Client(object):
|
|||||||
mimetype = guess_type(image)[0]
|
mimetype = guess_type(image)[0]
|
||||||
image_id = self.uploadImage({'file': (image, open(image), mimetype)})
|
image_id = self.uploadImage({'file': (image, open(image), mimetype)})
|
||||||
return self.send(recipient_id, message, message_type, None, image_id)
|
return self.send(recipient_id, message, message_type, 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)
|
||||||
# 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, start, end=None):
|
def getThreadInfo(self, userID, start, end=None):
|
||||||
"""Get the info of one Thread
|
"""Get the info of one Thread
|
||||||
|
|
||||||
@@ -386,7 +419,7 @@ class Client(object):
|
|||||||
for participant in j['payload']['participants']:
|
for participant in j['payload']['participants']:
|
||||||
participants[participant["fbid"]] = participant["name"]
|
participants[participant["fbid"]] = participant["name"]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(j)
|
log.warning(str(j))
|
||||||
|
|
||||||
# Prevent duplicates in self.threads
|
# Prevent duplicates in self.threads
|
||||||
threadIDs = [getattr(x, "thread_id") for x in self.threads]
|
threadIDs = [getattr(x, "thread_id") for x in self.threads]
|
||||||
@@ -501,6 +534,8 @@ class Client(object):
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
if 'ms' not in content: return
|
if 'ms' not in content: return
|
||||||
|
|
||||||
|
log.debug("Received {}".format(content["ms"]))
|
||||||
for m in content['ms']:
|
for m in content['ms']:
|
||||||
try:
|
try:
|
||||||
if m['type'] in ['m_messaging', 'messaging']:
|
if m['type'] in ['m_messaging', 'messaging']:
|
||||||
@@ -532,8 +567,7 @@ class Client(object):
|
|||||||
name = None
|
name = None
|
||||||
self.on_message(mid, fbid, name, message, m)
|
self.on_message(mid, fbid, name, message, m)
|
||||||
else:
|
else:
|
||||||
if self.debug:
|
log.debug("Unknwon type {}".format(m))
|
||||||
print(m)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# ex_type, ex, tb = sys.exc_info()
|
# ex_type, ex, tb = sys.exc_info()
|
||||||
self.on_message_error(sys.exc_info(), m)
|
self.on_message_error(sys.exc_info(), m)
|
||||||
@@ -543,9 +577,7 @@ class Client(object):
|
|||||||
self.listening = True
|
self.listening = True
|
||||||
sticky, pool = self._getSticky()
|
sticky, pool = self._getSticky()
|
||||||
|
|
||||||
if self.debug:
|
log.info("Listening...")
|
||||||
print("Listening...")
|
|
||||||
|
|
||||||
while self.listening:
|
while self.listening:
|
||||||
try:
|
try:
|
||||||
if markAlive: self.ping(sticky)
|
if markAlive: self.ping(sticky)
|
||||||
@@ -558,13 +590,13 @@ class Client(object):
|
|||||||
break
|
break
|
||||||
except requests.exceptions.Timeout:
|
except requests.exceptions.Timeout:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def getUserInfo(self,*user_ids):
|
def getUserInfo(self,*user_ids):
|
||||||
"""Get user info from id. Unordered.
|
"""Get user info from id. Unordered.
|
||||||
|
|
||||||
:param user_ids: one or more user id(s) to query
|
: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)}
|
data = {"ids[{}]".format(i):user_id for i,user_id in enumerate(user_ids)}
|
||||||
r = self._post(UserInfoURL, data)
|
r = self._post(UserInfoURL, data)
|
||||||
info = get_json(r.text)
|
info = get_json(r.text)
|
||||||
@@ -583,7 +615,7 @@ class Client(object):
|
|||||||
'''
|
'''
|
||||||
self.markAsDelivered(author_id, mid)
|
self.markAsDelivered(author_id, mid)
|
||||||
self.markAsRead(author_id)
|
self.markAsRead(author_id)
|
||||||
print("%s said: %s"%(author_name, message))
|
log.info("%s said: %s"%(author_name, message))
|
||||||
|
|
||||||
|
|
||||||
def on_typing(self, author_id):
|
def on_typing(self, author_id):
|
||||||
@@ -611,8 +643,7 @@ class Client(object):
|
|||||||
'''
|
'''
|
||||||
subclass Client and override this method to add custom behavior on event
|
subclass Client and override this method to add custom behavior on event
|
||||||
'''
|
'''
|
||||||
print("Exception: ")
|
log.warning("Exception:\n{}".format(exception))
|
||||||
print(exception)
|
|
||||||
|
|
||||||
|
|
||||||
def on_qprimer(self, timestamp):
|
def on_qprimer(self, timestamp):
|
||||||
|
Reference in New Issue
Block a user