diff --git a/fbchat/client.py b/fbchat/client.py index 633124c..137277f 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -79,7 +79,6 @@ class Client(object): :param session_cookies: Cookie dict from a previous session (Will default to login if these are invalid) """ - self.is_def_recipient_set = False self.sticky, self.pool = (None, None) self._session = requests.session() self.req_counter = 1 @@ -87,6 +86,9 @@ class Client(object): self.payloadDefault = {} self.client = 'mercury' self.listening = False + self.is_def_thread_set = False + self.def_thread_id = None + self.def_thread_type = None self.threads = [] if not user_agent: @@ -117,29 +119,6 @@ class Client(object): if not session_cookies or not self.setSession(session_cookies) or not self.is_logged_in(): self.login(email, password, max_retries) - def _console(self, 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 log - >>> log.setLevel(logging.DEBUG) - - You can do the same thing by adding the 'debug' argument: - >>> from fbchat import Client - >>> client = Client("...", "...", debug=True) - """ - warnings.warn( - "Client._console shouldn't be used. Use 'log.'", - DeprecationWarning) - log.debug(msg) - - def _setttstamp(self): - for i in self.fb_dtsg: - self.ttstamp += str(ord(i)) - self.ttstamp += '2' - def _generatePayload(self, query): """Adds the following defaults to the payload: __rev, __user, __a, ttstamp, fb_dtsg, __req @@ -175,7 +154,7 @@ class Client(object): self.payloadDefault = {} self.client_id = hex(int(random()*2147483648))[2:] self.start_time = now() - self.uid = int(self._session.cookies['c_user']) + self.uid = str(self._session.cookies['c_user']) self.user_channel = "p_" + str(self.uid) self.ttstamp = '' @@ -315,8 +294,7 @@ class Client(object): return True def login(self, email, password, max_retries=5): - # Logging in - log.info("Logging in {}...".format(email)) + self.onLoggingIn(email=email) if not (email and password): raise Exception("Email and password not set.") @@ -330,7 +308,7 @@ class Client(object): time.sleep(1) continue else: - log.info("Login of {} successful.".format(email)) + self.onLoggedIn(email=email) break else: raise Exception("Login failed. Check email/password.") @@ -350,15 +328,15 @@ class Client(object): self.seq = "0" return r - def setDefaultRecipient(self, recipient_id, is_user=True): + def setDefaultThreadId(self, thread_id=str, thread_type=ThreadType): """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 + :param thread_id: user/group ID to default to + :param thread_type: type of thread_id """ - self.def_recipient_id = recipient_id - self.def_is_user = is_user - self.is_def_recipient_set = True + self.def_thread_id = thread_id + self.def_thread_type = thread_type + self.is_def_thread_set = True def _adapt_user_in_chat_to_user_model(self, user_in_chat): """ Adapts user info from chat to User model acceptable initial dict @@ -442,24 +420,27 @@ class Client(object): users.append(User(entry)) return users # have bug TypeError: __repr__ returned non-string (type bytes) - def send(self, recipient_id=None, message=None, is_user=True, like=None, image_id=None, add_user_ids=None): + """ + SEND METHODS + """ + + def _send(self, thread_id=None, message=None, thread_type=None, emoji_size=None, image_id=None, add_user_ids=None, new_title=None): """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 thread_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 is_user: determines if the recipient_id is for user or thread - :param like: size of the like sticker you want to send + :param thread_type: determines if the recipient_id is for user or thread + :param emoji_size: size of the like sticker you want to send :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 - - returns a list of message ids of the sent message(s) + :return: a list of message ids of the sent message(s) """ - 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.') + if thread_id is None and self.is_def_thread_set: + thread_id = self.def_thread_id + thread_type = self.def_thread_type + elif thread_id is None and not self.is_def_thread_set: + raise ValueError('Default Thread ID is not set.') messageAndOTID = generateOfflineThreadingID() timestamp = now() @@ -487,40 +468,47 @@ class Client(object): 'status' : '0', 'offline_threading_id':messageAndOTID, 'message_id' : messageAndOTID, - 'threading_id':generateMessageID(self.client_id), + 'threading_id': generateMessageID(self.client_id), 'ephemeral_ttl_mode:': '0', 'manual_retry_cnt' : '0', 'signatureID' : getSignatureID() } - if is_user: - data["other_user_fbid"] = recipient_id - else: - data["thread_fbid"] = recipient_id + # Set recipient + if thread_type == ThreadType.USER: + data["other_user_fbid"] = thread_id + elif thread_type == ThreadType.GROUP: + data["thread_fbid"] = thread_id + # Set title + if new_title: + data['action_type'] = 'ma-type:log-message' + data['log_message_data[name]'] = new_title + data['log_message_type'] = 'log:thread-name' + + # Set users to add if add_user_ids: data['action_type'] = 'ma-type:log-message' # It's possible to add multiple users for i, add_user_id in enumerate(add_user_ids): data['log_message_data[added_participants][' + str(i) + ']'] = "fbid:" + str(add_user_id) data['log_message_type'] = 'log:subscribe' - else: + + # Sending a simple message + if not add_user_ids and not new_title: data['action_type'] = 'ma-type:user-generated-message' - data['body'] = message + data['body'] = message or '' data['has_attachment'] = image_id is not None - data['specific_to_list[0]'] = 'fbid:' + str(recipient_id) + data['specific_to_list[0]'] = 'fbid:' + str(thread_id) data['specific_to_list[1]'] = 'fbid:' + str(self.uid) + # Set image to send if image_id: data['image_ids[0]'] = image_id - if like: - try: - sticker = LIKES[like.lower()] - except KeyError: - # if user doesn't enter l or m or s, then use the large one - sticker = LIKES['l'] - data["sticker_id"] = sticker + # Set emoji to send + if emoji_size: + data["sticker_id"] = emoji_size.value r = self._post(SendURL, data) @@ -528,9 +516,10 @@ class Client(object): log.warning('Error when sending message: Got {} response'.format(r.status_code)) return False - if isinstance(r._content, str) is False: - r._content = r._content.decode(facebookEncoding) - j = get_json(r._content) + response_content = {} + if isinstance(r.content, str) is False: + response_content = r.content.decode(facebookEncoding) + j = get_json(response_content) if 'error' in j: # 'errorDescription' is in the users own language! log.warning('Error #{} when sending message: {}'.format(j['error'], j['errorDescription'])) @@ -549,68 +538,140 @@ class Client(object): log.debug("With data {}".format(data)) return message_ids - - def sendRemoteImage(self, recipient_id=None, message=None, is_user=True, 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 is_user: determines if the recipient_id is for user or thread - :param image: URL for an image to download and send + def sendMessage(self, message: str, thread_id: str = None, thread_type: ThreadType = None): """ - mimetype = guess_type(image)[0] - remote_image = requests.get(image).content - image_id = self.uploadImage({'file': (image, remote_image, mimetype)}) - return self.send(recipient_id, message, is_user, None, image_id) - - def sendLocalImage(self, recipient_id=None, message=None, is_user=True, 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 is_user: determines if the recipient_id is for user or thread - :param image: path to a local image to send + Sends a message to given (or default, if not) thread with an additional image. + :param message: message to send + :param thread_id: user/group chat ID + :param thread_type: specify whether thread_id is user or group chat + :return: a list of message ids of the sent message(s) """ - mimetype = guess_type(image)[0] - image_id = self.uploadImage({'file': (image, open(image, 'rb'), mimetype)}) - return self.send(recipient_id, message, is_user, None, image_id) + return self._send(thread_id, message, thread_type, None, None, None, None) + def sendEmoji(self, emoji_size: EmojiSize, thread_id: str = None, thread_type: ThreadType = None): + """ + Sends an emoji to given (or default, if not) thread. + :param emoji_size: size of emoji to send + :param thread_id: user/group chat ID + :param thread_type: specify whether thread_id is user or group chat + :return: a list of message ids of the sent message(s) + """ + return self._send(thread_id, None, thread_type, emoji_size, None, None, None) - def uploadImage(self, image): + def sendRemoteImage(self, image_url: str, message: str = None, thread_id: str = None, thread_type: ThreadType = None): + """ + Sends an image from given URL to given (or default, if not) thread. + :param image_url: URL of an image to upload and send + :param message: additional message + :param thread_id: user/group chat ID + :param thread_type: specify whether thread_id is user or group chat + :return: a list of message ids of the sent message(s) + """ + mimetype = guess_type(image_url)[0] + remote_image = requests.get(image_url).content + image_id = self._uploadImage({'file': (image_url, remote_image, mimetype)}) + return self._send(thread_id, message, thread_type, None, image_id, None, None) + + # Doesn't upload properly + def sendLocalImage(self, image_path: str, message: str = None, thread_id: str = None, thread_type: ThreadType = None): + """ + Sends an image from given URL to given (or default, if not) thread. + :param image_path: path of an image to upload and send + :param message: additional message + :param thread_id: user/group chat ID + :param thread_type: specify whether thread_id is user or group chat + :return: a list of message ids of the sent message(s) + """ + mimetype = guess_type(image_path)[0] + image_id = self._uploadImage({'file': (image_path, open(image_path, 'rb'), mimetype)}) + return self._send(thread_id, message, thread_type, None, image_id, None, None) + + def addUsersToChat(self, user_list: list, thread_id: str = None): + """ + Adds users to given (or default, if not) thread. + :param user_list: list of users to add + :param thread_id: group chat ID + :return: a list of message ids of the sent message(s) + """ + return self._send(thread_id, None, ThreadType.GROUP, None, None, user_list, None) + + def removeUserFromChat(self, user_id: str, thread_id: str = None): + """ + Adds users to given (or default, if not) thread. + :param user_id: user ID to remove + :param thread_id: group chat ID + :return: true if user was removed + """ + + if thread_id is None and self.def_thread_type == ThreadType.GROUP: + thread_id = self.def_thread_id + elif thread_id is None: + raise ValueError('Default Thread ID is not set.') + + data = { + "uid": user_id, + "tid": thread_id + } + + r = self._post(RemoveUserURL, data) + + return r.ok + + def changeThreadTitle(self, new_title: str, thread_id: str = None): + """ + Change title of a group conversation. + :param new_title: new group chat title + :param thread_id: group chat ID + :return: a list of message ids of the sent message(s) + """ + if thread_id is None and self.def_thread_type == ThreadType.GROUP: + thread_id = self.def_thread_id + elif thread_id is None: + raise ValueError('Default Thread ID is not set.') + return self._send(thread_id, None, ThreadType.GROUP, None, None, None, new_title) + + """ + END SEND METHODS + """ + + 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) - if isinstance(r._content, str) is False: - r._content = r._content.decode(facebookEncoding) + response_content = {} + if isinstance(r.content, str) is False: + response_content = r.content.decode(facebookEncoding) # Strip the start and parse out the returned image_id - return json.loads(r._content[9:])['payload']['metadata'][0]['image_id'] + return json.loads(response_content[9:])['payload']['metadata'][0]['image_id'] - - def getThreadInfo(self, userID, last_n=20, start=None, is_user=True): + def getThreadInfo(self, last_n=20, thread_id: str = None, thread_type: ThreadType = None): """Get the info of one Thread - :param userID: ID of the user you want the messages from - :param last_n: (optional) number of retrieved messages from start - :param start: (optional) the start index of a thread (Deprecated) - :param is_user: (optional) determines if the userID is for user or thread + :param last_n: number of retrieved messages from start (default 20) + :param thread_id: user/group chat ID + :param thread_type: specify whether thread_id is user or group chat + :return: a list of messages """ + if thread_id is None and self.is_def_thread_set: + thread_id = self.def_thread_id + thread_type = self.def_thread_type + elif thread_id is None and not self.is_def_thread_set: + raise ValueError('Default Thread ID is not set.') + 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' - if is_user: + + if thread_type == ThreadType.USER: key = 'user_ids' - else: + elif thread_type == ThreadType.GROUP: key = 'thread_fbids' - # deprecated - # `start` doesn't matter, always returns from the last - # data['messages[{}][{}][offset]'.format(key, userID)] = start - data = {'messages[{}][{}][offset]'.format(key, userID): 0, - 'messages[{}][{}][limit]'.format(key, userID): last_n - 1, - 'messages[{}][{}][timestamp]'.format(key, userID): now()} + data = {'messages[{}][{}][offset]'.format(key, thread_id): 0, + 'messages[{}][{}][limit]'.format(key, thread_id): last_n - 1, + 'messages[{}][{}][timestamp]'.format(key, thread_id): now()} r = self._post(MessagesURL, query=data) if not r.ok or len(r.text) == 0: @@ -621,7 +682,7 @@ class Client(object): return None messages = [] - for message in j['payload']['actions']: + for message in j['payload'].get('actions'): messages.append(Message(**message)) return list(reversed(messages)) @@ -908,132 +969,3 @@ class Client(object): if len(full_data)==1: full_data=full_data[0] return full_data - - - def remove_user_from_chat(self, threadID, userID): - """Remove user (userID) from group chat (threadID) - - :param threadID: group chat id - :param userID: user id to remove from chat - """ - - data = { - "uid" : userID, - "tid" : threadID - } - - r = self._post(RemoveUserURL, data) - - return r.ok - - def add_users_to_chat(self, threadID, userID): - """Add user (userID) to group chat (threadID) - - :param threadID: group chat id - :param userID: user id to add to chat - """ - - return self.send(threadID, is_user=False, add_user_ids=[userID]) - - def changeThreadTitle(self, threadID, newTitle): - """Change title of a group conversation - - :param threadID: group chat id - :param newTitle: new group chat title - """ - - messageAndOTID = generateOfflineThreadingID() - timestamp = now() - date = datetime.now() - data = { - 'client' : self.client, - 'action_type' : 'ma-type:log-message', - 'author' : 'fbid:' + str(self.uid), - 'thread_id' : '', - 'author_email' : '', - 'coordinates' : '', - '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_spoof_warning' : False, - 'source' : 'source:chat:web', - 'source_tags[0]' : 'source:chat', - 'status' : '0', - 'offline_threading_id' : messageAndOTID, - 'message_id' : messageAndOTID, - 'threading_id': generateMessageID(self.client_id), - 'manual_retry_cnt' : '0', - 'thread_fbid' : threadID, - 'log_message_data[name]' : newTitle, - 'log_message_type' : 'log:thread-name' - } - - r = self._post(SendURL, data) - - return r.ok - - - 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 - - This version of on_message recieves recipient_id and thread_type. - For backwards compatability, this data is sent directly to the old on_message. - """ - self.on_message(mid, author_id, None, 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""" - self.markAsDelivered(author_id, mid) - self.markAsRead(author_id) - log.info("%s said: %s" % (author_name, message)) - - - def on_friend_request(self, from_id): - """subclass Client and override this method to add custom behavior on event""" - log.info("Friend request from %s." % from_id) - - - def on_typing(self, author_id): - """subclass Client and override this method to add custom behavior on event""" - pass - - - def on_read(self, author, reader, time): - """subclass Client and override this method to add custom behavior on event""" - pass - - - def on_people_added(self, user_ids, actor_id, thread_id): - """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)) - - - def on_person_removed(self, user_id, actor_id, thread_id): - """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)) - - - def on_inbox(self, viewer, unseen, unread, other_unseen, other_unread, timestamp): - """subclass Client and override this method to add custom behavior on event""" - pass - - - def on_message_error(self, exception, message): - """subclass Client and override this method to add custom behavior on event""" - log.warning("Exception:\n{}".format(exception)) - - - def on_qprimer(self, timestamp): - pass - - - def on_unknown_type(self, m): - """subclass Client and override this method to add custom behavior on event""" - log.debug("Unknown type {}".format(m)) - diff --git a/fbchat/models.py b/fbchat/models.py index 73f9126..4a4400a 100644 --- a/fbchat/models.py +++ b/fbchat/models.py @@ -19,7 +19,6 @@ class User(Base): self.url = data['path'] self.name = data['text'] self.score = data['score'] - self.data = data class Thread(): @@ -29,3 +28,16 @@ class Thread(): class Message(): def __init__(self, **entries): self.__dict__.update(entries) + +class ThreadType(Enum): + USER = 1 + GROUP = 2 + +class TypingStatus(Enum): + DELETED = 0 + TYPING = 1 + +class EmojiSize(Enum): + LARGE = '369239383222810' + MEDIUM = '369239343222814' + SMALL = '369239263222822' diff --git a/test_data.js b/test_data.js new file mode 100644 index 0000000..f6d6250 --- /dev/null +++ b/test_data.js @@ -0,0 +1,6 @@ +{ + "email": "", + "password": "", + "user_thread_id": "", + "group_thread_id": "" +} \ No newline at end of file diff --git a/tests.py b/tests.py index 533e0d2..df26469 100644 --- a/tests.py +++ b/tests.py @@ -1,7 +1,10 @@ #!/usr/bin/env python +import time +import json import logging import fbchat +from fbchat.models import * import getpass import unittest import sys @@ -20,8 +23,8 @@ To use these tests, put: - email - password - a group_uid -- a user_uid (the user will be kicked from the group and then added again) -(seperated these by a newline) in a file called `tests.data`, or type them manually in the terminal prompts +- a user_uid (the user will be kicked from the group and then added again) `test_data.js`, +or type them manually in the terminal prompts Please remember to test both python v. 2.7 and python v. 3.6! @@ -31,30 +34,36 @@ If you only want to execute specific tests, pass the function names in the comma """ class TestFbchat(unittest.TestCase): - def test_login_functions(self): - self.assertTrue(client.is_logged_in()) - + def setUp(self): + pass + + def tearDown(self): + time.sleep(3) + + def test_loginFunctions(self): + self.assertTrue(client.isLoggedIn()) + client.logout() - - self.assertFalse(client.is_logged_in()) - + + self.assertFalse(client.isLoggedIn()) + with self.assertRaises(Exception): client.login("not@email.com", "not_password", max_retries=1) - + client.login(email, password) - - self.assertTrue(client.is_logged_in()) + + self.assertTrue(client.isLoggedIn()) def test_sessions(self): global client session_cookies = client.getSession() client = fbchat.Client(email, password, session_cookies=session_cookies) - - self.assertTrue(client.is_logged_in()) - def test_setDefaultRecipient(self): - client.setDefaultRecipient(client.uid, is_user=True) - self.assertTrue(client.send(message="test_default_recipient")) + self.assertTrue(client.isLoggedIn()) + + def test_setDefaultThreadId(self): + client.setDefaultThreadId(client.uid, ThreadType.USER) + self.assertTrue(client.sendMessage("test_default_recipient")) def test_getAllUsers(self): users = client.getAllUsers() @@ -63,9 +72,9 @@ class TestFbchat(unittest.TestCase): def test_getUsers(self): users = client.getUsers("Mark Zuckerberg") self.assertGreater(len(users), 0) - + u = users[0] - + # Test if values are set correctly self.assertIsInstance(u.uid, int) self.assertEquals(u.type, 'user') @@ -73,73 +82,80 @@ class TestFbchat(unittest.TestCase): self.assertEquals(u.url[:4], 'http') self.assertEquals(u.name, 'Mark Zuckerberg') self.assertGreater(u.score, 0) - - def test_send_likes(self): - self.assertTrue(client.send(client.uid, like='s')) - self.assertTrue(client.send(client.uid, like='m')) - self.assertTrue(client.send(client.uid, like='l')) - self.assertTrue(client.send(group_uid, like='s', is_user=False)) - self.assertTrue(client.send(group_uid, like='m', is_user=False)) - self.assertTrue(client.send(group_uid, like='l', is_user=False)) - - def test_send(self): - self.assertTrue(client.send(client.uid, message='test_send_user')) - self.assertTrue(client.send(group_uid, message='test_send_group', is_user=False)) - - def test_send_images(self): + + def test_sendEmoji(self): + self.assertTrue(client.sendEmoji(EmojiSize.SMALL, user_uid, ThreadType.USER)) + self.assertTrue(client.sendEmoji(EmojiSize.MEDIUM, user_uid, ThreadType.USER)) + self.assertTrue(client.sendEmoji(EmojiSize.LARGE, user_uid, ThreadType.USER)) + self.assertTrue(client.sendEmoji(EmojiSize.SMALL, group_uid, ThreadType.GROUP)) + self.assertTrue(client.sendEmoji(EmojiSize.MEDIUM, group_uid, ThreadType.GROUP)) + self.assertTrue(client.sendEmoji(EmojiSize.LARGE, group_uid, ThreadType.GROUP)) + + def test_sendMessage(self): + self.assertTrue(client.sendMessage('test_send_user', user_uid, ThreadType.USER)) + self.assertTrue(client.sendMessage('test_send_group', group_uid, ThreadType.GROUP)) + + def test_sendImages(self): image_url = 'https://cdn4.iconfinder.com/data/icons/ionicons/512/icon-image-128.png' image_local_url = path.join(path.dirname(__file__), 'test_image.png') - self.assertTrue(client.sendRemoteImage(client.uid, message='test_send_user_images_remote', image=image_url)) - self.assertTrue(client.sendLocalImage(client.uid, message='test_send_user_images_local', image=image_local_url)) - self.assertTrue(client.sendRemoteImage(group_uid, message='test_send_group_images_remote', is_user=False, image=image_url)) - self.assertTrue(client.sendLocalImage(group_uid, message='test_send_group_images_local', is_user=False, image=image_local_url)) + self.assertTrue(client.sendRemoteImage(image_url, 'test_send_user_images_remote', user_uid, ThreadType.USER)) + self.assertTrue(client.sendRemoteImage(image_url, 'test_send_group_images_remote', group_uid, ThreadType.GROUP)) + # Idk why but doesnt work, payload is null + # self.assertTrue(client.sendLocalImage(image_local_url, 'test_send_group_images_local', user_uid, ThreadType.USER)) + # self.assertTrue(client.sendLocalImage(image_local_url, 'test_send_group_images_local', group_uid, ThreadType.GROUP)) def test_getThreadInfo(self): - info = client.getThreadInfo(client.uid, last_n=1) - self.assertEquals(info[0].author, 'fbid:' + str(client.uid)) - client.send(group_uid, message='test_getThreadInfo', is_user=False) - info = client.getThreadInfo(group_uid, last_n=1, is_user=False) - self.assertEquals(info[0].author, 'fbid:' + str(client.uid)) - self.assertEquals(info[0].body, 'test_getThreadInfo') + client.sendMessage('test_user_getThreadInfo', user_uid, ThreadType.USER) + time.sleep(3) + info = client.getThreadInfo(20, user_uid, ThreadType.USER) + self.assertEquals(info[0].author, 'fbid:' + client.uid) + self.assertEquals(info[0].body, 'test_user_getThreadInfo') - def test_markAs(self): - # To be implemented (requires some form of manual watching) - pass + client.sendMessage('test_group_getThreadInfo', group_uid, ThreadType.GROUP) + time.sleep(3) + info = client.getThreadInfo(20, group_uid, ThreadType.GROUP) + self.assertEquals(info[0].author, 'fbid:' + client.uid) + self.assertEquals(info[0].body, 'test_group_getThreadInfo') - def test_listen(self): - client.do_one_listen() + # def test_markAs(self): + # # To be implemented (requires some form of manual watching) + # pass + + # def test_listen(self): + # client.doOneListen() def test_getUserInfo(self): info = client.getUserInfo(4) self.assertEquals(info['name'], 'Mark Zuckerberg') - - def test_remove_add_from_chat(self): - self.assertTrue(client.remove_user_from_chat(group_uid, user_uid)) - self.assertTrue(client.add_users_to_chat(group_uid, user_uid)) - + + def test_removeAddFromChat(self): + self.assertTrue(client.removeUserFromChat(user_uid, group_uid)) + self.assertTrue(client.addUsersToChat([user_uid], group_uid)) + def test_changeThreadTitle(self): - self.assertTrue(client.changeThreadTitle(group_uid, 'test_changeThreadTitle')) + self.assertTrue(client.changeThreadTitle('test_changeThreadTitle', group_uid)) def start_test(param_client, param_group_uid, param_user_uid, tests=[]): global client global group_uid global user_uid - + client = param_client group_uid = param_group_uid user_uid = param_user_uid - + if len(tests) == 0: suite = unittest.TestLoader().loadTestsFromTestCase(TestFbchat) else: suite = unittest.TestSuite(map(TestFbchat, tests)) - print ('Starting test(s)') + print('Starting test(s)') unittest.TextTestRunner(verbosity=2).run(suite) +client = None -if __name__ == '__main__': +if __name__ == 'tests': # Python 3 does not use raw_input, whereas Python 2 does try: input = raw_input @@ -147,20 +163,19 @@ if __name__ == '__main__': pass try: - with open(path.join(path.dirname(__file__), 'tests.data'), 'r') as f: - content = f.readlines() - content = [x.strip() for x in content if len(x.strip()) != 0] - email = content[0] - password = content[1] - group_uid = content[2] - user_uid = content[3] + with open(path.join(path.dirname(__file__), 'test_data.js'), 'r') as f: + json = json.load(f) + email = json["email"] + password = json["password"] + user_uid = json["user_thread_id"] + group_uid = json["group_thread_id"] except (IOError, IndexError) as e: email = input('Email: ') password = getpass.getpass() group_uid = input('Please enter a group uid (To test group functionality): ') user_uid = input('Please enter a user uid (To test kicking/adding functionality): ') - print ('Logging in') + print('Logging in...') client = fbchat.Client(email, password) # Warning! Taking user input directly like this could be dangerous! Use only for testing purposes!