Merge pull request #518 from carpedm20/fix-documentation

Improve documentation
This commit is contained in:
Mads Marquart
2020-01-23 00:15:07 +01:00
committed by GitHub
30 changed files with 419 additions and 478 deletions

View File

@@ -3,36 +3,40 @@ Contributing to ``fbchat``
Thanks for reading this, all contributions are very much welcome!
Please be aware that ``fbchat`` uses `Scemantic Versioning <https://semver.org/>`__
Please be aware that ``fbchat`` uses `Scemantic Versioning <https://semver.org/>`__ quite rigorously!
That means that if you're submitting a breaking change, it will probably take a while before it gets considered.
In that case, you can point your PR to the ``2.0.0-dev`` branch, where the API is being properly developed.
Otherwise, just point it to ``master``.
Development Environment
-----------------------
You can use `flit` to install the package as a symlink:
This project uses ``flit`` to configure development environments. You can install it using:
.. code-block::
.. code-block:: sh
$ pip install flit
And now you can install ``fbchat`` as a symlink:
.. code-block:: sh
$ git clone https://github.com/carpedm20/fbchat.git
$ cd fbchat
$ # *nix:
$ flit install --symlink
$ # Windows:
$ flit install --pth-file
This will also install required development tools like ``black``, ``pytest`` and ``sphinx``.
After that, you can ``import`` the module as normal.
Before committing, you should run ``black .`` in the main directory, to format your code.
Checklist
---------
Testing Environment
-------------------
Once you're done with your work, please follow the steps below:
The tests use `pytest <https://docs.pytest.org/>`__, and to work they need two Facebook accounts, and a group thread between these.
To set these up, you should export the following environment variables:
``client1_email``, ``client1_password``, ``client2_email``, ``client2_password`` and ``group_id``
If you're not able to do this, consider simply running ``pytest -m offline``.
And if you're adding new functionality, if possible, make sure to create a new test for it.
- Run ``black .`` to format your code.
- Run ``pytest`` to test your code.
- Run ``make -C docs html``, and view the generated docs, to verify that the docs still work.
- Run ``make -C docs spelling`` to check your spelling in docstrings.
- Create a pull request, and point it to ``master`` `here <https://github.com/carpedm20/fbchat/pulls/new>`__.

View File

