Fixed examples, added changeNickname
and changeThreadEmoji
, changed changeGroupTitle
back to changeThreadTitle
I also removed the parameter `set_default_events` from __init__, since it's not really necessary Also added testing of examples and simple testing of listen functions
This commit is contained in:
@@ -4,15 +4,15 @@ from fbchat import log, Client
|
|||||||
|
|
||||||
# Subclass fbchat.Client and override required methods
|
# Subclass fbchat.Client and override required methods
|
||||||
class EchoBot(Client):
|
class EchoBot(Client):
|
||||||
def onMessage(self, mid, author_id, message, thread_id, thread_type, ts, metadata, msg):
|
def onMessage(self, author_id, message, thread_id, thread_type, **kwargs):
|
||||||
self.markAsDelivered(author_id, thread_id)
|
self.markAsDelivered(author_id, thread_id)
|
||||||
self.markAsRead(author_id)
|
self.markAsRead(author_id)
|
||||||
|
|
||||||
log.info("Message from {} in {} ({}): {}".format(author_id, thread_id, thread_type.name, message)))
|
log.info("Message from {} in {} ({}): {}".format(author_id, thread_id, thread_type.name, message))
|
||||||
|
|
||||||
# If you're not the author, echo
|
# If you're not the author, echo
|
||||||
if author_id != self.uid:
|
if author_id != self.uid:
|
||||||
self.sendMessage(message, thread_id=thread_id, thread_type=thread_type)
|
self.sendMessage(message, thread_id=thread_id, thread_type=thread_type)
|
||||||
|
|
||||||
client = EchoBot("<email>", "<password>")
|
client = EchoBot("<email>", "<password>")
|
||||||
client.listen()
|
client.listen()
|
||||||
|
@@ -18,39 +18,37 @@ old_nicknames = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class KeepBot(Client):
|
class KeepBot(Client):
|
||||||
def onColorChange(self, mid, author_id, new_color, thread_id, thread_type, ts, metadata, msg):
|
def onColorChange(self, author_id, new_color, thread_id, thread_type, **kwargs):
|
||||||
if old_thread_id == thread_id and old_color != new_color:
|
if old_thread_id == thread_id and old_color != new_color:
|
||||||
log.info("{} changed the thread color. It will be changed back".format(author_id))
|
log.info("{} changed the thread color. It will be changed back".format(author_id))
|
||||||
self.changeThreadColor(old_color, thread_id=thread_id)
|
self.changeThreadColor(old_color, thread_id=thread_id)
|
||||||
|
|
||||||
def onEmojiChange(self, mid, author_id, new_emoji, thread_id, thread_type, ts, metadata, msg):
|
def onEmojiChange(self, author_id, new_emoji, thread_id, thread_type, **kwargs):
|
||||||
if old_thread_id == thread_id and new_emoji != old_emoji:
|
if old_thread_id == thread_id and new_emoji != old_emoji:
|
||||||
log.info("{} changed the thread emoji. It will be changed back".format(author_id))
|
log.info("{} changed the thread emoji. It will be changed back".format(author_id))
|
||||||
# Not currently possible in `fbchat`
|
self.changeThreadEmoji(old_emoji, thread_id=thread_id)
|
||||||
# self.changeThreadEmoji(old_emoji, thread_id=thread_id)
|
|
||||||
|
|
||||||
def onPeopleAdded(self, added_ids, author_id, thread_id, msg):
|
def onPeopleAdded(self, added_ids, author_id, thread_id, **kwargs):
|
||||||
if old_thread_id == thread_id and author_id != self.uid:
|
if old_thread_id == thread_id and author_id != self.uid:
|
||||||
log.info("{} got added. They will be removed".format(added_ids))
|
log.info("{} got added. They will be removed".format(added_ids))
|
||||||
for added_id in added_ids:
|
for added_id in added_ids:
|
||||||
self.removeUserFromGroup(added_id, thread_id=thread_id)
|
self.removeUserFromGroup(added_id, thread_id=thread_id)
|
||||||
|
|
||||||
def onPersonRemoved(self, removed_id, author_id, thread_id, msg):
|
def onPersonRemoved(self, removed_id, author_id, thread_id, **kwargs):
|
||||||
# No point in trying to add ourself
|
# No point in trying to add ourself
|
||||||
if old_thread_id == thread_id and removed_id != self.uid and author_id != self.uid:
|
if old_thread_id == thread_id and removed_id != self.uid and author_id != self.uid:
|
||||||
log.info("{} got removed. They will be re-added".format(removed_id))
|
log.info("{} got removed. They will be re-added".format(removed_id))
|
||||||
self.addUsersToGroup(removed_id, thread_id=thread_id)
|
self.addUsersToGroup(removed_id, thread_id=thread_id)
|
||||||
|
|
||||||
def onTitleChange(self, mid, author_id, new_title, thread_id, thread_type, ts, metadata, msg):
|
def onTitleChange(self, author_id, new_title, thread_id, thread_type, **kwargs):
|
||||||
if old_thread_id == thread_id and old_title != new_title:
|
if old_thread_id == thread_id and old_title != new_title:
|
||||||
log.info("{} changed the thread title. It will be changed back".format(author_id))
|
log.info("{} changed the thread title. It will be changed back".format(author_id))
|
||||||
self.changeGroupTitle(old_title, thread_id=thread_id)
|
self.changeThreadTitle(old_title, thread_id=thread_id, thread_type=thread_type)
|
||||||
|
|
||||||
def onNicknameChange(self, mid, author_id, changed_for, new_nickname, thread_id, thread_type, ts, metadata, msg):
|
def onNicknameChange(self, author_id, changed_for, new_nickname, thread_id, thread_type, **kwargs):
|
||||||
if old_thread_id == thread_id and changed_for in old_nicknames:
|
if old_thread_id == thread_id and changed_for in old_nicknames and old_nicknames[changed_for] != new_nickname:
|
||||||
log.info("{} changed {}'s' nickname. It will be changed back".format(author_id, changed_for))
|
log.info("{} changed {}'s' nickname. It will be changed back".format(author_id, changed_for))
|
||||||
# Not currently possible in `fbchat`
|
self.changeNickname(old_nicknames[changed_for], changed_for, thread_id=thread_id, thread_type=thread_type)
|
||||||
# self.changeNickname(old_nicknames[changed_for], changed_for, thread_id=thread_id, thread_type=thread_type)
|
|
||||||
|
|
||||||
client = KeepBot("<email>", "<password>")
|
client = KeepBot("<email>", "<password>")
|
||||||
client.listen()
|
client.listen()
|
||||||
|
@@ -4,13 +4,13 @@ from fbchat import log, Client
|
|||||||
from fbchat.models import *
|
from fbchat.models import *
|
||||||
|
|
||||||
class RemoveBot(Client):
|
class RemoveBot(Client):
|
||||||
def onMessage(self, mid, author_id, message, thread_id, thread_type, ts, metadata, msg):
|
def onMessage(self, author_id, message, thread_id, thread_type, **kwargs):
|
||||||
# We can only kick people from group chats, so no need to try if it's a user chat
|
# We can only kick people from group chats, so no need to try if it's a user chat
|
||||||
if message == 'Remove me!' and thread_type == ThreadType.GROUP:
|
if message == 'Remove me!' and thread_type == ThreadType.GROUP:
|
||||||
log.info("{} will be removed from {}".format(author_id, thread_id)))
|
log.info("{} will be removed from {}".format(author_id, thread_id))
|
||||||
self.removeUserFromGroup(user_id, thread_id=thread_id)
|
self.removeUserFromGroup(author_id, thread_id=thread_id)
|
||||||
else:
|
else:
|
||||||
log.info("Message from {} in {} ({}): {}".format(author_id, thread_id, thread_type.name, message)))
|
log.info("Message from {} in {} ({}): {}".format(author_id, thread_id, thread_type.name, message))
|
||||||
|
|
||||||
client = RemoveBot("<email>", "<password>")
|
client = RemoveBot("<email>", "<password>")
|
||||||
client.listen()
|
client.listen()
|
||||||
|
127
fbchat/client.py
127
fbchat/client.py
@@ -35,7 +35,7 @@ class Client(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, email, password, debug=False, info_log=False, user_agent=None, max_retries=5,
|
def __init__(self, email, password, debug=False, info_log=False, user_agent=None, max_retries=5,
|
||||||
session_cookies=None, logging_level=logging.INFO, set_default_events=True):
|
session_cookies=None, logging_level=logging.INFO):
|
||||||
"""Initializes and logs in the client
|
"""Initializes and logs in the client
|
||||||
|
|
||||||
:param email: Facebook `email`, `id` or `phone number`
|
:param email: Facebook `email`, `id` or `phone number`
|
||||||
@@ -44,11 +44,9 @@ class Client(object):
|
|||||||
:param max_retries: Maximum number of times to retry login
|
:param max_retries: Maximum number of times to retry login
|
||||||
:param session_cookies: Cookies from a previous session (Will default to login if these are invalid)
|
:param session_cookies: Cookies from a previous session (Will default to login if these are invalid)
|
||||||
:param logging_level: Configures the `logging level <https://docs.python.org/3/library/logging.html#logging-levels>`_. Defaults to `INFO`
|
:param logging_level: Configures the `logging level <https://docs.python.org/3/library/logging.html#logging-levels>`_. Defaults to `INFO`
|
||||||
:param set_default_events: Specifies whether the default `logging.info` events should be initialized
|
|
||||||
:type max_retries: int
|
:type max_retries: int
|
||||||
:type session_cookies: dict
|
:type session_cookies: dict
|
||||||
:type logging_level: int
|
:type logging_level: int
|
||||||
:type set_default_events: bool
|
|
||||||
:raises: Exception on failed login
|
:raises: Exception on failed login
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -62,7 +60,6 @@ class Client(object):
|
|||||||
self.default_thread_id = None
|
self.default_thread_id = None
|
||||||
self.default_thread_type = None
|
self.default_thread_type = None
|
||||||
self.threads = []
|
self.threads = []
|
||||||
self.set_default_events = set_default_events
|
|
||||||
|
|
||||||
self._setupEventHooks()
|
self._setupEventHooks()
|
||||||
self._setupOldEventHooks()
|
self._setupOldEventHooks()
|
||||||
@@ -94,10 +91,7 @@ class Client(object):
|
|||||||
|
|
||||||
def _setEventHook(self, event_name, *functions):
|
def _setEventHook(self, event_name, *functions):
|
||||||
if not hasattr(type(self), event_name):
|
if not hasattr(type(self), event_name):
|
||||||
if self.set_default_events:
|
eventhook = EventHook(*functions)
|
||||||
eventhook = EventHook(*functions)
|
|
||||||
else:
|
|
||||||
eventhook = EventHook()
|
|
||||||
setattr(self, event_name, eventhook)
|
setattr(self, event_name, eventhook)
|
||||||
|
|
||||||
def _setupEventHooks(self):
|
def _setupEventHooks(self):
|
||||||
@@ -138,16 +132,18 @@ class Client(object):
|
|||||||
log.info("Marked messages as seen in threads {} at {}s".format([(x[0], x[1].name) for x in threads], seen_ts/1000)))
|
log.info("Marked messages as seen in threads {} at {}s".format([(x[0], x[1].name) for x in threads], seen_ts/1000)))
|
||||||
|
|
||||||
|
|
||||||
self._setEventHook('onPeopleAdded', lambda added_ids, author_id, thread_id, msg:\
|
self._setEventHook('onPeopleAdded', lambda mid, added_ids, author_id, thread_id, ts, msg:\
|
||||||
log.info("{} added: {}".format(author_id, ', '.join(added_ids))))
|
log.info("{} added: {}".format(author_id, ', '.join(added_ids))))
|
||||||
|
|
||||||
self._setEventHook('onPersonRemoved', lambda removed_id, author_id, thread_id, msg:\
|
self._setEventHook('onPersonRemoved', lambda mid, removed_id, author_id, thread_id, ts, msg:\
|
||||||
log.info("{} removed: {}".format(author_id, removed_id)))
|
log.info("{} removed: {}".format(author_id, removed_id)))
|
||||||
|
|
||||||
self._setEventHook('onFriendRequest', lambda from_id, msg: log.info("Friend request from {}".format(from_id)))
|
self._setEventHook('onFriendRequest', lambda from_id, msg: log.info("Friend request from {}".format(from_id)))
|
||||||
|
|
||||||
self._setEventHook('onInbox', lambda unseen, unread, recent_unread, msg: log.info('Inbox event: {}, {}, {}'.format(unseen, unread, recent_unread)))
|
self._setEventHook('onInbox', lambda unseen, unread, recent_unread, msg: log.info('Inbox event: {}, {}, {}'.format(unseen, unread, recent_unread)))
|
||||||
|
|
||||||
|
self._setEventHook('onQprimer', lambda made, msg: None)
|
||||||
|
|
||||||
|
|
||||||
self._setEventHook('onUnknownMesssageType', lambda msg: log.debug('Unknown message received: {}'.format(msg)))
|
self._setEventHook('onUnknownMesssageType', lambda msg: log.debug('Unknown message received: {}'.format(msg)))
|
||||||
|
|
||||||
@@ -447,12 +443,9 @@ class Client(object):
|
|||||||
"""
|
"""
|
||||||
Safely logs out the client
|
Safely logs out the client
|
||||||
|
|
||||||
.. todo::
|
|
||||||
Possibly check return parameter with _checkRequest, and the write documentation about the return
|
|
||||||
|
|
||||||
:param timeout: See `requests timeout <http://docs.python-requests.org/en/master/user/advanced/#timeouts>`_
|
:param timeout: See `requests timeout <http://docs.python-requests.org/en/master/user/advanced/#timeouts>`_
|
||||||
:return:
|
:return: True if the action was successful
|
||||||
:rtype:
|
:rtype: bool
|
||||||
"""
|
"""
|
||||||
data = {
|
data = {
|
||||||
'ref': "mb",
|
'ref': "mb",
|
||||||
@@ -466,7 +459,8 @@ class Client(object):
|
|||||||
self._session = requests.session()
|
self._session = requests.session()
|
||||||
self.req_counter = 1
|
self.req_counter = 1
|
||||||
self.seq = "0"
|
self.seq = "0"
|
||||||
return r
|
|
||||||
|
return r.ok
|
||||||
|
|
||||||
@deprecated(deprecated_in='0.10.2', removed_in='0.15.0', details='Use setDefaultThread instead')
|
@deprecated(deprecated_in='0.10.2', removed_in='0.15.0', details='Use setDefaultThread instead')
|
||||||
def setDefaultRecipient(self, recipient_id, is_user=True):
|
def setDefaultRecipient(self, recipient_id, is_user=True):
|
||||||
@@ -928,13 +922,13 @@ class Client(object):
|
|||||||
image_id = self._uploadImage(image_path, open(image_path, 'rb'), mimetype)
|
image_id = self._uploadImage(image_path, open(image_path, 'rb'), mimetype)
|
||||||
return self.sendImage(image_id=image_id, message=message, thread_id=thread_id, thread_type=thread_type)
|
return self.sendImage(image_id=image_id, message=message, thread_id=thread_id, thread_type=thread_type)
|
||||||
|
|
||||||
def addUsersToGroup(self, user_ids, thread_id=None):
|
def addUsersToGroup(self, *user_ids, thread_id=None):
|
||||||
"""
|
"""
|
||||||
Adds users to a group.
|
Adds users to a group.
|
||||||
|
|
||||||
:param user_ids: User ids to add
|
:param user_ids: User ids to add
|
||||||
:param thread_id: Group ID to add people to. See :ref:`intro_thread_id`
|
:param thread_id: Group ID to add people to. See :ref:`intro_thread_id`
|
||||||
:type user_ids: list
|
:type user_ids: list of positional arguments
|
||||||
:return: :ref:`Message ID <intro_message_ids>` of the sent "message"
|
:return: :ref:`Message ID <intro_message_ids>` of the sent "message"
|
||||||
"""
|
"""
|
||||||
thread_id, thread_type = self._getThread(thread_id, None)
|
thread_id, thread_type = self._getThread(thread_id, None)
|
||||||
@@ -986,29 +980,61 @@ class Client(object):
|
|||||||
def remove_user_from_chat(self, threadID, userID):
|
def remove_user_from_chat(self, threadID, userID):
|
||||||
return self.removeUserFromGroup(userID, thread_id=threadID)
|
return self.removeUserFromGroup(userID, thread_id=threadID)
|
||||||
|
|
||||||
@deprecated(deprecated_in='0.10.2', removed_in='0.15.0', details='Use changeGroupTitle() instead')
|
def changeThreadTitle(self, title, thread_id=None, thread_type=ThreadType.USER):
|
||||||
def changeThreadTitle(self, threadID, newTitle):
|
|
||||||
return self.changeGroupTitle(newTitle, thread_id=threadID)
|
|
||||||
|
|
||||||
def changeGroupTitle(self, title, thread_id=None):
|
|
||||||
"""
|
"""
|
||||||
Changes title of a group conversation.
|
Changes title of a thread
|
||||||
|
|
||||||
.. todo::
|
|
||||||
Check whether this can work on group threads, and if it does, change it (back) to changeThreadTitle
|
|
||||||
|
|
||||||
:param title: New group chat title
|
:param title: New group chat title
|
||||||
:param thread_id: Group ID to change title of. See :ref:`intro_thread_id`
|
:param thread_id: Group ID to change title of. See :ref:`intro_thread_id`
|
||||||
:return: :ref:`Message ID <intro_message_ids>` of the sent "message"
|
:param thread_type: See :ref:`intro_thread_type`
|
||||||
|
:type thread_type: models.ThreadType
|
||||||
|
:return: True if the action was successful
|
||||||
|
:rtype: bool
|
||||||
"""
|
"""
|
||||||
thread_id, thread_type = self._getThread(thread_id, None)
|
# For backwards compatability. Previously `thread_id` and `title` were swapped around
|
||||||
data = self._getSendData(thread_id, ThreadType.GROUP)
|
try:
|
||||||
|
int(thread_id)
|
||||||
|
except ValueError:
|
||||||
|
deprecation('changeThreadTitle(threadID, newTitle)', deprecated_in='0.10.2', removed_in='0.15.0', details='Use changeThreadTitle(title, thread_id, thread_type) instead')
|
||||||
|
title, thread_id, thread_type = thread_id, title, ThreadType.GROUP
|
||||||
|
|
||||||
data['action_type'] = 'ma-type:log-message'
|
thread_id, thread_type = self._getThread(thread_id, thread_type)
|
||||||
data['log_message_data[name]'] = title
|
|
||||||
data['log_message_type'] = 'log:thread-name'
|
|
||||||
|
|
||||||
return self._doSendRequest(data)
|
if thread_type == ThreadType.USER:
|
||||||
|
# The thread is a user, so we change the user's nickname
|
||||||
|
return self.changeNickname(title, thread_id, thread_id=thread_id, thread_type=thread_type)
|
||||||
|
else:
|
||||||
|
data = self._getSendData(thread_id, thread_type)
|
||||||
|
|
||||||
|
data['action_type'] = 'ma-type:log-message'
|
||||||
|
data['log_message_data[name]'] = title
|
||||||
|
data['log_message_type'] = 'log:thread-name'
|
||||||
|
|
||||||
|
return False if self._doSendRequest(data) is None else True
|
||||||
|
|
||||||
|
def changeNickname(self, nickname, user_id, thread_id=None, thread_type=ThreadType.USER):
|
||||||
|
"""
|
||||||
|
Changes the nickname of a user in a thread
|
||||||
|
|
||||||
|
:param nickname: New nickname
|
||||||
|
:param user_id: User that will have their nickname changed
|
||||||
|
:param thread_id: User/Group ID to change color of. See :ref:`intro_thread_id`
|
||||||
|
:param thread_type: See :ref:`intro_thread_type`
|
||||||
|
:type thread_type: models.ThreadType
|
||||||
|
:return: True if the action was successful
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
thread_id, thread_type = self._getThread(thread_id, thread_type)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'nickname': nickname,
|
||||||
|
'participant_id': user_id,
|
||||||
|
'thread_or_other_fbid': thread_id
|
||||||
|
}
|
||||||
|
|
||||||
|
j = self._checkRequest(self._post(ReqUrl.THREAD_NICKNAME, data))
|
||||||
|
|
||||||
|
return False if j is None else True
|
||||||
|
|
||||||
def changeThreadColor(self, color, thread_id=None):
|
def changeThreadColor(self, color, thread_id=None):
|
||||||
"""
|
"""
|
||||||
@@ -1017,19 +1043,42 @@ class Client(object):
|
|||||||
:param color: New thread color
|
:param color: New thread color
|
||||||
:param thread_id: User/Group ID to change color of. See :ref:`intro_thread_id`
|
:param thread_id: User/Group ID to change color of. See :ref:`intro_thread_id`
|
||||||
:type color: models.ThreadColor
|
:type color: models.ThreadColor
|
||||||
:return: (*bool*) True if the action was successful
|
:return: True if the action was successful
|
||||||
|
:rtype: bool
|
||||||
"""
|
"""
|
||||||
thread_id, thread_type = self._getThread(thread_id, None)
|
thread_id, thread_type = self._getThread(thread_id, None)
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"color_choice": color.value,
|
'color_choice': color.value,
|
||||||
"thread_or_other_fbid": thread_id
|
'thread_or_other_fbid': thread_id
|
||||||
}
|
}
|
||||||
|
|
||||||
j = self._checkRequest(self._post(ReqUrl.THREAD_COLOR, data))
|
j = self._checkRequest(self._post(ReqUrl.THREAD_COLOR, data))
|
||||||
|
|
||||||
return False if j is None else True
|
return False if j is None else True
|
||||||
|
|
||||||
|
def changeThreadEmoji(self, emoji, thread_id=None):
|
||||||
|
"""
|
||||||
|
Changes thread color
|
||||||
|
|
||||||
|
Trivia: While changing the emoji, the Facebook web client actually sends multiple different requests, though only this one is required to make the change
|
||||||
|
|
||||||
|
:param color: New thread emoji
|
||||||
|
:param thread_id: User/Group ID to change emoji of. See :ref:`intro_thread_id`
|
||||||
|
:return: True if the action was successful
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
thread_id, thread_type = self._getThread(thread_id, None)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'emoji_choice': emoji,
|
||||||
|
'thread_or_other_fbid': thread_id
|
||||||
|
}
|
||||||
|
|
||||||
|
j = self._checkRequest(self._post(ReqUrl.THREAD_EMOJI, data))
|
||||||
|
|
||||||
|
return False if j is None else True
|
||||||
|
|
||||||
def reactToMessage(self, message_id, reaction):
|
def reactToMessage(self, message_id, reaction):
|
||||||
"""
|
"""
|
||||||
Reacts to a message.
|
Reacts to a message.
|
||||||
@@ -1280,7 +1329,7 @@ class Client(object):
|
|||||||
thread_id, thread_type = getThreadIdAndThreadType(metadata)
|
thread_id, thread_type = getThreadIdAndThreadType(metadata)
|
||||||
self.onNicknameChange(mid=mid, author_id=author_id, changed_for=changed_for,
|
self.onNicknameChange(mid=mid, author_id=author_id, changed_for=changed_for,
|
||||||
new_nickname=new_nickname,
|
new_nickname=new_nickname,
|
||||||
thread_id=thread_id, thread_type=thread_type, ts=ts, metadata=metadata)
|
thread_id=thread_id, thread_type=thread_type, ts=ts, metadata=metadata, msg=m)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Message delivered
|
# Message delivered
|
||||||
@@ -1347,7 +1396,7 @@ class Client(object):
|
|||||||
|
|
||||||
# Happens on every login
|
# Happens on every login
|
||||||
elif mtype == "qprimer":
|
elif mtype == "qprimer":
|
||||||
pass
|
self.onQprimer(made=m.get("made"), msg=m)
|
||||||
|
|
||||||
# Is sent before any other message
|
# Is sent before any other message
|
||||||
elif mtype == "deltaflow":
|
elif mtype == "deltaflow":
|
||||||
|
@@ -57,6 +57,8 @@ class ReqUrl(object):
|
|||||||
SAVE_DEVICE = "https://m.facebook.com/login/save-device/cancel/"
|
SAVE_DEVICE = "https://m.facebook.com/login/save-device/cancel/"
|
||||||
CHECKPOINT = "https://m.facebook.com/login/checkpoint/"
|
CHECKPOINT = "https://m.facebook.com/login/checkpoint/"
|
||||||
THREAD_COLOR = "https://www.facebook.com/messaging/save_thread_color/?source=thread_settings&dpr=1"
|
THREAD_COLOR = "https://www.facebook.com/messaging/save_thread_color/?source=thread_settings&dpr=1"
|
||||||
|
THREAD_NICKNAME = "https://www.facebook.com/messaging/save_thread_nickname/?source=thread_settings&dpr=1"
|
||||||
|
THREAD_EMOJI = "https://www.facebook.com/messaging/save_thread_emoji/?source=thread_settings&dpr=1"
|
||||||
MESSAGE_REACTION = "https://www.facebook.com/webgraphql/mutation"
|
MESSAGE_REACTION = "https://www.facebook.com/webgraphql/mutation"
|
||||||
TYPING = "https://www.facebook.com/ajax/messaging/typ.php"
|
TYPING = "https://www.facebook.com/ajax/messaging/typ.php"
|
||||||
|
|
||||||
|
93
tests.py
93
tests.py
@@ -7,13 +7,33 @@ import logging
|
|||||||
import unittest
|
import unittest
|
||||||
from getpass import getpass
|
from getpass import getpass
|
||||||
from sys import argv
|
from sys import argv
|
||||||
from os import path
|
from os import path, chdir
|
||||||
|
from glob import glob
|
||||||
from fbchat import Client
|
from fbchat import Client
|
||||||
from fbchat.models import *
|
from fbchat.models import *
|
||||||
|
import py_compile
|
||||||
|
|
||||||
logging_level = logging.ERROR
|
logging_level = logging.ERROR
|
||||||
|
|
||||||
|
class CustomClient(Client):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.got_qprimer = False
|
||||||
|
super(type(self), self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def onQprimer(self, made, msg):
|
||||||
|
self.got_qprimer = True
|
||||||
|
|
||||||
class TestFbchat(unittest.TestCase):
|
class TestFbchat(unittest.TestCase):
|
||||||
|
def test_examples(self):
|
||||||
|
# Checks for syntax errors in the examples
|
||||||
|
chdir('examples')
|
||||||
|
for f in glob('*.txt'):
|
||||||
|
print(f)
|
||||||
|
with self.assertRaises(py_compile.PyCompileError):
|
||||||
|
py_compile.compile(f)
|
||||||
|
|
||||||
|
chdir('..')
|
||||||
|
|
||||||
def test_loginFunctions(self):
|
def test_loginFunctions(self):
|
||||||
self.assertTrue(client.isLoggedIn())
|
self.assertTrue(client.isLoggedIn())
|
||||||
|
|
||||||
@@ -31,13 +51,16 @@ class TestFbchat(unittest.TestCase):
|
|||||||
def test_sessions(self):
|
def test_sessions(self):
|
||||||
global client
|
global client
|
||||||
session_cookies = client.getSession()
|
session_cookies = client.getSession()
|
||||||
client = Client(email, password, session_cookies=session_cookies, logging_level=logging_level)
|
client = CustomClient(email, password, session_cookies=session_cookies, logging_level=logging_level)
|
||||||
|
|
||||||
self.assertTrue(client.isLoggedIn())
|
self.assertTrue(client.isLoggedIn())
|
||||||
|
|
||||||
def test_defaultThread(self):
|
def test_defaultThread(self):
|
||||||
# setDefaultThread
|
# setDefaultThread
|
||||||
client.setDefaultThread(client.uid, ThreadType.USER)
|
client.setDefaultThread(group_uid, ThreadType.GROUP)
|
||||||
|
self.assertTrue(client.sendMessage('test_default_recipient★'))
|
||||||
|
|
||||||
|
client.setDefaultThread(user_uid, ThreadType.USER)
|
||||||
self.assertTrue(client.sendMessage('test_default_recipient★'))
|
self.assertTrue(client.sendMessage('test_default_recipient★'))
|
||||||
|
|
||||||
# resetDefaultThread
|
# resetDefaultThread
|
||||||
@@ -50,7 +73,7 @@ class TestFbchat(unittest.TestCase):
|
|||||||
self.assertGreater(len(users), 0)
|
self.assertGreater(len(users), 0)
|
||||||
|
|
||||||
def test_getUsers(self):
|
def test_getUsers(self):
|
||||||
users = client.getUsers("Mark Zuckerberg")
|
users = client.getUsers('Mark Zuckerberg')
|
||||||
self.assertGreater(len(users), 0)
|
self.assertGreater(len(users), 0)
|
||||||
|
|
||||||
u = users[0]
|
u = users[0]
|
||||||
@@ -66,11 +89,11 @@ class TestFbchat(unittest.TestCase):
|
|||||||
def test_sendEmoji(self):
|
def test_sendEmoji(self):
|
||||||
self.assertTrue(client.sendEmoji(size=EmojiSize.SMALL, thread_id=user_uid, thread_type=ThreadType.USER))
|
self.assertTrue(client.sendEmoji(size=EmojiSize.SMALL, thread_id=user_uid, thread_type=ThreadType.USER))
|
||||||
self.assertTrue(client.sendEmoji(size=EmojiSize.MEDIUM, thread_id=user_uid, thread_type=ThreadType.USER))
|
self.assertTrue(client.sendEmoji(size=EmojiSize.MEDIUM, thread_id=user_uid, thread_type=ThreadType.USER))
|
||||||
self.assertTrue(client.sendEmoji("😆", EmojiSize.LARGE, user_uid, ThreadType.USER))
|
self.assertTrue(client.sendEmoji('😆', EmojiSize.LARGE, user_uid, ThreadType.USER))
|
||||||
|
|
||||||
self.assertTrue(client.sendEmoji(size=EmojiSize.SMALL, thread_id=group_uid, thread_type=ThreadType.GROUP))
|
self.assertTrue(client.sendEmoji(size=EmojiSize.SMALL, thread_id=group_uid, thread_type=ThreadType.GROUP))
|
||||||
self.assertTrue(client.sendEmoji(size=EmojiSize.MEDIUM, thread_id=group_uid, thread_type=ThreadType.GROUP))
|
self.assertTrue(client.sendEmoji(size=EmojiSize.MEDIUM, thread_id=group_uid, thread_type=ThreadType.GROUP))
|
||||||
self.assertTrue(client.sendEmoji("😆", EmojiSize.LARGE, group_uid, ThreadType.GROUP))
|
self.assertTrue(client.sendEmoji('😆', EmojiSize.LARGE, group_uid, ThreadType.GROUP))
|
||||||
|
|
||||||
def test_sendMessage(self):
|
def test_sendMessage(self):
|
||||||
self.assertIsNotNone(client.sendMessage('test_send_user★', user_uid, ThreadType.USER))
|
self.assertIsNotNone(client.sendMessage('test_send_user★', user_uid, ThreadType.USER))
|
||||||
@@ -81,42 +104,54 @@ class TestFbchat(unittest.TestCase):
|
|||||||
def test_sendImages(self):
|
def test_sendImages(self):
|
||||||
image_url = 'https://cdn4.iconfinder.com/data/icons/ionicons/512/icon-image-128.png'
|
image_url = 'https://cdn4.iconfinder.com/data/icons/ionicons/512/icon-image-128.png'
|
||||||
image_local_url = path.join(path.dirname(__file__), 'tests/image.png')
|
image_local_url = path.join(path.dirname(__file__), 'tests/image.png')
|
||||||
#self.assertTrue(client.sendRemoteImage(image_url, 'test_send_user_images_remote', user_uid, ThreadType.USER))
|
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))
|
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', user_uid, ThreadType.USER))
|
|
||||||
self.assertTrue(client.sendLocalImage(image_local_url, 'test_send_group_images_local★', group_uid, ThreadType.GROUP))
|
self.assertTrue(client.sendLocalImage(image_local_url, 'test_send_group_images_local★', group_uid, ThreadType.GROUP))
|
||||||
|
|
||||||
def test_getThreadInfo(self):
|
def test_getThreadInfo(self):
|
||||||
client.sendMessage('test_user_getThreadInfo★', user_uid, ThreadType.USER)
|
client.sendMessage('test_user_getThreadInfo★', user_uid, ThreadType.USER)
|
||||||
|
|
||||||
info = client.getThreadInfo(20, user_uid, ThreadType.USER)
|
info = client.getThreadInfo(20, user_uid, ThreadType.USER)
|
||||||
self.assertEqual(info[0].author, 'fbid:' + client.uid)
|
self.assertEqual(info[0].author, 'fbid:' + client.uid)
|
||||||
self.assertEqual(info[0].body, 'test_user_getThreadInfo★')
|
self.assertEqual(info[0].body, 'test_user_getThreadInfo★')
|
||||||
|
|
||||||
client.sendMessage('test_group_getThreadInfo★', group_uid, ThreadType.GROUP)
|
client.sendMessage('test_group_getThreadInfo★', group_uid, ThreadType.GROUP)
|
||||||
|
|
||||||
info = client.getThreadInfo(20, group_uid, ThreadType.GROUP)
|
info = client.getThreadInfo(20, group_uid, ThreadType.GROUP)
|
||||||
self.assertEqual(info[0].author, 'fbid:' + client.uid)
|
self.assertEqual(info[0].author, 'fbid:' + client.uid)
|
||||||
self.assertEqual(info[0].body, 'test_group_getThreadInfo★')
|
self.assertEqual(info[0].body, 'test_group_getThreadInfo★')
|
||||||
|
|
||||||
def test_markAs(self):
|
|
||||||
# To be implemented (requires some form of manual watching)
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_listen(self):
|
def test_listen(self):
|
||||||
|
client.startListening()
|
||||||
client.doOneListen()
|
client.doOneListen()
|
||||||
|
client.stopListening()
|
||||||
|
|
||||||
|
self.assertTrue(client.got_qprimer)
|
||||||
|
|
||||||
def test_getUserInfo(self):
|
def test_getUserInfo(self):
|
||||||
info = client.getUserInfo(4)
|
info = client.getUserInfo(4)
|
||||||
self.assertEqual(info['name'], 'Mark Zuckerberg')
|
self.assertEqual(info['name'], 'Mark Zuckerberg')
|
||||||
|
|
||||||
def test_removeAddFromGroup(self):
|
def test_removeAddFromGroup(self):
|
||||||
self.assertTrue(client.removeUserFromGroup(user_uid, group_uid))
|
self.assertTrue(client.removeUserFromGroup(user_uid, thread_id=group_uid))
|
||||||
self.assertTrue(client.addUsersToGroup([user_uid], group_uid))
|
self.assertTrue(client.addUsersToGroup(user_uid, thread_id=group_uid))
|
||||||
|
|
||||||
def test_changeGroupTitle(self):
|
def test_changeThreadTitle(self):
|
||||||
self.assertTrue(client.changeGroupTitle('test_changeGroupTitle★', group_uid))
|
self.assertTrue(client.changeThreadTitle('test_changeThreadTitle★', thread_id=group_uid, thread_type=ThreadType.GROUP))
|
||||||
|
self.assertTrue(client.changeThreadTitle('test_changeThreadTitle★', thread_id=user_uid, thread_type=ThreadType.USER))
|
||||||
|
|
||||||
|
def test_changeNickname(self):
|
||||||
|
self.assertTrue(client.changeNickname('test_changeNicknameSelf★', client.uid, thread_id=user_uid, thread_type=ThreadType.USER))
|
||||||
|
self.assertTrue(client.changeNickname('test_changeNicknameOther★', user_uid, thread_id=user_uid, thread_type=ThreadType.USER))
|
||||||
|
self.assertTrue(client.changeNickname('test_changeNicknameSelf★', client.uid, thread_id=group_uid, thread_type=ThreadType.GROUP))
|
||||||
|
self.assertTrue(client.changeNickname('test_changeNicknameOther★', user_uid, thread_id=group_uid, thread_type=ThreadType.GROUP))
|
||||||
|
|
||||||
|
def test_changeThreadEmoji(self):
|
||||||
|
self.assertTrue(client.changeThreadEmoji('😀', group_uid))
|
||||||
|
self.assertTrue(client.changeThreadEmoji('😀', user_uid))
|
||||||
|
self.assertTrue(client.changeThreadEmoji('😆', group_uid))
|
||||||
|
self.assertTrue(client.changeThreadEmoji('😆', user_uid))
|
||||||
|
|
||||||
def test_changeThreadColor(self):
|
def test_changeThreadColor(self):
|
||||||
self.assertTrue(client.changeThreadColor(ThreadColor.BRILLIANT_ROSE, group_uid))
|
self.assertTrue(client.changeThreadColor(ThreadColor.BRILLIANT_ROSE, group_uid))
|
||||||
@@ -125,13 +160,16 @@ class TestFbchat(unittest.TestCase):
|
|||||||
self.assertTrue(client.changeThreadColor(ThreadColor.MESSENGER_BLUE, user_uid))
|
self.assertTrue(client.changeThreadColor(ThreadColor.MESSENGER_BLUE, user_uid))
|
||||||
|
|
||||||
def test_reactToMessage(self):
|
def test_reactToMessage(self):
|
||||||
mid = client.sendMessage('react_to_message', user_uid, ThreadType.USER)
|
mid = client.sendMessage('test_reactToMessage★', user_uid, ThreadType.USER)
|
||||||
self.assertTrue(client.reactToMessage(mid, MessageReaction.LOVE))
|
self.assertTrue(client.reactToMessage(mid, MessageReaction.LOVE))
|
||||||
|
mid = client.sendMessage('test_reactToMessage★', group_uid, ThreadType.GROUP)
|
||||||
|
self.assertTrue(client.reactToMessage(mid, MessageReaction.LOVE))
|
||||||
|
|
||||||
def test_setTypingStatus(self):
|
def test_setTypingStatus(self):
|
||||||
|
self.assertTrue(client.sendMessage('Hi', thread_id=user_uid, thread_type=ThreadType.USER))
|
||||||
self.assertTrue(client.setTypingStatus(TypingStatus.TYPING, thread_id=user_uid, thread_type=ThreadType.USER))
|
self.assertTrue(client.setTypingStatus(TypingStatus.TYPING, thread_id=user_uid, thread_type=ThreadType.USER))
|
||||||
self.assertTrue(client.setTypingStatus(TypingStatus.STOPPED, thread_id=user_uid, thread_type=ThreadType.USER))
|
|
||||||
self.assertTrue(client.setTypingStatus(TypingStatus.TYPING, thread_id=group_uid, thread_type=ThreadType.GROUP))
|
self.assertTrue(client.setTypingStatus(TypingStatus.TYPING, thread_id=group_uid, thread_type=ThreadType.GROUP))
|
||||||
|
self.assertTrue(client.setTypingStatus(TypingStatus.STOPPED, thread_id=user_uid, thread_type=ThreadType.USER))
|
||||||
self.assertTrue(client.setTypingStatus(TypingStatus.STOPPED, thread_id=group_uid, thread_type=ThreadType.GROUP))
|
self.assertTrue(client.setTypingStatus(TypingStatus.STOPPED, thread_id=group_uid, thread_type=ThreadType.GROUP))
|
||||||
|
|
||||||
|
|
||||||
@@ -143,8 +181,8 @@ def start_test(param_client, param_group_uid, param_user_uid, tests=[]):
|
|||||||
client = param_client
|
client = param_client
|
||||||
group_uid = param_group_uid
|
group_uid = param_group_uid
|
||||||
user_uid = param_user_uid
|
user_uid = param_user_uid
|
||||||
|
|
||||||
tests = ['test_' + test for test in tests]
|
tests = ['test_' + test if 'test_' != test[:5] else test for test in tests]
|
||||||
|
|
||||||
if len(tests) == 0:
|
if len(tests) == 0:
|
||||||
suite = unittest.TestLoader().loadTestsFromTestCase(TestFbchat)
|
suite = unittest.TestLoader().loadTestsFromTestCase(TestFbchat)
|
||||||
@@ -175,10 +213,9 @@ if __name__ == '__main__':
|
|||||||
password = getpass()
|
password = getpass()
|
||||||
group_uid = input('Please enter a group thread id (To test group functionality): ')
|
group_uid = input('Please enter a group thread id (To test group functionality): ')
|
||||||
user_uid = input('Please enter a user thread id (To test kicking/adding functionality): ')
|
user_uid = input('Please enter a user thread id (To test kicking/adding functionality): ')
|
||||||
|
|
||||||
print('Logging in...')
|
print('Logging in...')
|
||||||
client = Client(email, password, logging_level=logging_level)
|
client = CustomClient(email, password, logging_level=logging_level)
|
||||||
|
|
||||||
# Warning! Taking user input directly like this could be dangerous! Use only for testing purposes!
|
# Warning! Taking user input directly like this could be dangerous! Use only for testing purposes!
|
||||||
start_test(client, group_uid, user_uid, argv[1:])
|
start_test(client, group_uid, user_uid, argv[1:])
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user