From f07b4eeaef95e210f0917c93e6597d078da0390b Mon Sep 17 00:00:00 2001 From: Vincent Le Goff Date: Mon, 24 Oct 2016 15:00:33 -0700 Subject: [PATCH] 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. --- fbchat/client.py | 91 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 61 insertions(+), 30 deletions(-) diff --git a/fbchat/client.py b/fbchat/client.py index 777de09..dbbd363 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -13,7 +13,9 @@ import requests import json +import logging from uuid import uuid1 +import warnings from random import random, choice from datetime import datetime 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" UserInfoURL ="https://www.facebook.com/chat/user_info/" +# Log settings +log = logging.getLogger("client") + class Client(object): """A client for the Facebook Chat (Messenger). @@ -83,15 +88,28 @@ class Client(object): '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): 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) continue else: - self._console("login successful") + log.info("Login successful") break else: raise Exception("login failed. Check id/password") @@ -100,7 +118,23 @@ class Client(object): self.threads = [] 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.'", + DeprecationWarning) + if self.debug: log.info(msg) def _setttstamp(self): for i in self.fb_dtsg: @@ -131,12 +165,12 @@ class Client(object): def _cleanPost(self, url, query=None, timeout=30): self.req_counter += 1 return self._session.post(url, headers=self._header, data=query, timeout=timeout) - + def _postFile(self, url, files=None, timeout=30): payload=self._generatePayload(None) return self._session.post(url, data=payload, timeout=timeout, files=files) - - + + def login(self): if not (self.email and self.password): raise Exception("id and password or config is needed") @@ -266,12 +300,12 @@ class Client(object): '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), + 'specific_to_list[1]' : 'fbid:' + str(self.uid), } - + if image_id: @@ -287,14 +321,13 @@ class Client(object): r = self._post(SendURL, data) - if self.debug: - print(r) - print(data) + log.debug("Sending {}".format(r)) + log.debug("With data {}".format(data)) return r.ok def sendRemoteImage(self, recipient_id, message=None, message_type='user', image=''): """Send an image from a URL - + :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_type: determines if the recipient_id is for user or thread @@ -304,10 +337,10 @@ class Client(object): remote_image = requests.get(image).content image_id = self.uploadImage({'file': (image, remote_image, mimetype)}) return self.send(recipient_id, message, message_type, None, image_id) - + def sendLocalImage(self, recipient_id, message=None, message_type='user', image=''): """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 message: a text that you want to send :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] image_id = self.uploadImage({'file': (image, open(image), mimetype)}) return self.send(recipient_id, message, message_type, None, image_id) - + def uploadImage(self, image): """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 """ r = self._postFile(UploadURL, image) # Strip the start and parse out the returned image_id return json.loads(r._content[9:])['payload']['metadata'][0]['image_id'] - + def getThreadInfo(self, userID, start, end=None): """Get the info of one Thread @@ -386,7 +419,7 @@ class Client(object): for participant in j['payload']['participants']: participants[participant["fbid"]] = participant["name"] except Exception as e: - print(j) + log.warning(str(j)) # Prevent duplicates 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 + + log.debug("Received {}".format(content["ms"])) for m in content['ms']: try: if m['type'] in ['m_messaging', 'messaging']: @@ -532,8 +567,7 @@ class Client(object): name = None self.on_message(mid, fbid, name, message, m) else: - if self.debug: - print(m) + log.debug("Unknwon type {}".format(m)) except Exception as e: # ex_type, ex, tb = sys.exc_info() self.on_message_error(sys.exc_info(), m) @@ -543,9 +577,7 @@ class Client(object): self.listening = True sticky, pool = self._getSticky() - if self.debug: - print("Listening...") - + log.info("Listening...") while self.listening: try: if markAlive: self.ping(sticky) @@ -558,13 +590,13 @@ 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 + :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) @@ -583,7 +615,7 @@ class Client(object): ''' self.markAsDelivered(author_id, mid) 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): @@ -611,8 +643,7 @@ class Client(object): ''' subclass Client and override this method to add custom behavior on event ''' - print("Exception: ") - print(exception) + log.warning("Exception:\n{}".format(exception)) def on_qprimer(self, timestamp):