@@ -1,17 +1,17 @@
``fbchat``: Facebook Chat (Messenger) for Python
================================================
``fbchat`` - Facebook Messenger for Python
==========================================
.. image:: https://badgen.net/pypi/license/fbchat
:target: https://github.com/carpedm20/fbchat/tree/master/LICENSE
:alt: License: BSD 3-Clause
.. image:: https://badgen.net/pypi/v/fbchat
:target: https://pypi.python.org/pypi/fbchat
:alt: Project version
.. image:: https://badgen.net/badge/python/3.5,3.6,3.7,3.8,pypy?list=|
:target: https://pypi.python.org/pypi/fbchat
:alt: Supported python versions: 3.5, 3.6, 3.7, 3.8 and pypy
.. image:: https://badgen.net/pypi/v/fbchat
:target: https://pypi.python.org/pypi/fbchat
:alt: Project version
.. image:: https://badgen.net/pypi/license/fbchat
:target: https://github.com/carpedm20/fbchat/tree/master/LICENSE
:alt: License: BSD 3-Clause
.. image:: https://readthedocs.org/projects/fbchat/badge/?version=stable
:target: https://fbchat.readthedocs.io
@@ -25,37 +25,88 @@
:target: https://github.com/ambv/black
:alt: Code style
Facebook Chat (`Messenger <https://www.facebook.com/messages/>`__) for Python.
This project was inspired by `facebook-chat-api <https://github.com/Schmavery/facebook-chat-api>`__.
A powerful and efficient library to interact with
`Facebook's Messenger <https://www.facebook.com/messages/>`__, using just your email and password.
**No XMPP or API key is needed**. Just use your email and password.
This is *not* an official API, Facebook has that `over here <https://developers.facebook.com/docs/messenger-platform>`__ for chat bots. This library differs by using a normal Facebook account instead.
Go to `Read the Docs <https://fbchat.readthedocs.io>`__ to see the full documentation,
or jump right into the code by viewing the `examples <https://github.com/carpedm20/fbchat/tree/master/examples>`__
``fbchat`` currently support:
Version warning:
----------------
- Sending many types of messages, with files, stickers, mentions, etc.
- Fetching all messages, threads and images in threads.
- Searching for messages and threads.
- Creating groups, setting the group emoji, changing nicknames, creating polls, etc.
- Listening for, an reacting to messages and other events in real-time.
- Type hints, and it has a modern codebase (e.g. only Python 3.5 and upwards).
- ``async``/``await`` (COMING).
Essentially, everything you need to make an amazing Facebook bot!
Version Warning
---------------
``v2`` is currently being developed at the ``master`` branch and it's highly unstable. If you want to view the old ``v1``, go `here <https://github.com/carpedm20/fbchat/tree/v1>`__.
Additionally, you can view the project's progress `here <https://github.com/carpedm20/fbchat/projects/2>`__.
Installation:
Caveats
-------
``fbchat`` works by imitating what the browser does, and thereby tricking Facebook into thinking it's accessing the website normally.
However, there's a catch! **Using this library may not comply with Facebook's Terms Of Service!**, so be responsible Facebook citizens! We are not responsible if your account gets banned!
Additionally, **the APIs the library is calling is undocumented!** In theory, this means that your code could break tomorrow, without the slightest warning!
If this happens to you, please report it, so that we can fix it as soon as possible!
.. inclusion-marker-intro-end
.. This message doesn't make sense in the docs at Read The Docs, so we exclude it
With that out of the way, you may go to `Read The Docs <https://fbchat.readthedocs.io/>`__ to see the full documentation!
.. inclusion-marker-installation-start
Installation
------------
.. code-block::
$ pip install fbchat
If you don't have `pip <https://pip.pypa.io/>`_, `this guide <http://docs.python-guide.org/en/latest/starting/installation/>`_ can guide you through the process.
You can also install directly from source, provided you have ``pip>=19.0``:
.. code-block::
$ pip install git+https://github.com/carpedm20/fbchat.git
.. inclusion-marker-installation-end
Example Usage
-------------
.. code-block::
$ pip install fbchat
import getpass
import fbchat
session = fbchat.Session.login("<email/phone number>", getpass.getpass())
user = fbchat.User(session=session, id=session.user_id)
user.send_text("Test message!")
You can also install from source if you have ``pip>=19.0``:
.. code-block::
$ git clone https://github.com/carpedm20/fbchat.git
$ pip install fbchat
More examples are available `here <https://github.com/carpedm20/fbchat/tree/master/examples>`__.
Maintainer
----------
- Mads Marquart / `@madsmtm <https://github.com/madsmtm>`__
- Taehoon Kim / `@carpedm20 <http://carpedm20.github.io/about/>`__
Acknowledgements
----------------
This project was originally inspired by `facebook-chat-api <https://github.com/Schmavery/facebook-chat-api>`__.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 59 KiB

View File

@@ -1,70 +0,0 @@
.. module:: fbchat
.. _api:
.. Note: we're using () to hide the __init__ method where relevant
Full API
========
If you are looking for information on a specific function, class, or method, this part of the documentation is for you.
Client
------
.. autoclass:: Client
Threads
-------
.. autoclass:: Thread()
.. autoclass:: Page()
.. autoclass:: User()
.. autoclass:: Group()
Messages
--------
.. autoclass:: Message
.. autoclass:: Mention
.. autoclass:: EmojiSize(Enum)
:undoc-members:
Exceptions
----------
.. autoexception:: FBchatException()
.. autoexception:: FBchatFacebookError()
Attachments
-----------
.. autoclass:: Attachment()
.. autoclass:: ShareAttachment()
.. autoclass:: Sticker()
.. autoclass:: LocationAttachment()
.. autoclass:: LiveLocationAttachment()
.. autoclass:: FileAttachment()
.. autoclass:: AudioAttachment()
.. autoclass:: ImageAttachment()
.. autoclass:: VideoAttachment()
.. autoclass:: ImageAttachment()
Miscellaneous
-------------
.. autoclass:: ThreadLocation(Enum)
:undoc-members:
.. autoclass:: ActiveStatus()
.. autoclass:: QuickReply
.. autoclass:: QuickReplyText
.. autoclass:: QuickReplyLocation
.. autoclass:: QuickReplyPhoneNumber
.. autoclass:: QuickReplyEmail
.. autoclass:: Poll
.. autoclass:: PollOption
.. autoclass:: Plan
.. autoclass:: GuestStatus(Enum)
:undoc-members:

13
docs/api/attachments.rst Normal file
View File

@@ -0,0 +1,13 @@
Attachments
===========
.. autoclass:: Attachment()
.. autoclass:: ShareAttachment()
.. autoclass:: Sticker()
.. autoclass:: LocationAttachment()
.. autoclass:: LiveLocationAttachment()
.. autoclass:: FileAttachment()
.. autoclass:: AudioAttachment()
.. autoclass:: ImageAttachment()
.. autoclass:: VideoAttachment()
.. autoclass:: ImageAttachment()

4
docs/api/client.rst Normal file
View File

