class fb_client(Client): def __init__(self, email=None, password=None, user_agent=None, \ max_tries=5, session_cookies=None, logging_level=logging.INFO): """ Overrides parent constructor to make email and password optional. (This helps because we primarily use session cookies to log users back in) Args: | email: Facebook login email | password: Facebook login password | max_tries: maximum login attempts | session_cookies: Cookies from a previous session | logging_level: Configures the `logging level` \ Defaults to logging.INFO Raises: FBchatException on failed login """ super(fb_client, self).__init__(email=email, password=password, \ max_tries=max_tries, session_cookies=session_cookies, logging_level=logging_level) def _extract_mentions(self, message_text): """ Takes message in raw text form and returns a list of appropriate Mentions objects Args: message_text: message in raw text form Returns: A list of :class:`fbchat.models.Mention` objects """ # Find all occurrences of @ (for offset) index = 0 mention = [] at_indices = [] for c in message_text: if c is '@': at_indices.append(index) index += 1 # used for offset at_count = 0 elements = message_text.split(" ") for element in elements: # look for @s if element.startswith("@"): users = self.searchForUsers(element[1:]) # @ is a valid Mention if len(users) is not 0: user = users[0] mention.append( \ Mention(user.uid, offset=at_indices[at_count], \ length=len(element))) at_count += 1 return (None if len(mention) is 0 else mention) def _construct_message_from_text(self, message_text): """ Takes message in raw text form and constructs a :class:`fbchat.models.Message object` Args: message_text: message in raw text form Returns: :class:`fbchat.models.Message` object representing raw text message input """ mentions = self._extract_mentions(message_text) return Message(text=message_text, mentions=mentions) def send_message(self, message_text, user_uid=None, group_uid=None): """ Sends a appropriately constructed message given raw text message input. Args: | message_text: message in raw text form | user_uid: uid of user | group_uid: uid of group Raises: FBClientError on missing user_uid and group_uid or when both are provided """ if user_uid is None and group_uid is None: raise FBClientError("Must provide a user_uid or group_uid") if user_uid is not None and group_uid is not None: raise FBClientError("Must only provide one of either a " + \ "user_uid or group_uid") if user_uid is not None: self.send(self._construct_message_from_text(message_text), \ thread_id=user_uid, thread_type=ThreadType.USER) elif group_uid is not None: self.send(self._construct_message_from_text(message_text), \ thread_id=group_uid, thread_type=ThreadType.GROUP) def _listen_daemon(self): """ listener that runs until the thread it is part of is terminated. Not a true daemon, but purpose is to make the listening process run on a separate thread. """ self.startListening() self.onListening() while self.thread.is_running() and self.doOneListen(): pass self.stopListening() self.thread.stopped() def start_listen(self): """ Creates and starts a listener thread. Raises: FBClientError if client is already listening """ try: self.thread raise FBClientError("Called start_listen when " + \ "client is already listening") except AttributeError: logging.info('Client is listening...') self.thread = StoppableThread(name="listen", target=self._listen_daemon, args=()) self.thread.start() def stop_listen(self): """ "Terminates" the listener thread. Raises: FBClientError if client has not been listening """ try: self.thread except AttributeError: raise FBClientError("Called stop_listen when " + \ "client was not listening") if self.thread.is_running(): print "Stopping client listen..." self.thread.stop() self.thread.join() logging.info('Client stopped listening') del self.thread