Merge pull request #518 from carpedm20/fix-documentation
Improve documentation
This commit is contained in:
@@ -3,36 +3,40 @@ Contributing to ``fbchat``
|
|||||||
|
|
||||||
Thanks for reading this, all contributions are very much welcome!
|
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.
|
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
|
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:
|
$ # *nix:
|
||||||
$ flit install --symlink
|
$ flit install --symlink
|
||||||
$ # Windows:
|
$ # Windows:
|
||||||
$ flit install --pth-file
|
$ 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.
|
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.
|
- Run ``black .`` to format your code.
|
||||||
To set these up, you should export the following environment variables:
|
- Run ``pytest`` to test your code.
|
||||||
|
- Run ``make -C docs html``, and view the generated docs, to verify that the docs still work.
|
||||||
``client1_email``, ``client1_password``, ``client2_email``, ``client2_password`` and ``group_id``
|
- 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>`__.
|
||||||
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.
|
|
||||||
|
99
README.rst
99
README.rst
@@ -1,17 +1,17 @@
|
|||||||
``fbchat``: Facebook Chat (Messenger) for Python
|
``fbchat`` - Facebook Messenger for Python
|
||||||
================================================
|
==========================================
|
||||||
|
|
||||||
.. image:: https://badgen.net/pypi/license/fbchat
|
.. image:: https://badgen.net/pypi/v/fbchat
|
||||||
:target: https://github.com/carpedm20/fbchat/tree/master/LICENSE
|
:target: https://pypi.python.org/pypi/fbchat
|
||||||
:alt: License: BSD 3-Clause
|
:alt: Project version
|
||||||
|
|
||||||
.. image:: https://badgen.net/badge/python/3.5,3.6,3.7,3.8,pypy?list=|
|
.. image:: https://badgen.net/badge/python/3.5,3.6,3.7,3.8,pypy?list=|
|
||||||
:target: https://pypi.python.org/pypi/fbchat
|
:target: https://pypi.python.org/pypi/fbchat
|
||||||
:alt: Supported python versions: 3.5, 3.6, 3.7, 3.8 and pypy
|
:alt: Supported python versions: 3.5, 3.6, 3.7, 3.8 and pypy
|
||||||
|
|
||||||
.. image:: https://badgen.net/pypi/v/fbchat
|
.. image:: https://badgen.net/pypi/license/fbchat
|
||||||
:target: https://pypi.python.org/pypi/fbchat
|
:target: https://github.com/carpedm20/fbchat/tree/master/LICENSE
|
||||||
:alt: Project version
|
:alt: License: BSD 3-Clause
|
||||||
|
|
||||||
.. image:: https://readthedocs.org/projects/fbchat/badge/?version=stable
|
.. image:: https://readthedocs.org/projects/fbchat/badge/?version=stable
|
||||||
:target: https://fbchat.readthedocs.io
|
:target: https://fbchat.readthedocs.io
|
||||||
@@ -25,37 +25,88 @@
|
|||||||
:target: https://github.com/ambv/black
|
:target: https://github.com/ambv/black
|
||||||
:alt: Code style
|
:alt: Code style
|
||||||
|
|
||||||
Facebook Chat (`Messenger <https://www.facebook.com/messages/>`__) for Python.
|
A powerful and efficient library to interact with
|
||||||
This project was inspired by `facebook-chat-api <https://github.com/Schmavery/facebook-chat-api>`__.
|
`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,
|
``fbchat`` currently support:
|
||||||
or jump right into the code by viewing the `examples <https://github.com/carpedm20/fbchat/tree/master/examples>`__
|
|
||||||
|
|
||||||
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>`__.
|
``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>`__.
|
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::
|
.. 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``:
|
More examples are available `here <https://github.com/carpedm20/fbchat/tree/master/examples>`__.
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
$ git clone https://github.com/carpedm20/fbchat.git
|
|
||||||
$ pip install fbchat
|
|
||||||
|
|
||||||
|
|
||||||
Maintainer
|
Maintainer
|
||||||
----------
|
----------
|
||||||
|
|
||||||
- Mads Marquart / `@madsmtm <https://github.com/madsmtm>`__
|
- 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>`__.
|
||||||
|
BIN
docs/_static/find-group-id.png
vendored
BIN
docs/_static/find-group-id.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 59 KiB |
70
docs/api.rst
70
docs/api.rst
@@ -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
13
docs/api/attachments.rst
Normal 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
4
docs/api/client.rst
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Client
|
||||||
|
======
|
||||||
|
|
||||||
|
.. autoclass:: Client
|
4
docs/api/events.rst
Normal file
4
docs/api/events.rst
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Events
|
||||||
|
======
|
||||||
|
|
||||||
|
.. autoclass:: Listener
|
8
docs/api/exceptions.rst
Normal file
8
docs/api/exceptions.rst
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
Exceptions
|
||||||
|
==========
|
||||||
|
|
||||||
|
.. autoexception:: FacebookError()
|
||||||
|
.. autoexception:: HTTPError()
|
||||||
|
.. autoexception:: ParseError()
|
||||||
|
.. autoexception:: ExternalError()
|
||||||
|
.. autoexception:: GraphQLError()
|
21
docs/api/index.rst
Normal file
21
docs/api/index.rst
Normal 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
8
docs/api/messages.rst
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
Messages
|
||||||
|
========
|
||||||
|
|
||||||
|
.. autoclass:: Message
|
||||||
|
.. autoclass:: Mention
|
||||||
|
.. autoclass:: EmojiSize(Enum)
|
||||||
|
:undoc-members:
|
||||||
|
.. autoclass:: MessageData()
|
20
docs/api/misc.rst
Normal file
20
docs/api/misc.rst
Normal 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
4
docs/api/session.rst
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Session
|
||||||
|
=======
|
||||||
|
|
||||||
|
.. autoclass:: Session()
|
6
docs/api/thread_data.rst
Normal file
6
docs/api/thread_data.rst
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Thread Data
|
||||||
|
===========
|
||||||
|
|
||||||
|
.. autoclass:: PageData()
|
||||||
|
.. autoclass:: UserData()
|
||||||
|
.. autoclass:: GroupData()
|
8
docs/api/threads.rst
Normal file
8
docs/api/threads.rst
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
Threads
|
||||||
|
=======
|
||||||
|
|
||||||
|
.. autoclass:: ThreadABC()
|
||||||
|
.. autoclass:: Thread
|
||||||
|
.. autoclass:: Page
|
||||||
|
.. autoclass:: User
|
||||||
|
.. autoclass:: Group
|
38
docs/conf.py
38
docs/conf.py
@@ -11,13 +11,18 @@ import sys
|
|||||||
|
|
||||||
sys.path.insert(0, os.path.abspath(".."))
|
sys.path.insert(0, os.path.abspath(".."))
|
||||||
|
|
||||||
|
os.environ["_FBCHAT_DISABLE_FIX_MODULE_METADATA"] = "1"
|
||||||
|
|
||||||
import fbchat
|
import fbchat
|
||||||
|
|
||||||
|
del os.environ["_FBCHAT_DISABLE_FIX_MODULE_METADATA"]
|
||||||
|
|
||||||
# -- Project information -----------------------------------------------------
|
# -- Project information -----------------------------------------------------
|
||||||
|
|
||||||
project = fbchat.__name__
|
project = fbchat.__name__
|
||||||
copyright = fbchat.__copyright__
|
copyright = "Copyright 2015 - 2018 by Taehoon Kim and 2018 - 2020 by Mads Marquart"
|
||||||
author = fbchat.__author__
|
author = "Taehoon Kim; Moreels Pieter-Jan; Mads Marquart"
|
||||||
|
description = fbchat.__doc__.split("\n")[0]
|
||||||
|
|
||||||
# The short X.Y version
|
# The short X.Y version
|
||||||
version = fbchat.__version__
|
version = fbchat.__version__
|
||||||
@@ -37,10 +42,10 @@ needs_sphinx = "2.0"
|
|||||||
extensions = [
|
extensions = [
|
||||||
"sphinx.ext.autodoc",
|
"sphinx.ext.autodoc",
|
||||||
"sphinx.ext.intersphinx",
|
"sphinx.ext.intersphinx",
|
||||||
"sphinx.ext.todo",
|
|
||||||
"sphinx.ext.viewcode",
|
"sphinx.ext.viewcode",
|
||||||
"sphinx.ext.napoleon",
|
"sphinx.ext.napoleon",
|
||||||
"sphinxcontrib.spelling",
|
"sphinxcontrib.spelling",
|
||||||
|
"sphinx_autodoc_typehints",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# 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.
|
# 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 ---------------------------------------------
|
# -- Options for HTMLHelp output ---------------------------------------------
|
||||||
@@ -127,16 +132,14 @@ htmlhelp_basename = project + "doc"
|
|||||||
# Grouping the document tree into LaTeX files. List of tuples
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
# (source start file, target name, title,
|
# (source start file, target name, title,
|
||||||
# author, documentclass [howto, manual, or own class]).
|
# 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 ------------------------------------------
|
# -- Options for manual page output ------------------------------------------
|
||||||
|
|
||||||
# One entry per manual page. List of tuples
|
# One entry per manual page. List of tuples
|
||||||
# (source start file, name, description, authors, manual section).
|
# (source start file, name, description, authors, manual section).
|
||||||
man_pages = [
|
man_pages = [(master_doc, project, project, [x.strip() for x in author.split(";")], 1)]
|
||||||
(master_doc, project, fbchat.__title__, [x.strip() for x in author.split(";")], 1)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for Texinfo output ----------------------------------------------
|
# -- Options for Texinfo output ----------------------------------------------
|
||||||
@@ -145,15 +148,7 @@ man_pages = [
|
|||||||
# (source start file, target name, title, author,
|
# (source start file, target name, title, author,
|
||||||
# dir menu entry, description, category)
|
# dir menu entry, description, category)
|
||||||
texinfo_documents = [
|
texinfo_documents = [
|
||||||
(
|
(master_doc, project, project, author, project, description, "Miscellaneous",)
|
||||||
master_doc,
|
|
||||||
project,
|
|
||||||
fbchat.__title__,
|
|
||||||
author,
|
|
||||||
project,
|
|
||||||
fbchat.__description__,
|
|
||||||
"Miscellaneous",
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -167,7 +162,7 @@ epub_exclude_files = ["search.html"]
|
|||||||
|
|
||||||
# -- Options for autodoc extension ---------------------------------------
|
# -- Options for autodoc extension ---------------------------------------
|
||||||
|
|
||||||
autoclass_content = "both"
|
autoclass_content = "class"
|
||||||
autodoc_member_order = "bysource"
|
autodoc_member_order = "bysource"
|
||||||
autodoc_default_options = {"members": True}
|
autodoc_default_options = {"members": True}
|
||||||
|
|
||||||
@@ -176,13 +171,6 @@ autodoc_default_options = {"members": True}
|
|||||||
# Example configuration for intersphinx: refer to the Python standard library.
|
# Example configuration for intersphinx: refer to the Python standard library.
|
||||||
intersphinx_mapping = {"https://docs.python.org/": None}
|
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 ----------------------------------------------
|
# -- Options for napoleon extension ----------------------------------------------
|
||||||
|
|
||||||
# Use Google style docstrings
|
# Use Google style docstrings
|
||||||
|
41
docs/faq.rst
41
docs/faq.rst
@@ -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,
|
That means that breaking changes can *only* occur in major versions (e.g. ``v1.9.6`` -> ``v2.0.0``).
|
||||||
most of the things may be broken at any point in time
|
|
||||||
|
|
||||||
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
|
.. code-block:: sh
|
||||||
|
|
||||||
$ pip install fbchat==<X>
|
$ pip install fbchat==1.9.6
|
||||||
|
|
||||||
Where you replace ``<X>`` with the version you want to use
|
|
||||||
|
|
||||||
|
|
||||||
Will you be supporting creating posts/events/pages and so on?
|
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 ;)
|
We won't be focusing on anything else than chat-related things. This library 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
|
|
||||||
|
@@ -1,52 +1,22 @@
|
|||||||
.. fbchat documentation master file, created by
|
.. See README.rst for explanation of these markers
|
||||||
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.
|
|
||||||
|
|
||||||
.. This documentation's layout is heavily inspired by requests' layout: https://requests.readthedocs.io
|
.. include:: ../README.rst
|
||||||
Some documentation is also partially copied from facebook-chat-api: https://github.com/Schmavery/facebook-chat-api
|
:end-before: inclusion-marker-intro-end
|
||||||
|
|
||||||
``fbchat``: Facebook Chat (Messenger) for Python
|
With that said, let's get started!
|
||||||
================================================
|
|
||||||
|
|
||||||
Release v\ |version|. (:ref:`install`)
|
.. include:: ../README.rst
|
||||||
|
:start-after: inclusion-marker-installation-start
|
||||||
Facebook Chat (`Messenger <https://www.facebook.com/messages/>`_) for Python.
|
:end-before: inclusion-marker-installation-end
|
||||||
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``
|
|
||||||
|
|
||||||
|
|
||||||
Overview
|
Documentation Overview
|
||||||
--------
|
----------------------
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
install
|
|
||||||
intro
|
intro
|
||||||
examples
|
examples
|
||||||
testing
|
|
||||||
api
|
|
||||||
todo
|
|
||||||
faq
|
faq
|
||||||
|
api/index
|
||||||
|
@@ -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
|
|
218
docs/intro.rst
218
docs/intro.rst
@@ -1,194 +1,150 @@
|
|||||||
.. _intro:
|
|
||||||
|
|
||||||
Introduction
|
Introduction
|
||||||
============
|
============
|
||||||
|
|
||||||
``fbchat`` uses your email and password to communicate with the Facebook server.
|
Welcome, this page will guide you through the basic concepts of using ``fbchat``.
|
||||||
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
|
|
||||||
|
|
||||||
|
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
|
Logging In
|
||||||
----------
|
----------
|
||||||
|
|
||||||
Simply create an instance of `Client`. If you have two factor authentication enabled, type the code in the terminal prompt
|
Everything in ``fbchat`` starts with getting an instance of `Session`. Currently there are two ways of doing that, `Session.login` and `Session.from_cookies`.
|
||||||
(If you want to supply the code in another fashion, overwrite `Client.on_2fa_code`)::
|
|
||||||
|
|
||||||
from fbchat import Client
|
The follow example will prompt you for you password, and use it to login::
|
||||||
from fbchat.models import *
|
|
||||||
client = Client('<email>', '<password>')
|
|
||||||
|
|
||||||
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::
|
However, **this is not something you should do often!** Logging in/out all the time *will* get your Facebook account locked!
|
||||||
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
|
|
||||||
|
|
||||||
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`.
|
Usability-wise, this is also better, since you won't have to re-type your password every time you want to login.
|
||||||
An example would be to login again if you've been logged out, using `Client.login`::
|
|
||||||
|
|
||||||
if not client.is_logged_in():
|
The following, quite lengthy, yet very import example, illustrates a way to do this:
|
||||||
client.login('<email>', '<password>')
|
|
||||||
|
|
||||||
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`,
|
You can get your own user ID with `Session.user_id`.
|
||||||
and searching for users is possible via. `Client.search_for_users`. See :ref:`intro_fetching`
|
|
||||||
|
|
||||||
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/>`_,
|
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.
|
||||||
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 the process is shown below:
|
||||||
An image to illustrate this is shown below:
|
|
||||||
|
|
||||||
.. image:: /_static/find-group-id.png
|
.. image:: /_static/find-group-id.png
|
||||||
:alt: An image illustrating how to find the ID of a group
|
: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>``
|
group = fbchat.Group(session=session, id="<The id you found>")
|
||||||
corresponds to the ID of a single user, and the ID of a group respectively::
|
# Or for user threads
|
||||||
|
user = fbchat.User(session=session, id="<The id you found>")
|
||||||
|
|
||||||
user.send(Message(text='<message>'))
|
Just like threads, every message, poll, plan, attachment, action etc. you send or do on Facebook has a unique ID.
|
||||||
group.send(Message(text='<message>'))
|
|
||||||
|
|
||||||
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>")
|
# Provide the thread the message was created in, and it's ID
|
||||||
thread.set_color("#a695c7")
|
message = fbchat.Message(thread=user, id="<The message id>")
|
||||||
thread.set_color("#67b868")
|
|
||||||
|
|
||||||
|
|
||||||
.. _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
|
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`.
|
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::
|
||||||
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.search_for_users('<name of user>')
|
client = fbchat.Client(session=session)
|
||||||
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))
|
|
||||||
|
|
||||||
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.
|
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.
|
||||||
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::
|
|
||||||
|
|
||||||
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.
|
There are many types of messages you can send, see the full API documentation for more.
|
||||||
(If the session cookies are invalid, your email and password will be used to login instead)::
|
|
||||||
|
|
||||||
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::
|
# React to the message with the 😍 emoji
|
||||||
You session cookies can be just as valuable as you password, so store them with equal care
|
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
|
Listening & Events
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
To use the listening functions ``fbchat`` offers (like `Client.listen`),
|
Now, we are finally at the point we have all been waiting for: Creating an automatic Facebook bot!
|
||||||
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
|
|
||||||
|
|
||||||
.. note::
|
To get started, you create your methods that will handle your events::
|
||||||
You can identify the event methods by their ``on`` prefix, e.g. ``on_message``
|
|
||||||
|
|
||||||
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):
|
And then you create a listener object, and start handling the incoming events::
|
||||||
def on_message(self, mid, author_id, message_object, thread, ts, metadata, msg, **kwargs):
|
|
||||||
# Do something with message_object here
|
|
||||||
pass
|
|
||||||
|
|
||||||
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):
|
View the :ref:`examples` to see some more examples illustrating the event system.
|
||||||
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
|
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
iterables
|
iterables
|
||||||
|
iterable
|
||||||
|
mimetype
|
||||||
timestamp
|
timestamp
|
||||||
metadata
|
metadata
|
||||||
spam
|
spam
|
||||||
@@ -12,3 +14,4 @@ spritemap
|
|||||||
online
|
online
|
||||||
inbox
|
inbox
|
||||||
subclassing
|
subclassing
|
||||||
|
codebase
|
||||||
|
@@ -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)
|
|
@@ -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::
|
|
42
examples/session_handling.py
Normal file
42
examples/session_handling.py
Normal 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
|
@@ -1,7 +1,11 @@
|
|||||||
"""Facebook Chat (Messenger) for Python
|
"""Facebook Messenger for Python.
|
||||||
|
|
||||||
:copyright: (c) 2015 - 2019 by Taehoon Kim
|
Copyright:
|
||||||
:license: BSD 3-Clause, see LICENSE for more details.
|
(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
|
import logging as _logging
|
||||||
@@ -84,47 +88,12 @@ from ._mqtt import Listener
|
|||||||
|
|
||||||
from ._client import Client
|
from ._client import Client
|
||||||
|
|
||||||
__title__ = "fbchat"
|
|
||||||
__version__ = "1.9.6"
|
__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")
|
__all__ = ("Session", "Listener", "Client")
|
||||||
|
|
||||||
# Everything below is taken from the excellent trio project:
|
|
||||||
|
|
||||||
|
from . import _fix_module_metadata
|
||||||
|
|
||||||
def fixup_module_metadata(namespace):
|
_fix_module_metadata.fixup_module_metadata(globals())
|
||||||
def fix_one(qualname, name, obj):
|
del _fix_module_metadata
|
||||||
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
|
|
||||||
|
@@ -477,15 +477,12 @@ class Client:
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
threads: Threads to set as unread
|
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)
|
return self._read_status(False, threads, at)
|
||||||
|
|
||||||
def mark_as_seen(self):
|
def mark_as_seen(self):
|
||||||
"""
|
# TODO: Documenting this
|
||||||
Todo:
|
|
||||||
Documenting this
|
|
||||||
"""
|
|
||||||
j = self.session._payload_post(
|
j = self.session._payload_post(
|
||||||
"/ajax/mercury/mark_seen.php", {"seen_timestamp": _util.now()}
|
"/ajax/mercury/mark_seen.php", {"seen_timestamp": _util.now()}
|
||||||
)
|
)
|
||||||
|
39
fbchat/_fix_module_metadata.py
Normal file
39
fbchat/_fix_module_metadata.py
Normal 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
|
@@ -8,6 +8,8 @@ from . import _session, _plan, _thread
|
|||||||
class Page(_thread.ThreadABC):
|
class Page(_thread.ThreadABC):
|
||||||
"""Represents a Facebook page. Implements `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.
|
#: The session to use when making requests.
|
||||||
session = attr.ib(type=_session.Session)
|
session = attr.ib(type=_session.Session)
|
||||||
#: The unique identifier of the page.
|
#: The unique identifier of the page.
|
||||||
|
@@ -329,6 +329,8 @@ class Session:
|
|||||||
raise _exception.ParseError("Missing payload", data=j) from e
|
raise _exception.ParseError("Missing payload", data=j) from e
|
||||||
|
|
||||||
def _graphql_requests(self, *queries):
|
def _graphql_requests(self, *queries):
|
||||||
|
# TODO: Explain usage of GraphQL, probably in the docs
|
||||||
|
# Perhaps provide this API as public?
|
||||||
data = {
|
data = {
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"response_format": "json",
|
"response_format": "json",
|
||||||
|
@@ -110,7 +110,7 @@ class ThreadABC(metaclass=abc.ABCMeta):
|
|||||||
reply_to_id: Optional message to reply to
|
reply_to_id: Optional message to reply to
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
:ref:`Message ID <intro_message_ids>` of the sent message
|
The sent message
|
||||||
"""
|
"""
|
||||||
data = self._to_send_data()
|
data = self._to_send_data()
|
||||||
data["action_type"] = "ma-type:user-generated-message"
|
data["action_type"] = "ma-type:user-generated-message"
|
||||||
@@ -139,7 +139,7 @@ class ThreadABC(metaclass=abc.ABCMeta):
|
|||||||
size: The size of the emoji
|
size: The size of the emoji
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
:ref:`Message ID <intro_message_ids>` of the sent message
|
The sent message
|
||||||
"""
|
"""
|
||||||
data = self._to_send_data()
|
data = self._to_send_data()
|
||||||
data["action_type"] = "ma-type:user-generated-message"
|
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
|
sticker_id: ID of the sticker to send
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
:ref:`Message ID <intro_message_ids>` of the sent message
|
The sent message
|
||||||
"""
|
"""
|
||||||
data = self._to_send_data()
|
data = self._to_send_data()
|
||||||
data["action_type"] = "ma-type:user-generated-message"
|
data["action_type"] = "ma-type:user-generated-message"
|
||||||
@@ -420,7 +420,7 @@ class ThreadABC(metaclass=abc.ABCMeta):
|
|||||||
def set_color(self, color: str):
|
def set_color(self, color: str):
|
||||||
"""Change thread color.
|
"""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",
|
"#0084ff", "#44bec7", "#ffc300", "#fa3c4c", "#d696bb", "#6699cc", "#13cf13",
|
||||||
"#ff7e29", "#e68585", "#7646ff", "#20cef5", "#67b868", "#d4a88c", "#ff5ca1",
|
"#ff7e29", "#e68585", "#7646ff", "#20cef5", "#67b868", "#d4a88c", "#ff5ca1",
|
||||||
|
@@ -56,7 +56,8 @@ test = [
|
|||||||
]
|
]
|
||||||
docs = [
|
docs = [
|
||||||
"sphinx~=2.0",
|
"sphinx~=2.0",
|
||||||
"sphinxcontrib-spelling~=4.0"
|
"sphinxcontrib-spelling~=4.0",
|
||||||
|
"sphinx-autodoc-typehints~=1.10",
|
||||||
]
|
]
|
||||||
lint = [
|
lint = [
|
||||||
"black",
|
"black",
|
||||||
|
Reference in New Issue
Block a user