@@ -0,0 +1,4 @@
Client
======
.. autoclass:: Client

4
docs/api/events.rst Normal file
View File

@@ -0,0 +1,4 @@
Events
======
.. autoclass:: Listener

8
docs/api/exceptions.rst Normal file
View File

@@ -0,0 +1,8 @@
Exceptions
==========
.. autoexception:: FacebookError()
.. autoexception:: HTTPError()
.. autoexception:: ParseError()
.. autoexception:: ExternalError()
.. autoexception:: GraphQLError()

21
docs/api/index.rst Normal file
View File

@@ -0,0 +1,21 @@
.. module:: fbchat
.. Note: we're using () to hide the __init__ method where relevant
Full API
========
If you are looking for information on a specific function, class, or method, this part of the documentation is for you.
.. toctree::
:maxdepth: 1
session
client
threads
thread_data
messages
exceptions
attachments
events
misc

8
docs/api/messages.rst Normal file
View File

@@ -0,0 +1,8 @@
Messages
========
.. autoclass:: Message
.. autoclass:: Mention
.. autoclass:: EmojiSize(Enum)
:undoc-members:
.. autoclass:: MessageData()

20
docs/api/misc.rst Normal file
View File

@@ -0,0 +1,20 @@
Miscellaneous
=============
.. autoclass:: ThreadLocation(Enum)
:undoc-members:
.. autoclass:: ActiveStatus()
.. autoclass:: QuickReply
.. autoclass:: QuickReplyText
.. autoclass:: QuickReplyLocation
.. autoclass:: QuickReplyPhoneNumber
.. autoclass:: QuickReplyEmail
.. autoclass:: Poll
.. autoclass:: PollOption
.. autoclass:: Plan
.. autoclass:: PlanData()
.. autoclass:: GuestStatus(Enum)
:undoc-members:

4
docs/api/session.rst Normal file
View File

@@ -0,0 +1,4 @@
Session
=======
.. autoclass:: Session()

6
docs/api/thread_data.rst Normal file
View File

@@ -0,0 +1,6 @@
Thread Data
===========
.. autoclass:: PageData()
.. autoclass:: UserData()
.. autoclass:: GroupData()

8
docs/api/threads.rst Normal file
View File

@@ -0,0 +1,8 @@
Threads
=======
.. autoclass:: ThreadABC()
.. autoclass:: Thread
.. autoclass:: Page
.. autoclass:: User
.. autoclass:: Group

View File

@@ -11,13 +11,18 @@ import sys
sys.path.insert(0, os.path.abspath(".."))
os.environ["_FBCHAT_DISABLE_FIX_MODULE_METADATA"] = "1"
import fbchat
del os.environ["_FBCHAT_DISABLE_FIX_MODULE_METADATA"]
# -- Project information -----------------------------------------------------
project = fbchat.__name__
copyright = fbchat.__copyright__
author = fbchat.__author__
copyright = "Copyright 2015 - 2018 by Taehoon Kim and 2018 - 2020 by Mads Marquart"
author = "Taehoon Kim; Moreels Pieter-Jan; Mads Marquart"
description = fbchat.__doc__.split("\n")[0]
# The short X.Y version
version = fbchat.__version__
@@ -37,10 +42,10 @@ needs_sphinx = "2.0"
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.intersphinx",
"sphinx.ext.todo",
"sphinx.ext.viewcode",
"sphinx.ext.napoleon",
"sphinxcontrib.spelling",
"sphinx_autodoc_typehints",
]
# Add any paths that contain templates here, relative to this directory.
@@ -113,7 +118,7 @@ html_show_sourcelink = False
# A shorter title for the navigation bar. Default is the same as html_title.
#
html_short_title = fbchat.__description__
html_short_title = description
# -- Options for HTMLHelp output ---------------------------------------------
@@ -127,16 +132,14 @@ htmlhelp_basename = project + "doc"
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [(master_doc, project + ".tex", fbchat.__title__, author, "manual")]
latex_documents = [(master_doc, project + ".tex", project, author, "manual")]
# -- Options for manual page output ------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, project, fbchat.__title__, [x.strip() for x in author.split(";")], 1)
]
man_pages = [(master_doc, project, project, [x.strip() for x in author.split(";")], 1)]
# -- Options for Texinfo output ----------------------------------------------
@@ -145,15 +148,7 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(
master_doc,
project,
fbchat.__title__,
author,
project,
fbchat.__description__,
"Miscellaneous",
)
(master_doc, project, project, author, project, description, "Miscellaneous",)
]
@@ -167,7 +162,7 @@ epub_exclude_files = ["search.html"]
# -- Options for autodoc extension ---------------------------------------
autoclass_content = "both"
autoclass_content = "class"
autodoc_member_order = "bysource"
autodoc_default_options = {"members": True}
@@ -176,13 +171,6 @@ autodoc_default_options = {"members": True}
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {"https://docs.python.org/": None}
# -- Options for todo extension ----------------------------------------------
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True
todo_link_only = True
# -- Options for napoleon extension ----------------------------------------------
# Use Google style docstrings

