Removed deprecations and new event system, improved other things
Removed deprecations Removed new event system Added documentation for all events Added FAQ Changed Client.uid to Client.id Improved User model Prepared for support of pages
This commit is contained in:
@@ -16,15 +16,9 @@ Client
|
||||
This is the main class of `fbchat`, which contains all the methods you use to interract with Facebook.
|
||||
You can extend this class, and overwrite the events, to provide custom event handling (mainly used while listening)
|
||||
|
||||
.. todo::
|
||||
Add documentation for all events
|
||||
|
||||
.. autoclass:: Client(email, password, user_agent=None, max_retries=5, session_cookies=None, logging_level=logging.INFO)
|
||||
.. autoclass:: Client(email, password, user_agent=None, max_tries=5, session_cookies=None, logging_level=logging.INFO)
|
||||
:members:
|
||||
|
||||
.. automethod:: sendRemoteImage(image_url, message=None, thread_id=None, thread_type=ThreadType.USER)
|
||||
.. automethod:: sendLocalImage(image_path, message=None, thread_id=None, thread_type=ThreadType.USER)
|
||||
|
||||
|
||||
.. _api_models:
|
||||
|
||||
|
@@ -7,6 +7,14 @@ Examples
|
||||
These are a few examples on how to use `fbchat`. Remember to swap out `<email>` and `<password>` for your email and password
|
||||
|
||||
|
||||
Basic example
|
||||
-------------
|
||||
|
||||
This will show basic usage of `fbchat`
|
||||
|
||||
.. literalinclude:: ../examples/basic_usage.py
|
||||
|
||||
|
||||
Interacting with Threads
|
||||
------------------------
|
||||
|
||||
|
44
docs/faq.rst
Normal file
44
docs/faq.rst
Normal file
@@ -0,0 +1,44 @@
|
||||
.. highlight:: python
|
||||
.. module:: fbchat
|
||||
.. _faq:
|
||||
|
||||
Frequently asked questions
|
||||
==========================
|
||||
|
||||
Version X broke my installation
|
||||
-------------------------------
|
||||
|
||||
We try to provide backwards compatability where possible, but since we're not part of Facebook,
|
||||
most of the things may be broken at any point in time
|
||||
|
||||
Downgrade to an earlier version of fbchat, run this command
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ pip install fbchat==<X>
|
||||
|
||||
Where you replace ``<X>`` with the version you want to use
|
||||
|
||||
|
||||
Will you be supporting creating posts/events/pages and so on?
|
||||
-------------------------------------------------------------
|
||||
|
||||
We won't be focusing on anything else than chat-related things. This API is called `fbCHAT`, after all ;)
|
||||
|
||||
|
||||
Submitting Issues
|
||||
-----------------
|
||||
|
||||
If you're having trouble with some of the snippets, or you think some of the functionality is broken,
|
||||
please feel free to submit an issue on `Github <https://github.com/carpedm20/fbchat>`_.
|
||||
You should first login with ``logging_level`` set to ``logging.DEBUG``::
|
||||
|
||||
from fbchat import Client
|
||||
import logging
|
||||
client = Client('<email>', '<password>', logging_level=logging.DEBUG)
|
||||
|
||||
Then you can submit the relevant parts of this log, and detailed steps on how to reproduce
|
||||
|
||||
.. warning::
|
||||
Always remove your credentials from any debug information you may provide us.
|
||||
Preferably, use a test account, in case you miss anything
|
@@ -36,6 +36,9 @@ Currently `fbchat` support Python 2.7, 3.4, 3.5 and 3.6:
|
||||
This means doing the exact same GET/POST requests and tricking Facebook into thinking it's accessing the website normally.
|
||||
Therefore, this API requires the credentials of a Facebook account.
|
||||
|
||||
.. note::
|
||||
If you're having problems, please check the :ref:`faq`, before asking questions on Github
|
||||
|
||||
.. warning::
|
||||
We are not responsible if your account gets banned for spammy activities,
|
||||
such as sending lots of messages to people you don't know, sending messages very quickly,
|
||||
@@ -60,3 +63,4 @@ Overview
|
||||
testing
|
||||
api
|
||||
todo
|
||||
faq
|
||||
|
@@ -53,9 +53,9 @@ These will specify whether the thread is a single user chat or a group chat.
|
||||
This is required for many of `fbchat`'s functions, since Facebook differetiates between these two internally
|
||||
|
||||
Searching for group chats and finding their ID is not yet possible with `fbchat`,
|
||||
but searching for users is possible via. :func:`Client.getUsers`. See :ref:`intro_fetching`
|
||||
but searching for users is possible via. :func:`Client.searchForUsers`. See :ref:`intro_fetching`
|
||||
|
||||
You can get your own user ID by using :any:`Client.uid`
|
||||
You can get your own user ID by using :any:`Client.id`
|
||||
|
||||
Getting the ID of a group chat is fairly trivial though, since you only need to navigate to `<https://www.facebook.com/messages/>`_,
|
||||
click on the group you want to find the ID of, and then read the id from the address bar.
|
||||
@@ -108,7 +108,7 @@ like adding users to and removing users from a group chat, logically only works
|
||||
The simplest way of using `fbchat` is to send a message.
|
||||
The following snippet will, as you've probably already figured out, send the message `test message` to your account::
|
||||
|
||||
message_id = client.sendMessage('test message', thread_id=client.uid, thread_type=ThreadType.USER)
|
||||
message_id = client.sendMessage('test message', thread_id=client.id, thread_type=ThreadType.USER)
|
||||
|
||||
You can see a full example showing all the possible thread interactions with `fbchat` by going to :ref:`examples`
|
||||
|
||||
@@ -120,12 +120,12 @@ Fetching Information
|
||||
|
||||
You can use `fbchat` to fetch basic information like user names, profile pictures, thread names and user IDs
|
||||
|
||||
You can retrieve a user's ID with :func:`Client.getUsers`.
|
||||
You can retrieve a user's ID with :func:`Client.searchForUsers`.
|
||||
The following snippet will search for users by their name, take the first (and most likely) user, and then get their user ID from the result::
|
||||
|
||||
users = client.getUsers('<name of user>')
|
||||
users = client.searchForUsers('<name of user>')
|
||||
user = users[0]
|
||||
print("User's ID: {}".format(user.uid))
|
||||
print("User's ID: {}".format(user.id))
|
||||
print("User's name: {}".format(user.name))
|
||||
print("User's profile picture url: {}".format(user.photo))
|
||||
print("User's main url: {}".format(user.url))
|
||||
@@ -198,23 +198,3 @@ and ``mid``, ``ts``, ``metadata`` and ``msg`` got removed, but the function stil
|
||||
the API actually requires that you include ``**kwargs`` as your final argument.
|
||||
|
||||
View the :ref:`examples` to see some more examples illustrating the event system
|
||||
|
||||
|
||||
.. _intro_submitting:
|
||||
|
||||
Submitting Issues
|
||||
-----------------
|
||||
|
||||
If you're having trouble with some of the snippets shown here, or you think some of the functionality is broken,
|
||||
please feel free to submit an issue on `Github <https://github.com/carpedm20/fbchat>`_.
|
||||
One side note is that you should first login with ``logging_level`` set to ``logging.DEBUG``::
|
||||
|
||||
from fbchat import Client
|
||||
import logging
|
||||
client = Client('<email>', '<password>', logging_level=logging.DEBUG)
|
||||
|
||||
Then you can submit the relevant parts of this log, and detailed steps on how to reproduce
|
||||
|
||||
.. warning::
|
||||
Always remove your credentials from any debug information you may provide us.
|
||||
Preferably, use a test account, in case you miss anything
|
||||
|
@@ -17,7 +17,7 @@ Missing Functionality
|
||||
- This will use the graphql request API
|
||||
- Implement chatting with pages
|
||||
- This might require a new :class:`models.ThreadType`, something like ``ThreadType.PAGE``
|
||||
- Rework `User`, `Thread` and `Message` models, and rework fething methods, to make the whole process more streamlined
|
||||
- Rework `Message` model, to make the whole process more streamlined
|
||||
|
||||
|
||||
Documentation
|
||||
|
12
examples/basic_usage.py
Normal file
12
examples/basic_usage.py
Normal file
@@ -0,0 +1,12 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
from fbchat import Client
|
||||
from fbchat.models import *
|
||||
|
||||
client = Client('<email>', '<password>')
|
||||
|
||||
print('Own id: {}'.format(client.id))
|
||||
|
||||
client.sendMessage('Hi me!', thread_id=self.id, thread_type=ThreadType.USER)
|
||||
|
||||
client.logout()
|
@@ -3,42 +3,44 @@
|
||||
from fbchat import Client
|
||||
from fbchat.models import *
|
||||
|
||||
client = Client("<email>", "<password>")
|
||||
client = Client('<email>', '<password>')
|
||||
|
||||
# Fetches a list of all users you're currently chatting with, as `User` objects
|
||||
users = client.getAllUsers()
|
||||
users = client.fetchAllUsers()
|
||||
|
||||
print('user IDs: {}'.format(user.uid for user in users))
|
||||
print("user's names: {}".format(user.name for user in users))
|
||||
print("users' IDs: {}".format(user.uid for user in users))
|
||||
print("users' names: {}".format(user.name for user in users))
|
||||
|
||||
|
||||
# If we have a user id, we can use `getUserInfo` to fetch a `User` object
|
||||
user = client.getUserInfo('<user id>')
|
||||
user = client.fetchUserInfo('<user id>')['<user id>']
|
||||
# We can also query both mutiple users together, which returns list of `User` objects
|
||||
users = client.getUserInfo('<1st user id>', '<2nd user id>', '<3rd user id>')
|
||||
users = client.fetchUserInfo('<1st user id>', '<2nd user id>', '<3rd user id>')
|
||||
|
||||
print('User INFO: {}'.format(user))
|
||||
print("User's INFO: {}".format(users))
|
||||
print("user's name: {}".format(user.name))
|
||||
print("users' names: {}".format(users[k].name for k in users))
|
||||
|
||||
|
||||
# `getUsers` searches for the user and gives us a list of the results,
|
||||
# `searchForUsers` searches for the user and gives us a list of the results,
|
||||
# and then we just take the first one, aka. the most likely one:
|
||||
user = client.getUsers('<name of user>')[0]
|
||||
user = client.searchForUsers('<name of user>')[0]
|
||||
|
||||
print('user ID: {}'.format(user.uid))
|
||||
print("user's name: {}".format(user.name))
|
||||
print("user's photo: {}".format(user.photo))
|
||||
print("Is user client's friend: {}".format(user.is_friend))
|
||||
|
||||
|
||||
# Fetches a list of all threads you're currently chatting with
|
||||
threads = client.getThreadList()
|
||||
# Fetches a list of the 20 top threads you're currently chatting with
|
||||
threads = client.fetchThreadList()
|
||||
# Fetches the next 10 threads
|
||||
threads += client.getThreadList(start=20, length=10)
|
||||
threads += client.fetchThreadList(offset=20, amount=10)
|
||||
|
||||
print("Thread's INFO: {}".format(threads))
|
||||
|
||||
|
||||
# Gets the last 10 messages sent to the thread
|
||||
messages = client.getThreadInfo(last_n=10, thread_id='<thread id>', thread_type=ThreadType)
|
||||
messages = client.fetchThreadMessages(offset=0, amount=10, thread_id='<thread id>', thread_type=ThreadType)
|
||||
# Since the message come in reversed order, reverse them
|
||||
messages.reverse()
|
||||
|
||||
|
@@ -7,10 +7,11 @@ class RemoveBot(Client):
|
||||
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
|
||||
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(author_id, thread_id=thread_id)
|
||||
else:
|
||||
log.info("Message from {} in {} ({}): {}".format(author_id, thread_id, thread_type.name, message))
|
||||
# Sends the data to the inherited onMessage, so that we can still see when a message is recieved
|
||||
super(type(self), self).onMessage(author_id=author_id, message=message, thread_id=thread_id, thread_type=thread_type, **kwargs)
|
||||
|
||||
client = RemoveBot("<email>", "<password>")
|
||||
client.listen()
|
||||
|
@@ -14,7 +14,7 @@ from datetime import datetime
|
||||
from .client import *
|
||||
|
||||
__copyright__ = 'Copyright 2015 - {} by Taehoon Kim'.format(datetime.now().year)
|
||||
__version__ = '0.10.4'
|
||||
__version__ = '1.0.0'
|
||||
__license__ = 'BSD'
|
||||
__author__ = 'Taehoon Kim; Moreels Pieter-Jan; Mads Marquart'
|
||||
__email__ = 'carpedm20@gmail.com'
|
||||
|
978
fbchat/client.py
978
fbchat/client.py
File diff suppressed because it is too large
Load Diff
@@ -1,28 +0,0 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
class EventHook(object):
|
||||
"""
|
||||
A simple implementation of the Observer-Pattern.
|
||||
All listeners added to this will be called, regardless of parameters
|
||||
"""
|
||||
|
||||
def __init__(self, *args):
|
||||
self._handlers = list(args)
|
||||
|
||||
def add(self, handler):
|
||||
return self.__iadd__(handler)
|
||||
|
||||
def remove(self, handler):
|
||||
return self.__isub__(handler)
|
||||
|
||||
def __iadd__(self, handler):
|
||||
self._handlers.append(handler)
|
||||
return self
|
||||
|
||||
def __isub__(self, handler):
|
||||
self._handlers.remove(handler)
|
||||
return self
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
for handler in self._handlers:
|
||||
handler(*args, **kwargs)
|
118
fbchat/models.py
118
fbchat/models.py
@@ -3,81 +3,76 @@
|
||||
from __future__ import unicode_literals
|
||||
import enum
|
||||
|
||||
class User(object):
|
||||
"""Represents a Facebook User"""
|
||||
|
||||
class Thread(object):
|
||||
#: The unique identifier of the user. Can be used a `thread_id`. See :ref:`intro_threads` for more info
|
||||
uid = None
|
||||
#: Currently always set to `user`. Might change in the future
|
||||
type = 'user'
|
||||
#: The profile picture of the user
|
||||
id = None
|
||||
#: Specifies the type of thread. Uses ThreadType
|
||||
type = None
|
||||
#: The thread's picture
|
||||
photo = None
|
||||
#: The profile url
|
||||
url = None
|
||||
#: The name of the user
|
||||
#: The name of the thread
|
||||
name = None
|
||||
#: Only used by :any:`Client.getUsers`. Each user is assigned a score between 0 and 1, based on how likely it is that they were the person being searched for
|
||||
score = None
|
||||
#: Dictionary containing raw userdata from when the :class:`User` was created
|
||||
data = None
|
||||
|
||||
def __init__(self, data):
|
||||
"""Represents a Facebook User"""
|
||||
if data['type'] != 'user':
|
||||
raise Exception("[!] %s <%s> is not a user" % (data['text'], data['path']))
|
||||
self.uid = data['uid']
|
||||
self.type = data['type']
|
||||
self.photo = data['photo']
|
||||
self.url = data['path']
|
||||
self.name = data['text']
|
||||
self.score = float(data['score'])
|
||||
self.data = data
|
||||
def __init__(self, _type, _id, photo=None, name=None):
|
||||
"""Represents a Facebook thread"""
|
||||
self.id = str(_id)
|
||||
self.type = _type
|
||||
self.photo = photo
|
||||
self.name = name
|
||||
|
||||
def __repr__(self):
|
||||
return self.__unicode__()
|
||||
|
||||
def __unicode__(self):
|
||||
return u'<%s %s (%s)>' % (self.type.upper(), self.name, self.url)
|
||||
return '<{} {} ({})>'.format(self.type.name, self.name, self.id)
|
||||
|
||||
@staticmethod
|
||||
def _adaptFromChat(user_in_chat):
|
||||
"""Adapts user info from chat to User model acceptable initial dict
|
||||
|
||||
:param user_in_chat: user info from chat
|
||||
:return: :class:`User` object
|
||||
class User(Thread):
|
||||
#: The profile url
|
||||
url = None
|
||||
#: The users first name
|
||||
first_name = None
|
||||
#: The users last name
|
||||
last_name = None
|
||||
#: Whether the user and the client are friends
|
||||
is_friend = None
|
||||
#: The user's gender
|
||||
gender = None
|
||||
|
||||
'dir': None,
|
||||
'mThumbSrcSmall': None,
|
||||
'is_friend': False,
|
||||
'is_nonfriend_messenger_contact': True,
|
||||
'alternateName': '',
|
||||
'i18nGender': 16777216,
|
||||
'vanity': '',
|
||||
'type': 'friend',
|
||||
'searchTokens': ['Voznesenskij', 'Sergej'],
|
||||
'thumbSrc': 'https://fb-s-b-a.akamaihd.net/h-ak-xfa1/v/t1.0-1/c9.0.32.32/p32x32/10354686_10150004552801856_220367501106153455_n.jpg?oh=71a87d76d4e4d17615a20c43fb8dbb47&oe=59118CE4&__gda__=1493753268_ae75cef40e9785398e744259ccffd7ff',
|
||||
'mThumbSrcLarge': None,
|
||||
'firstName': 'Sergej',
|
||||
'name': 'Sergej Voznesenskij',
|
||||
'uri': 'https://www.facebook.com/profile.php?id=100014812758264',
|
||||
'id': '100014812758264',
|
||||
'gender': 2
|
||||
"""
|
||||
def __init__(self, _id, url=None, first_name=None, last_name=None, is_friend=None, gender=None, **kwargs):
|
||||
"""Represents a Facebook user. Inherits `Thread`"""
|
||||
super(User, self).__init__(ThreadType.USER, _id, **kwargs)
|
||||
self.url = url
|
||||
self.first_name = first_name
|
||||
self.last_name = last_name
|
||||
self.is_friend = is_friend
|
||||
self.gender = gender
|
||||
|
||||
return {
|
||||
'type': 'user',
|
||||
'uid': user_in_chat['id'],
|
||||
'photo': user_in_chat['thumbSrc'],
|
||||
'path': user_in_chat['uri'],
|
||||
'text': user_in_chat['name'],
|
||||
'score': 1,
|
||||
'data': user_in_chat,
|
||||
}
|
||||
|
||||
class Thread(object):
|
||||
"""Represents a thread. Currently just acts as a dict"""
|
||||
def __init__(self, **entries):
|
||||
self.__dict__.update(entries)
|
||||
class Group(Thread):
|
||||
def __init__(self, _id, **kwargs):
|
||||
"""Represents a Facebook group. Inherits `Thread`"""
|
||||
super(Group, self).__init__(ThreadType.GROUP, _id, **kwargs)
|
||||
|
||||
|
||||
class Page(Thread):
|
||||
#: The page's custom url
|
||||
url = None
|
||||
#: The name of the page's location city
|
||||
city = None
|
||||
#: Amount of likes the page has
|
||||
likees = None
|
||||
#: Some extra information about the page
|
||||
sub_text = None
|
||||
|
||||
def __init__(self, _id, url=None, city=None, likees=None, sub_text=None, **kwargs):
|
||||
"""Represents a Facebook page. Inherits `Thread`"""
|
||||
super(Page, self).__init__(ThreadType.PAGE, _id, **kwargs)
|
||||
self.url = url
|
||||
self.city = city
|
||||
self.likees = likees
|
||||
self.sub_text = sub_text
|
||||
|
||||
|
||||
class Message(object):
|
||||
"""Represents a message. Currently just acts as a dict"""
|
||||
@@ -94,6 +89,7 @@ class ThreadType(Enum):
|
||||
"""Used to specify what type of Facebook thread is being used. See :ref:`intro_threads` for more info"""
|
||||
USER = 1
|
||||
GROUP = 2
|
||||
PAGE = 3
|
||||
|
||||
class TypingStatus(Enum):
|
||||
"""Used to specify whether the user is typing or has stopped typing"""
|
||||
|
@@ -6,8 +6,22 @@ import json
|
||||
from time import time
|
||||
from random import random
|
||||
import warnings
|
||||
import logging
|
||||
from .models import *
|
||||
|
||||
# Python 2's `input` executes the input, whereas `raw_input` just returns the input
|
||||
try:
|
||||
input = raw_input
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
# Log settings
|
||||
log = logging.getLogger("client")
|
||||
log.setLevel(logging.DEBUG)
|
||||
# Creates the console handler
|
||||
handler = logging.StreamHandler()
|
||||
log.addHandler(handler)
|
||||
|
||||
#: Default list of user agents
|
||||
USER_AGENTS = [
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36",
|
||||
@@ -49,7 +63,7 @@ class ReqUrl(object):
|
||||
STICKY = "https://0-edge-chat.facebook.com/pull"
|
||||
PING = "https://0-channel-proxy-06-ash2.facebook.com/active_ping"
|
||||
UPLOAD = "https://upload.facebook.com/ajax/mercury/upload.php"
|
||||
USER_INFO = "https://www.facebook.com/chat/user_info/"
|
||||
INFO = "https://www.facebook.com/chat/user_info/"
|
||||
CONNECT = "https://www.facebook.com/ajax/add_friend/action.php?dpr=1"
|
||||
REMOVE_USER = "https://www.facebook.com/chat/remove_participants/"
|
||||
LOGOUT = "https://www.facebook.com/logout.php"
|
||||
@@ -82,7 +96,7 @@ def get_decoded(r):
|
||||
def get_json(r):
|
||||
return json.loads(strip_to_json(get_decoded(r)))
|
||||
|
||||
def digit_to_char(digit):
|
||||
def digitToChar(digit):
|
||||
if digit < 10:
|
||||
return str(digit)
|
||||
return chr(ord('a') + digit - 10)
|
||||
@@ -92,8 +106,8 @@ def str_base(number, base):
|
||||
return '-' + str_base(-number, base)
|
||||
(d, m) = divmod(number, base)
|
||||
if d > 0:
|
||||
return str_base(d, base) + digit_to_char(m)
|
||||
return digit_to_char(m)
|
||||
return str_base(d, base) + digitToChar(m)
|
||||
return digitToChar(m)
|
||||
|
||||
def generateMessageID(client_id=None):
|
||||
k = now()
|
||||
@@ -110,31 +124,20 @@ def generateOfflineThreadingID():
|
||||
msgs = format(ret, 'b') + string
|
||||
return str(int(msgs, 2))
|
||||
|
||||
def isUserToThreadType(is_user):
|
||||
return ThreadType.USER if is_user else ThreadType.GROUP
|
||||
def checkRequest(r, check_json=True):
|
||||
if not r.ok:
|
||||
raise Exception('Error when sending request: Got {} response'.format(r.status_code))
|
||||
|
||||
def raise_exception(e):
|
||||
raise e
|
||||
content = get_decoded(r)
|
||||
|
||||
def deprecation(name, deprecated_in=None, removed_in=None, details='', stacklevel=3):
|
||||
"""Used to mark parameters as deprecated. Will result in a warning being emmitted when the parameter is used."""
|
||||
warning = "Client.{} is deprecated".format(name)
|
||||
if deprecated_in:
|
||||
warning += ' in v. {}'.format(deprecated_in)
|
||||
if removed_in:
|
||||
warning += ' and will be removed in v. {}'.format(removed_in)
|
||||
if details:
|
||||
warning += '. {}'.format(details)
|
||||
if content is None or len(content) == 0:
|
||||
raise Exception('Error when sending request: Got empty response')
|
||||
|
||||
warnings.simplefilter('always', DeprecationWarning)
|
||||
warnings.warn(warning, category=DeprecationWarning, stacklevel=stacklevel)
|
||||
warnings.simplefilter('default', DeprecationWarning)
|
||||
|
||||
def deprecated(deprecated_in=None, removed_in=None, details=''):
|
||||
"""A decorator used to mark functions as deprecated. Will result in a warning being emmitted when the decorated function is used."""
|
||||
def wrap(func, *args, **kwargs):
|
||||
def wrapped_func(*args, **kwargs):
|
||||
deprecation(func.__name__, deprecated_in=deprecated_in, removed_in=removed_in, details=details, stacklevel=3)
|
||||
return func(*args, **kwargs)
|
||||
return wrapped_func
|
||||
return wrap
|
||||
if check_json:
|
||||
j = json.loads(strip_to_json(content))
|
||||
if 'error' in j:
|
||||
# 'errorDescription' is in the users own language!
|
||||
raise Exception('Error #{} when sending request: {}'.format(j['error'], j['errorDescription']))
|
||||
return j
|
||||
else:
|
||||
return r
|
||||
|
136
tests.py
136
tests.py
@@ -27,7 +27,7 @@ class CustomClient(Client):
|
||||
self.got_qprimer = False
|
||||
super(type(self), self).__init__(*args, **kwargs)
|
||||
|
||||
def onQprimer(self, made, msg):
|
||||
def onQprimer(self, msg, **kwargs):
|
||||
self.got_qprimer = True
|
||||
|
||||
class TestFbchat(unittest.TestCase):
|
||||
@@ -49,7 +49,7 @@ class TestFbchat(unittest.TestCase):
|
||||
self.assertFalse(client.isLoggedIn())
|
||||
|
||||
with self.assertRaises(Exception):
|
||||
client.login('<email>', '<password>', max_retries=1)
|
||||
client.login('<email>', '<password>', max_tries=1)
|
||||
|
||||
client.login(email, password)
|
||||
|
||||
@@ -64,10 +64,10 @@ class TestFbchat(unittest.TestCase):
|
||||
|
||||
def test_defaultThread(self):
|
||||
# setDefaultThread
|
||||
client.setDefaultThread(group_uid, ThreadType.GROUP)
|
||||
client.setDefaultThread(group_id, ThreadType.GROUP)
|
||||
self.assertTrue(client.sendMessage('test_default_recipient★'))
|
||||
|
||||
client.setDefaultThread(user_uid, ThreadType.USER)
|
||||
client.setDefaultThread(user_id, ThreadType.USER)
|
||||
self.assertTrue(client.sendMessage('test_default_recipient★'))
|
||||
|
||||
# resetDefaultThread
|
||||
@@ -75,57 +75,58 @@ class TestFbchat(unittest.TestCase):
|
||||
with self.assertRaises(ValueError):
|
||||
client.sendMessage('should_not_send')
|
||||
|
||||
def test_getAllUsers(self):
|
||||
users = client.getAllUsers()
|
||||
def test_fetchAllUsers(self):
|
||||
users = client.fetchAllUsers()
|
||||
self.assertGreater(len(users), 0)
|
||||
|
||||
def test_getUsers(self):
|
||||
users = client.getUsers('Mark Zuckerberg')
|
||||
def test_searchForUsers(self):
|
||||
users = client.searchForUsers('Mark Zuckerberg')
|
||||
self.assertGreater(len(users), 0)
|
||||
|
||||
u = users[0]
|
||||
|
||||
# Test if values are set correctly
|
||||
self.assertIsInstance(u.uid, int)
|
||||
self.assertEqual(u.type, 'user')
|
||||
self.assertEqual(u.id, '4')
|
||||
self.assertEqual(u.type, ThreadType.USER)
|
||||
self.assertEqual(u.photo[:4], 'http')
|
||||
self.assertEqual(u.url[:4], 'http')
|
||||
self.assertEqual(u.name, 'Mark Zuckerberg')
|
||||
self.assertGreater(u.score, 0)
|
||||
|
||||
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.MEDIUM, thread_id=user_uid, thread_type=ThreadType.USER))
|
||||
self.assertTrue(client.sendEmoji('😆', EmojiSize.LARGE, user_uid, ThreadType.USER))
|
||||
self.assertIsNotNone(client.sendEmoji(size=EmojiSize.SMALL, thread_id=user_id, thread_type=ThreadType.USER))
|
||||
self.assertIsNotNone(client.sendEmoji(size=EmojiSize.MEDIUM, thread_id=user_id, thread_type=ThreadType.USER))
|
||||
self.assertIsNotNone(client.sendEmoji('😆', EmojiSize.LARGE, user_id, ThreadType.USER))
|
||||
|
||||
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('😆', EmojiSize.LARGE, group_uid, ThreadType.GROUP))
|
||||
self.assertIsNotNone(client.sendEmoji(size=EmojiSize.SMALL, thread_id=group_id, thread_type=ThreadType.GROUP))
|
||||
self.assertIsNotNone(client.sendEmoji(size=EmojiSize.MEDIUM, thread_id=group_id, thread_type=ThreadType.GROUP))
|
||||
self.assertIsNotNone(client.sendEmoji('😆', EmojiSize.LARGE, group_id, ThreadType.GROUP))
|
||||
|
||||
def test_sendMessage(self):
|
||||
self.assertIsNotNone(client.sendMessage('test_send_user★', user_uid, ThreadType.USER))
|
||||
self.assertIsNotNone(client.sendMessage('test_send_group★', group_uid, ThreadType.GROUP))
|
||||
self.assertIsNone(client.sendMessage('test_send_user_should_fail★', user_uid, ThreadType.GROUP))
|
||||
self.assertIsNone(client.sendMessage('test_send_group_should_fail★', group_uid, ThreadType.USER))
|
||||
self.assertIsNotNone(client.sendMessage('test_send_user★', user_id, ThreadType.USER))
|
||||
self.assertIsNotNone(client.sendMessage('test_send_group★', group_id, ThreadType.GROUP))
|
||||
with self.assertRaises(Exception):
|
||||
client.sendMessage('test_send_user_should_fail★', user_id, ThreadType.GROUP)
|
||||
with self.assertRaises(Exception):
|
||||
client.sendMessage('test_send_group_should_fail★', group_id, ThreadType.USER)
|
||||
|
||||
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__), '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_group_images_remote★', group_uid, ThreadType.GROUP))
|
||||
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.sendRemoteImage(image_url, 'test_send_user_images_remote★', user_id, ThreadType.USER))
|
||||
self.assertTrue(client.sendRemoteImage(image_url, 'test_send_group_images_remote★', group_id, ThreadType.GROUP))
|
||||
self.assertTrue(client.sendLocalImage(image_local_url, 'test_send_group_images_local★', user_id, ThreadType.USER))
|
||||
self.assertTrue(client.sendLocalImage(image_local_url, 'test_send_group_images_local★', group_id, ThreadType.GROUP))
|
||||
|
||||
def test_getThreadInfo(self):
|
||||
client.sendMessage('test_user_getThreadInfo★', user_uid, ThreadType.USER)
|
||||
def test_fetchThreadMessages(self):
|
||||
client.sendMessage('test_user_getThreadInfo★', thread_id=user_id, thread_type=ThreadType.USER)
|
||||
|
||||
info = client.getThreadInfo(20, user_uid, ThreadType.USER)
|
||||
info = client.fetchThreadMessages(offset=0, amount=2, thread_id=user_id, thread_type=ThreadType.USER)
|
||||
self.assertEqual(info[0].author, 'fbid:' + client.uid)
|
||||
self.assertEqual(info[0].body, 'test_user_getThreadInfo★')
|
||||
|
||||
client.sendMessage('test_group_getThreadInfo★', group_uid, ThreadType.GROUP)
|
||||
client.sendMessage('test_group_getThreadInfo★', thread_id=group_id, thread_type=ThreadType.GROUP)
|
||||
|
||||
info = client.getThreadInfo(20, group_uid, ThreadType.GROUP)
|
||||
info = client.fetchThreadMessages(offset=0, amount=2, thread_id=group_id, thread_type=ThreadType.GROUP)
|
||||
self.assertEqual(info[0].author, 'fbid:' + client.uid)
|
||||
self.assertEqual(info[0].body, 'test_group_getThreadInfo★')
|
||||
|
||||
@@ -136,58 +137,57 @@ class TestFbchat(unittest.TestCase):
|
||||
|
||||
self.assertTrue(client.got_qprimer)
|
||||
|
||||
def test_getUserInfo(self):
|
||||
info = client.getUserInfo(4)
|
||||
self.assertEqual(info['name'], 'Mark Zuckerberg')
|
||||
def test_fetchUserInfo(self):
|
||||
info = client.fetchUserInfo('4')['4']
|
||||
self.assertEqual(info.name, 'Mark Zuckerberg')
|
||||
|
||||
def test_removeAddFromGroup(self):
|
||||
self.assertTrue(client.removeUserFromGroup(user_uid, thread_id=group_uid))
|
||||
self.assertTrue(client.addUsersToGroup(user_uid, thread_id=group_uid))
|
||||
client.removeUserFromGroup(user_id, thread_id=group_id)
|
||||
client.addUsersToGroup(user_id, thread_id=group_id)
|
||||
|
||||
def test_changeThreadTitle(self):
|
||||
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))
|
||||
client.changeThreadTitle('test_changeThreadTitle★', thread_id=group_id, thread_type=ThreadType.GROUP)
|
||||
client.changeThreadTitle('test_changeThreadTitle★', thread_id=user_id, 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))
|
||||
client.changeNickname('test_changeNicknameSelf★', client.id, thread_id=user_id, thread_type=ThreadType.USER)
|
||||
client.changeNickname('test_changeNicknameOther★', user_id, thread_id=user_id, thread_type=ThreadType.USER)
|
||||
client.changeNickname('test_changeNicknameSelf★', client.id, thread_id=group_id, thread_type=ThreadType.GROUP)
|
||||
client.changeNickname('test_changeNicknameOther★', user_id, thread_id=group_id, 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))
|
||||
client.changeThreadEmoji('😀', group_id)
|
||||
client.changeThreadEmoji('😀', user_id)
|
||||
client.changeThreadEmoji('😆', group_id)
|
||||
client.changeThreadEmoji('😆', user_id)
|
||||
|
||||
def test_changeThreadColor(self):
|
||||
self.assertTrue(client.changeThreadColor(ThreadColor.BRILLIANT_ROSE, group_uid))
|
||||
self.assertTrue(client.changeThreadColor(ThreadColor.MESSENGER_BLUE, group_uid))
|
||||
self.assertTrue(client.changeThreadColor(ThreadColor.BRILLIANT_ROSE, user_uid))
|
||||
self.assertTrue(client.changeThreadColor(ThreadColor.MESSENGER_BLUE, user_uid))
|
||||
client.changeThreadColor(ThreadColor.BRILLIANT_ROSE, group_id)
|
||||
client.changeThreadColor(ThreadColor.MESSENGER_BLUE, group_id)
|
||||
client.changeThreadColor(ThreadColor.BRILLIANT_ROSE, user_id)
|
||||
client.changeThreadColor(ThreadColor.MESSENGER_BLUE, user_id)
|
||||
|
||||
def test_reactToMessage(self):
|
||||
mid = client.sendMessage('test_reactToMessage★', user_uid, ThreadType.USER)
|
||||
self.assertTrue(client.reactToMessage(mid, MessageReaction.LOVE))
|
||||
mid = client.sendMessage('test_reactToMessage★', group_uid, ThreadType.GROUP)
|
||||
self.assertTrue(client.reactToMessage(mid, MessageReaction.LOVE))
|
||||
mid = client.sendMessage('test_reactToMessage★', user_id, ThreadType.USER)
|
||||
client.reactToMessage(mid, MessageReaction.LOVE)
|
||||
mid = client.sendMessage('test_reactToMessage★', group_id, ThreadType.GROUP)
|
||||
client.reactToMessage(mid, MessageReaction.LOVE)
|
||||
|
||||
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.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.STOPPED, thread_id=group_uid, thread_type=ThreadType.GROUP))
|
||||
client.setTypingStatus(TypingStatus.TYPING, thread_id=user_id, thread_type=ThreadType.USER)
|
||||
client.setTypingStatus(TypingStatus.STOPPED, thread_id=user_id, thread_type=ThreadType.USER)
|
||||
client.setTypingStatus(TypingStatus.TYPING, thread_id=group_id, thread_type=ThreadType.GROUP)
|
||||
client.setTypingStatus(TypingStatus.STOPPED, thread_id=group_id, thread_type=ThreadType.GROUP)
|
||||
|
||||
|
||||
def start_test(param_client, param_group_uid, param_user_uid, tests=[]):
|
||||
def start_test(param_client, param_group_id, param_user_id, tests=[]):
|
||||
global client
|
||||
global group_uid
|
||||
global user_uid
|
||||
global group_id
|
||||
global user_id
|
||||
|
||||
client = param_client
|
||||
group_uid = param_group_uid
|
||||
user_uid = param_user_uid
|
||||
group_id = param_group_id
|
||||
user_id = param_user_id
|
||||
|
||||
tests = ['test_' + test if 'test_' != test[:5] else test for test in tests]
|
||||
|
||||
@@ -213,16 +213,16 @@ if __name__ == '__main__':
|
||||
json = json.load(f)
|
||||
email = json['email']
|
||||
password = json['password']
|
||||
user_uid = json['user_thread_id']
|
||||
group_uid = json['group_thread_id']
|
||||
user_id = json['user_thread_id']
|
||||
group_id = json['group_thread_id']
|
||||
except (IOError, IndexError) as e:
|
||||
email = input('Email: ')
|
||||
password = getpass()
|
||||
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): ')
|
||||
group_id = input('Please enter a group thread id (To test group functionality): ')
|
||||
user_id = input('Please enter a user thread id (To test kicking/adding functionality): ')
|
||||
|
||||
print('Logging in...')
|
||||
client = CustomClient(email, password, logging_level=logging_level)
|
||||
|
||||
# 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_id, user_id, argv[1:])
|
||||
|
Reference in New Issue
Block a user