View File

@@ -1,42 +1,23 @@
.. _faq:
Frequently Asked Questions
==========================
FAQ
===
The new version broke my application
------------------------------------
Version X broke my installation
-------------------------------
``fbchat`` follows `Scemantic Versioning <https://semver.org/>`__ quite rigorously!
We try to provide backwards compatibility where possible, but since we're not part of Facebook,
most of the things may be broken at any point in time
That means that breaking changes can *only* occur in major versions (e.g. ``v1.9.6`` -> ``v2.0.0``).
Downgrade to an earlier version of ``fbchat``, run this command
If you find that something breaks, and you didn't update to a new major version, then it is a bug, and we would be grateful if you reported it!
In case you're stuck with an old codebase, you can downgrade to a previous version of ``fbchat``, e.g. version ``1.9.6``:
.. code-block:: sh
$ pip install fbchat==<X>
Where you replace ``<X>`` with the version you want to use
$ pip install fbchat==1.9.6
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
We won't be focusing on anything else than chat-related things. This library is called ``fbCHAT``, after all!

View File

@@ -1,52 +1,22 @@
.. fbchat documentation master file, created by
sphinx-quickstart on Thu May 25 15:43:01 2017.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
.. See README.rst for explanation of these markers
.. This documentation's layout is heavily inspired by requests' layout: https://requests.readthedocs.io
Some documentation is also partially copied from facebook-chat-api: https://github.com/Schmavery/facebook-chat-api
.. include:: ../README.rst
:end-before: inclusion-marker-intro-end
``fbchat``: Facebook Chat (Messenger) for Python
================================================
With that said, let's get started!
Release v\ |version|. (:ref:`install`)
Facebook Chat (`Messenger <https://www.facebook.com/messages/>`_) for Python.
This project was inspired by `facebook-chat-api <https://github.com/Schmavery/facebook-chat-api>`_.
**No XMPP or API key is needed**. Just use your email and password.
Currently ``fbchat`` support Python 3.5, 3.6, 3.7 and 3.8:
``fbchat`` works by emulating the browser.
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,
sending spammy looking URLs, logging in and out very quickly... Be responsible Facebook citizens.
.. note::
Facebook now has an `official API <https://developers.facebook.com/docs/messenger-platform>`_ for chat bots,
so if you're familiar with ``Node.js``, this might be what you're looking for.
If you're already familiar with the basics of how Facebook works internally, go to :ref:`examples` to see example usage of ``fbchat``
.. include:: ../README.rst
:start-after: inclusion-marker-installation-start
:end-before: inclusion-marker-installation-end
Overview
--------
Documentation Overview
----------------------
.. toctree::
:maxdepth: 2
install
intro
examples
testing
api
todo
faq
api/index

View File

@@ -1,43 +0,0 @@
.. _install:
Installation
============
Install using pip
-----------------
To install ``fbchat``, run this command:
.. code-block:: sh
$ pip install fbchat
If you don't have `pip <https://pip.pypa.io>`_ installed,
`this Python installation guide <http://docs.python-guide.org/en/latest/starting/installation/>`_
can guide you through the process.
Get the Source Code
-------------------
``fbchat`` is developed on GitHub, where the code is
`always available <https://github.com/carpedm20/fbchat>`_.
You can either clone the public repository:
.. code-block:: sh
$ git clone git://github.com/carpedm20/fbchat.git
Or, download a `tarball <https://github.com/carpedm20/fbchat/tarball/master>`_:
.. code-block:: sh
$ curl -OL https://github.com/carpedm20/fbchat/tarball/master
# optionally, zipball is also available (for Windows users).
Once you have a copy of the source, you can embed it in your own Python
package, or install it into your site-packages easily:
.. code-block:: sh
$ python setup.py install

View File

@@ -1,194 +1,150 @@
.. _intro:
Introduction
============
``fbchat`` uses your email and password to communicate with the Facebook server.
That means that you should always store your password in a separate file, in case e.g. someone looks over your shoulder while you're writing code.
You should also make sure that the file's access control is appropriately restrictive
Welcome, this page will guide you through the basic concepts of using ``fbchat``.
The hardest, and most error prone part is logging in, and managing your login session, so that is what we will look at first.
.. _intro_logging_in:
Logging In
----------
Simply create an instance of `Client`. If you have two factor authentication enabled, type the code in the terminal prompt
(If you want to supply the code in another fashion, overwrite `Client.on_2fa_code`)::
Everything in ``fbchat`` starts with getting an instance of `Session`. Currently there are two ways of doing that, `Session.login` and `Session.from_cookies`.
from fbchat import Client
from fbchat.models import *
client = Client('<email>', '<password>')
The follow example will prompt you for you password, and use it to login::
Replace ``<email>`` and ``<password>`` with your email and password respectively
import getpass
import fbchat
session = fbchat.Session.login("<email/phone number>", getpass.getpass())
# If your account requires a two factor authentication code:
session = fbchat.Session.login(
"<your email/phone number>",
getpass.getpass(),
lambda: getpass.getpass("2FA code"),
)
.. note::
For ease of use then most of the code snippets in this document will assume you've already completed the login process
Though the second line, ``from fbchat.models import *``, is not strictly necessary here, later code snippets will assume you've done this
However, **this is not something you should do often!** Logging in/out all the time *will* get your Facebook account locked!
If you want to change how verbose ``fbchat`` is, change the logging level (in `Client`)
Instead, you should start by using `Session.login`, and then store the cookies with `Session.get_cookies`, so that they can be used instead the next time your application starts.
Throughout your code, if you want to check whether you are still logged in, use `Client.is_logged_in`.
An example would be to login again if you've been logged out, using `Client.login`::
Usability-wise, this is also better, since you won't have to re-type your password every time you want to login.
if not client.is_logged_in():
client.login('<email>', '<password>')
The following, quite lengthy, yet very import example, illustrates a way to do this:
When you're done using the client, and want to securely logout, use `Client.logout`::
.. literalinclude:: ../examples/session_handling.py
client.logout()
Assuming you have successfully completed the above, congratulations! Using ``fbchat`` should be mostly trouble free from now on!
.. _intro_threads:
Understanding Thread Ids
------------------------
Threads
-------
At the core of any thread is its unique identifier, its ID.
A thread can refer to two things: A Messenger group chat (`Group`) or a single Facebook user (`User`).
A thread basically just means "something I can chat with", but more precisely, it can refer to a few things:
- A Messenger group thread (`Group`)
- The conversation between you and a single Facebook user (`User`)
- The conversation between you and a Facebook Page (`Page`)
Searching for group chats and finding their ID can be done via. `Client.search_for_groups`,
and searching for users is possible via. `Client.search_for_users`. See :ref:`intro_fetching`
You can get your own user ID with `Session.user_id`.
You can get your own user ID by using `Session.user_id`
Getting the ID of a specific group thread is fairly trivial, you only need to login to `<https://www.messenger.com/>`_, click on the group you want to find the ID of, and then read the id from the address bar.
The URL will look something like this: ``https://www.messenger.com/t/1234567890``, where ``1234567890`` would be the ID of the group.
Getting the ID of a group chat is fairly trivial otherwise, 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.
The URL will look something like this: ``https://www.facebook.com/messages/t/1234567890``, where ``1234567890`` would be the ID of the group.
An image to illustrate this is shown below:
The same method can be applied to some user accounts, though if they have set a custom URL, then you will have to use a different method.
An image to illustrate the process is shown below:
.. image:: /_static/find-group-id.png
:alt: An image illustrating how to find the ID of a group
The same method can be applied to some user accounts, though if they've set a custom URL, then you'll just see that URL instead
Once you have an ID, you can use it to create a `Group` or a `User` instance, which will allow you to do all sorts of things. To do this, you need a valid, logged in session::
Here's an snippet showing the usage of thread IDs and thread types, where ``<user id>`` and ``<group id>``
corresponds to the ID of a single user, and the ID of a group respectively::
group = fbchat.Group(session=session, id="<The id you found>")
# Or for user threads
user = fbchat.User(session=session, id="<The id you found>")
user.send(Message(text='<message>'))
group.send(Message(text='<message>'))
Just like threads, every message, poll, plan, attachment, action etc. you send or do on Facebook has a unique ID.
Some functions don't require a thread type, so in these cases you just provide the thread ID::
Below is an example of using such a message ID to get a `Message` instance::
thread = fbchat.Thread(session=session, id="<user-or-group-id>")
thread.set_color("#a695c7")
thread.set_color("#67b868")
# Provide the thread the message was created in, and it's ID
message = fbchat.Message(thread=user, id="<The message id>")
.. _intro_message_ids:
Message IDs
-----------
Every message you send on Facebook has a unique ID, and every action you do in a thread,
like changing a nickname or adding a person, has a unique ID too.
This snippet shows how to send a message, and then use the returned ID to react to that message with a 😍 emoji::
message = thread.send_text("A message!")
message.react("😍")
.. _intro_interacting:
Interacting with Threads
------------------------
``fbchat`` provides multiple functions for interacting with threads
Most functionality works on all threads, though some things,
like adding users to and removing users from a group chat, logically only works on group chats
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::
user = User(session=session, id=session.user_id)
message_id = user.send(Message(text='test message'))
You can see a full example showing all the possible thread interactions with ``fbchat`` by going to :ref:`examples`
.. _intro_fetching:
Fetching Information
--------------------
You can use ``fbchat`` to fetch basic information like user names, profile pictures, thread names and user IDs
Managing these ids yourself quickly becomes very cumbersome! Luckily, there are other, easier ways of getting `Group`/`User` instances.
You can retrieve a user's ID with `Client.search_for_users`.
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::
You would start by creating a `Client` instance, which is basically just a helper on top of `Session`, that will allow you to do various things::
users = client.search_for_users('<name of user>')
user = users[0]
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))
client = fbchat.Client(session=session)
Since this uses Facebook's search functions, you don't have to specify the whole name, first names will usually be enough
Now, you could search for threads using `Client.search_for_threads`, or fetch a list of them using `Client.fetch_threads`::
You can see a full example showing all the possible ways to fetch information with ``fbchat`` by going to :ref:`examples`
# Fetch the 5 most likely search results
# Uses Facebook's search functions, you don't have to specify the whole name, first names will usually be enough
threads = list(client.search_for_threads("<name of the thread to search for>", limit=5))
# Fetch the 5 most recent threads in your account
threads = list(client.fetch_threads(limit=5))
Note the `list` statements; this is because the methods actually return `generators <https://wiki.python.org/moin/Generators>`__. If you don't know what that means, don't worry, it is just something you can use to make your code faster later.
The examples above will actually fetch `UserData`/`GroupData`, which are subclasses of `User`/`Group`. These model have extra properties, so you could for example print the names and ids of the fetched threads like this::
for thread in threads:
print(f"{thread.id}: {thread.name}")
Once you have a thread, you can use that to fetch the messages therein::
for message in thread.fetch_messages(limit=20):
print(message.text)
.. _intro_sessions:
Interacting with Threads
------------------------
Sessions
--------
Once you have a `User`/`Group` instance, you can do things on them as described in `ThreadABC`, since they are subclasses of that.
``fbchat`` provides functions to retrieve and set the session cookies.
This will enable you to store the session cookies in a separate file, so that you don't have to login each time you start your script.
Use `Client.get_gession` to retrieve the cookies::
Some functionality, like adding users to a `Group`, or blocking a `User`, logically only works the relevant threads, so see the full API documentation for that.
session_cookies = client.get_gession()
With that out of the way, let's see some examples!
Then you can use `Client.set_gession`::
The simplest way of interracting with a thread is by sending a message::
client.set_gession(session_cookies)
# Send a message to the user
message = user.send_text("test message")
Or you can set the ``session_cookies`` on your initial login.
(If the session cookies are invalid, your email and password will be used to login instead)::
There are many types of messages you can send, see the full API documentation for more.
client = Client('<email>', '<password>', session_cookies=session_cookies)
Notice how we held on to the sent message? The return type i a `Message` instance, so you can interract with it afterwards::
.. warning::
You session cookies can be just as valuable as you password, so store them with equal care
# React to the message with the 😍 emoji
message.react("😍")
Besides sending messages, you can also interract with threads in other ways. An example is to change the thread color::
# Will change the thread color to the default blue
thread.set_color("#0084ff")
.. _intro_events:
Listening & Events
------------------
To use the listening functions ``fbchat`` offers (like `Client.listen`),
you have to define what should be executed when certain events happen.
By default, (most) events will just be a `logging.info` statement,
meaning it will simply print information to the console when an event happens
Now, we are finally at the point we have all been waiting for: Creating an automatic Facebook bot!
.. note::
You can identify the event methods by their ``on`` prefix, e.g. ``on_message``
To get started, you create your methods that will handle your events::
The event actions can be changed by subclassing the `Client`, and then overwriting the event methods::
def on_message(event):
print(f"Message from {event.author.id}: {event.message.text}")
class CustomClient(Client):
def on_message(self, mid, author_id, message_object, thread, ts, metadata, msg, **kwargs):
# Do something with message_object here
pass
And then you create a listener object, and start handling the incoming events::
client = CustomClient('<email>', '<password>')
listener = fbchat.Listener.connect(session, False, False)
**Notice:** The following snippet is as equally valid as the previous one::
for event in listener.listen():
if isinstance(event, fbchat.MessageEvent):
on_message(event)
class CustomClient(Client):
def on_message(self, message_object, author_id, thread, **kwargs):
# Do something with message_object here
pass
client = CustomClient('<email>', '<password>')
The change was in the parameters that our ``on_message`` method took: ``message_object`` and ``author_id`` got swapped,
and ``mid``, ``ts``, ``metadata`` and ``msg`` got removed, but the function still works, since we included ``**kwargs``
.. note::
Therefore, for both backwards and forwards compatibility,
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
View the :ref:`examples` to see some more examples illustrating the event system.

View File

@@ -1,4 +1,6 @@
iterables
iterable
mimetype
timestamp
metadata
spam
@@ -12,3 +14,4 @@ spritemap
online
inbox
subclassing
codebase

View File

@@ -1,25 +0,0 @@
.. _testing:
Testing
=======
To use the tests, copy ``tests/data.json`` to ``tests/my_data.json`` or type the information manually in the terminal prompts.
- email: Your (or a test user's) email / phone number
- password: Your (or a test user's) password
- group_thread_id: A test group that will be used to test group functionality
- user_thread_id: A person that will be used to test kick/add functionality (This user should be in the group)
Please remember to test all supported python versions.
If you've made any changes to the 2FA functionality, test it with a 2FA enabled account.
If you only want to execute specific tests, pass the function names in the command line (not including the ``test_`` prefix). Example:
.. code-block:: sh
$ python tests.py sendMessage sessions sendEmoji
.. warning::
Do not execute the full set of tests in too quick succession. This can get your account temporarily blocked for spam!
(You should execute the script at max about 10 times a day)

View File

@@ -1,22 +0,0 @@
.. _todo:
Todo
====
This page will be periodically updated to show missing features and documentation
Missing Functionality
---------------------
- Implement ``Client.search_for_message``
- This will use the GraphQL request API
- Implement chatting with pages properly
- Write better FAQ
- Explain usage of GraphQL
Documentation
-------------
.. todolist::

View File

@@ -0,0 +1,42 @@
# TODO: Consider adding Session.from_file and Session.to_file,
# which would make this example a lot easier!
import atexit
import json
import getpass
import fbchat
def load_cookies(filename):
try:
# Load cookies from file
with open(filename) as f:
return json.load(f)
except FileNotFoundError:
return # No cookies yet
def save_cookies(filename, cookies):
with open(filename, "w") as f:
json.dump(f, cookies)
def load_session(cookies):
if not cookies:
return
try:
return fbchat.Session.from_cookies(cookies)
except fbchat.FacebookError:
return # Failed loading from cookies
cookies = load_cookies("session.json")
session = load_session(cookies)
if not session:
# Session could not be loaded, login instead!
session = fbchat.Session.login("<email>", getpass.getpass())
# Save session cookies to file when the program exits
atexit.register(lambda: save_cookies("session.json", session.get_cookies()))
# Do stuff with session here

View File

@@ -1,7 +1,11 @@
"""Facebook Chat (Messenger) for Python
"""Facebook Messenger for Python.
:copyright: (c) 2015 - 2019 by Taehoon Kim
:license: BSD 3-Clause, see LICENSE for more details.
Copyright:
(c) 2015 - 2018 by Taehoon Kim
(c) 2018 - 2020 by Mads Marquart
License:
BSD 3-Clause, see LICENSE for more details.
"""
import logging as _logging
@@ -84,47 +88,12 @@ from ._mqtt import Listener
from ._client import Client
__title__ = "fbchat"
__version__ = "1.9.6"
__description__ = "Facebook Chat (Messenger) for Python"
__copyright__ = "Copyright 2015 - 2019 by Taehoon Kim"
__license__ = "BSD 3-Clause"
__author__ = "Taehoon Kim; Moreels Pieter-Jan; Mads Marquart"
__email__ = "carpedm20@gmail.com"
__all__ = ("Session", "Listener", "Client")
# Everything below is taken from the excellent trio project:
from . import _fix_module_metadata
def fixup_module_metadata(namespace):
def fix_one(qualname, name, obj):
mod = getattr(obj, "__module__", None)
if mod is not None and mod.startswith("fbchat."):
obj.__module__ = "fbchat"
# Modules, unlike everything else in Python, put fully-qualitied
# names into their __name__ attribute. We check for "." to avoid
# rewriting these.
if hasattr(obj, "__name__") and "." not in obj.__name__:
obj.__name__ = name
obj.__qualname__ = qualname
if isinstance(obj, type):
# Fix methods
for attr_name, attr_value in obj.__dict__.items():
fix_one(objname + "." + attr_name, attr_name, attr_value)
for objname, obj in namespace.items():
if not objname.startswith("_"): # ignore private attributes
fix_one(objname, objname, obj)
# Having the public path in .__module__ attributes is important for:
# - exception names in printed tracebacks
# - sphinx :show-inheritance:
# - deprecation warnings
# - pickle
# - probably other stuff
fixup_module_metadata(globals())
del fixup_module_metadata
_fix_module_metadata.fixup_module_metadata(globals())
del _fix_module_metadata

View File

@@ -477,15 +477,12 @@ class Client:
Args:
threads: Threads to set as unread
at: Timestam to signal the read cursor at
at: Timestamp to signal the read cursor at
"""
return self._read_status(False, threads, at)
def mark_as_seen(self):
"""
Todo:
Documenting this
"""
# TODO: Documenting this
j = self.session._payload_post(
"/ajax/mercury/mark_seen.php", {"seen_timestamp": _util.now()}
)

View File

@@ -0,0 +1,39 @@
"""Everything in this module is taken from the excellent trio project.
Having the public path in .__module__ attributes is important for:
- exception names in printed tracebacks
- ~sphinx :show-inheritance:~
- deprecation warnings
- pickle
- probably other stuff
"""
import os
def fixup_module_metadata(namespace):
def fix_one(qualname, name, obj):
mod = getattr(obj, "__module__", None)
if mod is not None and mod.startswith("fbchat."):
obj.__module__ = "fbchat"
# Modules, unlike everything else in Python, put fully-qualitied
# names into their __name__ attribute. We check for "." to avoid
# rewriting these.
if hasattr(obj, "__name__") and "." not in obj.__name__:
obj.__name__ = name
obj.__qualname__ = qualname
if isinstance(obj, type):
# Fix methods
for attr_name, attr_value in obj.__dict__.items():
fix_one(objname + "." + attr_name, attr_name, attr_value)
for objname, obj in namespace.items():
if not objname.startswith("_"): # ignore private attributes
fix_one(objname, objname, obj)
# Allow disabling this when running Sphinx
# This is done so that Sphinx autodoc can detect the file's source
# TODO: Find a better way to detect when we're running Sphinx!
if os.environ.get("_FBCHAT_DISABLE_FIX_MODULE_METADATA") == "1":
fixup_module_metadata = lambda namespace: None

View File

@@ -8,6 +8,8 @@ from . import _session, _plan, _thread
class Page(_thread.ThreadABC):
"""Represents a Facebook page. Implements `ThreadABC`."""
# TODO: Implement pages properly, the implementation is lacking in a lot of places!
#: The session to use when making requests.
session = attr.ib(type=_session.Session)
#: The unique identifier of the page.

View File

@@ -329,6 +329,8 @@ class Session:
raise _exception.ParseError("Missing payload", data=j) from e
def _graphql_requests(self, *queries):
# TODO: Explain usage of GraphQL, probably in the docs
# Perhaps provide this API as public?
data = {
"method": "GET",
"response_format": "json",

View File

@@ -110,7 +110,7 @@ class ThreadABC(metaclass=abc.ABCMeta):
reply_to_id: Optional message to reply to
Returns:
:ref:`Message ID <intro_message_ids>` of the sent message
The sent message
"""
data = self._to_send_data()
data["action_type"] = "ma-type:user-generated-message"
@@ -139,7 +139,7 @@ class ThreadABC(metaclass=abc.ABCMeta):
size: The size of the emoji
Returns:
:ref:`Message ID <intro_message_ids>` of the sent message
The sent message
"""
data = self._to_send_data()
data["action_type"] = "ma-type:user-generated-message"
@@ -154,7 +154,7 @@ class ThreadABC(metaclass=abc.ABCMeta):
sticker_id: ID of the sticker to send
Returns:
:ref:`Message ID <intro_message_ids>` of the sent message
The sent message
"""
data = self._to_send_data()
data["action_type"] = "ma-type:user-generated-message"
@@ -420,7 +420,7 @@ class ThreadABC(metaclass=abc.ABCMeta):
def set_color(self, color: str):
"""Change thread color.
The new color must be one of the following:
The new color must be one of the following::
"#0084ff", "#44bec7", "#ffc300", "#fa3c4c", "#d696bb", "#6699cc", "#13cf13",
"#ff7e29", "#e68585", "#7646ff", "#20cef5", "#67b868", "#d4a88c", "#ff5ca1",

View File

@@ -56,7 +56,8 @@ test = [
]
docs = [
"sphinx~=2.0",
"sphinxcontrib-spelling~=4.0"
"sphinxcontrib-spelling~=4.0",
"sphinx-autodoc-typehints~=1.10",
]
lint = [
"black",