Add inline examples

This commit is contained in:
Mads Marquart
2020-01-23 12:07:40 +01:00
parent 0d139cee73
commit 45a71fd1a3
10 changed files with 396 additions and 51 deletions

View File

@@ -19,11 +19,14 @@ from typing import Sequence, Iterable, Tuple, Optional, Set
@attrs_default
class Client:
"""A client for the Facebook Chat (Messenger).
"""A client for Facebook Messenger.
This contains all the methods you use to interact with Facebook. You can extend this
class, and overwrite the ``on`` methods, to provide custom event handling (mainly
useful while listening).
This contains methods that are generally needed to interact with Facebook.
Example:
Create a new client instance.
>>> client = fbchat.Client(session=session)
"""
#: The session to use when making requests.
@@ -39,6 +42,15 @@ class Client:
But does not include deactivated, deleted or memorialized users (logically,
since you can't chat with those).
The order these are returned is arbitary.
Example:
Get the name of an arbitary user that you're currently chatting with.
>>> users = client.fetch_users()
>>> users[0].name
"A user"
"""
data = {"viewer": self.session.user_id}
j = self.session._payload_post("/chat/user_info_all", data)
@@ -54,12 +66,18 @@ class Client:
def search_for_users(self, name: str, limit: int) -> Iterable[_user.UserData]:
"""Find and get users by their name.
The returned users are ordered by relevance.
Args:
name: Name of the user
limit: The max. amount of users to fetch
Returns:
Users, ordered by relevance
Example:
Get the full name of the first found user.
>>> (user,) = client.search_for_users("user", limit=1)
>>> user.name
"A user"
"""
params = {"search": name, "limit": limit}
(j,) = self.session._graphql_requests(
@@ -74,9 +92,18 @@ class Client:
def search_for_pages(self, name: str, limit: int) -> Iterable[_page.PageData]:
"""Find and get pages by their name.
The returned pages are ordered by relevance.
Args:
name: Name of the page
limit: The max. amount of pages to fetch
Example:
Get the full name of the first found page.
>>> (page,) = client.search_for_pages("page", limit=1)
>>> page.name
"A page"
"""
params = {"search": name, "limit": limit}
(j,) = self.session._graphql_requests(
@@ -91,9 +118,18 @@ class Client:
def search_for_groups(self, name: str, limit: int) -> Iterable[_group.GroupData]:
"""Find and get group threads by their name.
The returned groups are ordered by relevance.
Args:
name: Name of the group thread
limit: The max. amount of groups to fetch
Example:
Get the full name of the first found group.
>>> (group,) = client.search_for_groups("group", limit=1)
>>> group.name
"A group"
"""
params = {"search": name, "limit": limit}
(j,) = self.session._graphql_requests(
@@ -108,9 +144,19 @@ class Client:
def search_for_threads(self, name: str, limit: int) -> Iterable[_thread.ThreadABC]:
"""Find and get threads by their name.
The returned threads are ordered by relevance.
Args:
name: Name of the thread
limit: The max. amount of threads to fetch
Example:
Search for a user, and get the full name of the first found result.
>>> (user,) = client.search_for_threads("user", limit=1)
>>> assert isinstance(user, fbchat.User)
>>> user.name
"A user"
"""
params = {"search": name, "limit": limit}
(j,) = self.session._graphql_requests(
@@ -173,16 +219,26 @@ class Client:
Intended to be used alongside `ThreadABC.search_messages`
Warning! If someone send a message to a thread that matches the query, while
we're searching, some snippets will get returned twice.
we're searching, some snippets will get returned twice, and some will be lost.
Not sure if we should handle it, Facebook's implementation doesn't...
This is fundamentally unfixable, it's just how the endpoint is implemented.
Args:
query: Text to search for
limit: Max. number of threads to retrieve. If ``None``, all threads will be
retrieved.
retrieved
Returns:
Example:
Search for messages, and print the amount of snippets in each thread.
>>> for thread, count in client.search_messages("abc", limit=3):
... print(f"{thread.id} matched the search {count} time(s)")
...
1234 matched the search 2 time(s)
2345 matched the search 1 time(s)
3456 matched the search 100 time(s)
Return:
Iterable with tuples of threads, and the total amount of matches.
"""
offset = 0
@@ -237,6 +293,13 @@ class Client:
Args:
ids: Thread ids to query
Example:
Get data about the user with id "4".
>>> (user,) = client.fetch_thread_info(["4"])
>>> user.name
"Mark Zuckerberg"
"""
ids = list(ids)
queries = []
@@ -323,6 +386,16 @@ class Client:
limit: Max. number of threads to retrieve. If ``None``, all threads will be
retrieved.
location: INBOX, PENDING, ARCHIVED or OTHER
Example:
Fetch the last three threads that the user chatted with.
>>> for thread in client.fetch_threads(limit=3):
... print(f"{thread.id}: {thread.name}")
...
1234: A user
2345: A group
3456: A page
"""
# This is measured empirically as 837, safe default chosen below
MAX_BATCH_LIMIT = 100
@@ -395,6 +468,10 @@ class Client:
Args:
image_id: The image you want to fetch
Example:
>>> client.fetch_image_url("1234")
"https://scontent-arn1-1.xx.fbcdn.net/v/t1.123-4/1_23_45_n.png?..."
Returns:
An URL where you can download the original image
"""
@@ -513,7 +590,15 @@ class Client:
j = self.session._payload_post("/ajax/mercury/move_thread.php", data)
def delete_threads(self, threads: Iterable[_thread.ThreadABC]):
"""Delete threads."""
"""Bulk delete threads.
Args:
threads: Threads to delete
Example:
>>> group = fbchat.Group(session=session, id="1234")
>>> client.delete_threads([group])
"""
data_unpin = {}
data_delete = {}
for i, thread in enumerate(threads):
@@ -527,10 +612,15 @@ class Client:
)
def delete_messages(self, messages: Iterable[_message.Message]):
"""Delete specified messages.
"""Bulk delete specified messages.
Args:
messages: Messages to delete
Example:
>>> message1 = fbchat.Message(thread=thread, id="1234")
>>> message2 = fbchat.Message(thread=thread, id="2345")
>>> client.delete_threads([message1, message2])
"""
data = {}
for i, message in enumerate(messages):

View File

@@ -7,7 +7,11 @@ from typing import Sequence, Iterable, Set, Mapping
@attrs_default
class Group(_thread.ThreadABC):
"""Represents a Facebook group. Implements `ThreadABC`."""
"""Represents a Facebook group. Implements `ThreadABC`.
Example:
>>> group = fbchat.Group(session=session, id="1234")
"""
#: The session to use when making requests.
session = attr.ib(type=_session.Session)
@@ -22,6 +26,9 @@ class Group(_thread.ThreadABC):
Args:
user_ids: One or more user IDs to add
Example:
>>> group.add_participants(["1234", "2345"])
"""
data = self._to_send_data()
@@ -45,6 +52,9 @@ class Group(_thread.ThreadABC):
Args:
user_id: User ID to remove
Example:
>>> group.remove_participant("1234")
"""
data = {"uid": user_id, "tid": self.id}
j = self.session._payload_post("/chat/remove_participants/", data)
@@ -62,6 +72,9 @@ class Group(_thread.ThreadABC):
Args:
user_ids: One or more user IDs to set admin
Example:
>>> group.add_admins(["1234", "2345"])
"""
self._admin_status(user_ids, True)
@@ -70,6 +83,9 @@ class Group(_thread.ThreadABC):
Args:
user_ids: One or more user IDs to remove admin
Example:
>>> group.remove_admins(["1234", "2345"])
"""
self._admin_status(user_ids, False)
@@ -78,6 +94,9 @@ class Group(_thread.ThreadABC):
Args:
title: New title
Example:
>>> group.set_title("Abc")
"""
data = {"thread_name": title, "thread_id": self.id}
j = self.session._payload_post("/messaging/set_thread_name/?dpr=1", data)
@@ -87,6 +106,14 @@ class Group(_thread.ThreadABC):
Args:
image_id: ID of uploaded image
Example:
Upload an image, and use it as the group image.
>>> with open("image.png", "rb") as f:
... (file,) = session._upload([("image.png", f, "image/png")])
...
>>> group.set_image(file[0])
"""
data = {"thread_image_id": image_id, "thread_id": self.id}
j = self.session._payload_post("/messaging/set_thread_image/?dpr=1", data)
@@ -96,6 +123,9 @@ class Group(_thread.ThreadABC):
Args:
require_admin_approval: True or False
Example:
>>> group.set_approval_mode(False)
"""
data = {"set_mode": int(require_admin_approval), "thread_fbid": self.id}
j = self.session._payload_post("/messaging/set_approval_mode/?dpr=1", data)
@@ -118,6 +148,9 @@ class Group(_thread.ThreadABC):
Args:
user_ids: One or more user IDs to accept
Example:
>>> group.accept_users(["1234", "2345"])
"""
self._users_approval(user_ids, True)
@@ -126,6 +159,9 @@ class Group(_thread.ThreadABC):
Args:
user_ids: One or more user IDs to deny
Example:
>>> group.deny_users(["1234", "2345"])
"""
self._users_approval(user_ids, False)

View File

@@ -43,7 +43,11 @@ class EmojiSize(enum.Enum):
@attrs_default
class Mention:
"""Represents a ``@mention``."""
"""Represents a ``@mention``.
>>> fbchat.Mention(thread_id="1234", offset=5, length=2)
Mention(thread_id="1234", offset=5, length=2)
"""
#: The thread ID the mention is pointing at
thread_id = attr.ib(type=str)
@@ -82,7 +86,12 @@ SENDABLE_REACTIONS = ("❤", "😍", "😆", "😮", "😢", "😠", "👍", "
@attrs_default
class Message:
"""Represents a Facebook message."""
"""Represents a Facebook message.
Example:
>>> thread = fbchat.User(session=session, id="1234")
>>> message = fbchat.Message(thread=thread, id="mid.$XYZ")
"""
#: The thread that this message belongs to.
thread = attr.ib(type="_thread.ThreadABC")
@@ -95,7 +104,11 @@ class Message:
return self.thread.session
def unsend(self):
"""Unsend the message (removes it for everyone)."""
"""Unsend the message (removes it for everyone).
Example:
>>> message.unsend()
"""
data = {"message_id": self.id}
j = self.session._payload_post("/messaging/unsend_message/?dpr=1", data)
@@ -107,6 +120,9 @@ class Message:
Args:
reaction: Reaction emoji to use, or if ``None``, removes reaction.
Example:
>>> message.react("😍")
"""
if reaction and reaction not in SENDABLE_REACTIONS:
raise ValueError(
@@ -128,7 +144,13 @@ class Message:
_exception.handle_graphql_errors(j)
def fetch(self) -> "MessageData":
"""Fetch fresh `MessageData` object."""
"""Fetch fresh `MessageData` object.
Example:
>>> message = message.fetch()
>>> message.text
"The message text"
"""
message_info = self.thread._forced_fetch(self.id).get("message")
return MessageData._from_graphql(self.thread, message_info)
@@ -139,10 +161,10 @@ class Message:
Return a tuple, with the formatted string and relevant mentions.
>>> Message.format_mentions("Hey {!r}! My name is {}", ("1234", "Peter"), ("4321", "Michael"))
("Hey 'Peter'! My name is Michael", [<Mention 1234: offset=4 length=7>, <Mention 4321: offset=24 length=7>])
("Hey 'Peter'! My name is Michael", [Mention(thread_id=1234, offset=4, length=7), Mention(thread_id=4321, offset=24, length=7)])
>>> Message.format_mentions("Hey {p}! My name is {}", ("1234", "Michael"), p=("4321", "Peter"))
('Hey Peter! My name is Michael', [<Mention 4321: offset=4 length=5>, <Mention 1234: offset=22 length=7>])
('Hey Peter! My name is Michael', [Mention(thread_id=4321, offset=4, length=5), Mention(thread_id=1234, offset=22, length=7)])
"""
result = ""
mentions = list()

View File

@@ -49,6 +49,9 @@ class Listener:
session: The session to use when making requests.
chat_on: Whether ...
foreground: Whether ...
Example:
>>> listener = fbchat.Listener.connect(session, chat_on=True, foreground=True)
"""
mqtt = paho.mqtt.client.Client(
client_id="mqttwsclient",
@@ -343,6 +346,12 @@ class Listener:
Yields events when they arrive.
This will automatically reconnect on errors.
Example:
Print events continually.
>>> for event in listener.listen():
... print(event)
"""
while self._loop_once():
if self._events:
@@ -355,6 +364,14 @@ class Listener:
Can be called while listening, which will stop the listening loop.
The `Listener` object should not be used after this is called!
Example:
Stop the listener when recieving a message with the text "/stop"
>>> for event in listener.listen():
... if isinstance(event, fbchat.MessageEvent):
... if event.message.text == "/stop":
... listener.disconnect() # Almost the same "break"
"""
self._mqtt.disconnect()

View File

@@ -6,7 +6,11 @@ from . import _session, _plan, _thread
@attrs_default
class Page(_thread.ThreadABC):
"""Represents a Facebook page. Implements `ThreadABC`."""
"""Represents a Facebook page. Implements `ThreadABC`.
Example:
>>> page = fbchat.Page(session=session, id="1234")
"""
# TODO: Implement pages properly, the implementation is lacking in a lot of places!

View File

@@ -22,7 +22,11 @@ ACONTEXT = {
@attrs_default
class Plan:
"""Base model for plans."""
"""Base model for plans.
Example:
>>> plan = fbchat.Plan(session=session, id="1234")
"""
#: The session to use when making requests.
session = attr.ib(type=_session.Session)
@@ -30,7 +34,13 @@ class Plan:
id = attr.ib(converter=str, type=str)
def fetch(self) -> "PlanData":
"""Fetch fresh `PlanData` object."""
"""Fetch fresh `PlanData` object.
Example:
>>> plan = plan.fetch()
>>> plan.title
"A plan"
"""
data = {"event_reminder_id": self.id}
j = self.session._payload_post("/ajax/eventreminder", data)
return PlanData._from_fetch(self.session, j)
@@ -80,7 +90,11 @@ class Plan:
j = self.session._payload_post("/ajax/eventreminder/submit", data)
def delete(self):
"""Delete the plan."""
"""Delete the plan.
Example:
>>> plan.delete()
"""
data = {"event_reminder_id": self.id, "delete": "true", "acontext": ACONTEXT}
j = self.session._payload_post("/ajax/eventreminder/submit", data)
@@ -93,11 +107,19 @@ class Plan:
j = self.session._payload_post("/ajax/eventreminder/rsvp", data)
def participate(self):
"""Set yourself as GOING/participating to the plan."""
"""Set yourself as GOING/participating to the plan.
Example:
>>> plan.participate()
"""
return self._change_participation(True)
def decline(self):
"""Set yourself as having DECLINED the plan."""
"""Set yourself as having DECLINED the plan.
Example:
>>> plan.decline()
"""
return self._change_participation(False)

View File

@@ -70,7 +70,15 @@ class Poll:
)
def fetch_options(self) -> Sequence[PollOption]:
"""Fetch full list of `PollOption` objects on the poll."""
"""Fetch all `PollOption` objects on the poll.
The result is ordered with options with the most votes first.
Example:
>>> options = poll.fetch_options()
>>> options[0].text
"An option"
"""
data = {"question_id": self.id}
j = self.session._payload_post("/ajax/mercury/get_poll_options", data)
return [PollOption._from_graphql(m) for m in j]
@@ -83,11 +91,11 @@ class Poll:
new_options: New options to add
Example:
options = poll.fetch_options()
# Add option
poll.set_votes([o.id for o in options], new_options=["New option"])
# Remove vote from option
poll.set_votes([o.id for o in options if o.text != "Option 1"])
>>> options = poll.fetch_options()
>>> # Add option
>>> poll.set_votes([o.id for o in options], new_options=["New option"])
>>> # Remove vote from option
>>> poll.set_votes([o.id for o in options if o.text != "Option 1"])
"""
data = {"question_id": self.id}

View File

@@ -155,10 +155,17 @@ class Session:
"""Login the user, using ``email`` and ``password``.
Args:
email: Facebook ``email`` or ``id`` or ``phone number``
email: Facebook ``email``, ``id`` or ``phone number``
password: Facebook account password
on_2fa_callback: Function that will be called, in case a 2FA code is needed.
This should return the requested 2FA code.
Example:
>>> import getpass
>>> import fbchat
>>> session = fbchat.Session.login("<email or phone>", getpass.getpass())
>>> session.user_id
"1234"
"""
session = session_factory()
@@ -215,6 +222,9 @@ class Session:
Returns:
Whether the user is still logged in
Example:
>>> assert session.is_logged_in()
"""
# Send a request to the login url, to see if we're directed to the home page
url = "https://m.facebook.com/login.php?login_attempt=1"
@@ -228,6 +238,9 @@ class Session:
"""Safely log out the user.
The session object must not be used after this action has been performed!
Example:
>>> session.logout()
"""
logout_h = self._logout_h
if not logout_h:
@@ -285,6 +298,9 @@ class Session:
Returns:
A dictionary containing session cookies
Example:
>>> cookies = session.get_cookies()
"""
return self._session.cookies.get_dict()
@@ -294,6 +310,11 @@ class Session:
Args:
cookies: A dictionary containing session cookies
Example:
>>> cookies = session.get_cookies()
>>> # Store cookies somewhere, and then subsequently
>>> session = fbchat.Session.from_cookies(cookies)
"""
session = session_factory()
session.cookies = requests.cookies.merge_cookies(session.cookies, cookies)
@@ -346,7 +367,14 @@ class Session:
`files` should be a list of files that requests can upload, see
`requests.request <https://docs.python-requests.org/en/master/api/#requests.request>`_.
Return a list of tuples with a file's ID and mimetype.
Example:
>>> with open("file.txt", "rb") as f:
... (file,) = session._upload([("file.txt", f, "text/plain")])
...
>>> file
("1234", "text/plain")
Return:
Tuples with a file's ID and mimetype.
"""
file_dict = {"upload_{}".format(i): f for i, f in enumerate(files)}

View File

@@ -84,6 +84,11 @@ class ThreadABC(metaclass=abc.ABCMeta):
Args:
first: Whether to wave first or wave back
Example:
Wave back to the thread.
>>> thread.wave(False)
"""
data = self._to_send_data()
data["action_type"] = "ma-type:user-generated-message"
@@ -109,6 +114,10 @@ class ThreadABC(metaclass=abc.ABCMeta):
files: Optional tuples, each containing an uploaded file's ID and mimetype
reply_to_id: Optional message to reply to
Example:
>>> mention = fbchat.Mention(thread_id="1234", offset=5, length=2)
>>> thread.send_text("A message", mentions=[mention])
Returns:
The sent message
"""
@@ -138,6 +147,9 @@ class ThreadABC(metaclass=abc.ABCMeta):
emoji: The emoji to send
size: The size of the emoji
Example:
>>> thread.send_emoji("😀", size=fbchat.EmojiSize.LARGE)
Returns:
The sent message
"""
@@ -153,6 +165,11 @@ class ThreadABC(metaclass=abc.ABCMeta):
Args:
sticker_id: ID of the sticker to send
Example:
Send a sticker with the id "1889713947839631"
>>> thread.send_sticker("1889713947839631")
Returns:
The sent message
"""
@@ -175,6 +192,11 @@ class ThreadABC(metaclass=abc.ABCMeta):
Args:
latitude: The location latitude
longitude: The location longitude
Example:
Send a location in London, United Kingdom.
>>> thread.send_location(51.5287718, -0.2416815)
"""
self._send_location(True, latitude=latitude, longitude=longitude)
@@ -184,6 +206,11 @@ class ThreadABC(metaclass=abc.ABCMeta):
Args:
latitude: The location latitude
longitude: The location longitude
Example:
Send a pinned location in Beijing, China.
>>> thread.send_location(39.9390731, 116.117273)
"""
self._send_location(False, latitude=latitude, longitude=longitude)
@@ -191,6 +218,14 @@ class ThreadABC(metaclass=abc.ABCMeta):
"""Send files from file IDs to a thread.
`files` should be a list of tuples, with a file's ID and mimetype.
Example:
Upload and send a video to a thread.
>>> with open("video.mp4", "rb") as f:
... files = session._upload([("video.mp4", f, "video/mp4")])
>>>
>>> thread.send_files(files=files)
"""
return self.send_text(text=None, files=files)
@@ -285,11 +320,20 @@ class ThreadABC(metaclass=abc.ABCMeta):
Warning! If someone send a message to the thread that matches the query, while
we're searching, some snippets will get returned twice.
Not sure if we should handle it, Facebook's implementation doesn't...
This is fundamentally unfixable, it's just how the endpoint is implemented.
The returned message snippets are ordered by last sent.
Args:
query: Text to search for
limit: Max. number of message snippets to retrieve
Example:
Fetch the latest message in the thread that matches the query.
>>> (message,) = thread.search_messages("abc", limit=1)
>>> message.text
"Some text and abc"
"""
offset = 0
# The max limit is measured empirically to 420, safe default chosen below
@@ -330,11 +374,22 @@ class ThreadABC(metaclass=abc.ABCMeta):
]
def fetch_messages(self, limit: Optional[int]) -> Iterable["_message.Message"]:
"""Fetch messages in a thread, with most recent messages first.
"""Fetch messages in a thread.
The returned messages are ordered by most recent first.
Args:
limit: Max. number of threads to retrieve. If ``None``, all threads will be
retrieved.
Example:
>>> for message in thread.fetch_messages(limit=5)
... print(message.text)
...
A message
Another message
None
A fourth message
"""
# This is measured empirically as 210 in extreme cases, fairly safe default
# chosen below
@@ -389,6 +444,13 @@ class ThreadABC(metaclass=abc.ABCMeta):
Args:
limit: Max. number of images to retrieve. If ``None``, all images will be
retrieved.
Example:
>>> for image in thread.fetch_messages(limit=3)
... print(image.id)
...
1234
2345
"""
cursor = None
# The max limit on this request is unknown, so we set it reasonably high
@@ -407,6 +469,9 @@ class ThreadABC(metaclass=abc.ABCMeta):
Args:
user_id: User that will have their nickname changed
nickname: New nickname
Example:
>>> thread.set_nickname("1234", "A nickname")
"""
data = {
"nickname": nickname,
@@ -422,16 +487,22 @@ class ThreadABC(metaclass=abc.ABCMeta):
The new color must be one of the following::
"#0084ff", "#44bec7", "#ffc300", "#fa3c4c", "#d696bb", "#6699cc", "#13cf13",
"#ff7e29", "#e68585", "#7646ff", "#20cef5", "#67b868", "#d4a88c", "#ff5ca1",
"#a695c7", "#ff7ca8", "#1adb5b", "#f01d6a", "#ff9c19" or "#0edcde".
The default is "#0084ff".
"#0084ff", "#44bec7", "#ffc300", "#fa3c4c", "#d696bb", "#6699cc",
"#13cf13", "#ff7e29", "#e68585", "#7646ff", "#20cef5", "#67b868",
"#d4a88c", "#ff5ca1", "#a695c7", "#ff7ca8", "#1adb5b", "#f01d6a",
"#ff9c19" or "#0edcde".
This list is subject to change in the future!
The default when creating a new thread is ``"#0084ff"``.
Args:
color: New thread color
Example:
Set the thread color to "Coral Pink".
>>> thread.set_color("#e68585")
"""
if color not in SETABLE_COLORS:
raise ValueError(
@@ -459,11 +530,16 @@ class ThreadABC(metaclass=abc.ABCMeta):
# _graphql.from_doc_id("1768656253222505", {"data": data})
# )
def set_emoji(self, emoji: str):
def set_emoji(self, emoji: Optional[str]):
"""Change thread emoji.
Args:
emoji: New thread emoji
emoji: New thread emoji. If ``None``, will be set to the default "LIKE" icon
Example:
Set the thread emoji to "😊".
>>> thread.set_emoji("😊")
"""
data = {"emoji_choice": emoji, "thread_or_other_fbid": self.id}
# While changing the emoji, the Facebook web client actually sends multiple
@@ -477,6 +553,9 @@ class ThreadABC(metaclass=abc.ABCMeta):
Args:
attachment_id: Attachment ID to forward
Example:
>>> thread.forward_attachment("1234")
"""
data = {
"attachment_id": attachment_id,
@@ -497,11 +576,19 @@ class ThreadABC(metaclass=abc.ABCMeta):
j = self.session._payload_post("/ajax/messaging/typ.php", data)
def start_typing(self):
"""Set the current user to start typing in the thread."""
"""Set the current user to start typing in the thread.
Example:
>>> thread.start_typing()
"""
self._set_typing(True)
def stop_typing(self):
"""Set the current user to stop typing in the thread."""
"""Set the current user to stop typing in the thread.
Example:
>>> thread.stop_typing()
"""
self._set_typing(False)
def create_plan(
@@ -518,6 +605,9 @@ class ThreadABC(metaclass=abc.ABCMeta):
Args:
name: Name of the new plan
at: When the plan is for
Example:
>>> thread.create_plan(...)
"""
return _plan.Plan._create(self, name, at, location_name, location_id)
@@ -529,7 +619,7 @@ class ThreadABC(metaclass=abc.ABCMeta):
options: Options and whether you want to select the option
Example:
thread.create_poll("Test poll", {"Option 1": True, "Option 2": False})
>>> thread.create_poll("Test poll", {"Option 1": True, "Option 2": False})
"""
# We're using ordered dictionaries, because the Facebook endpoint that parses
# the POST parameters is badly implemented, and deals with ordering the options
@@ -557,6 +647,10 @@ class ThreadABC(metaclass=abc.ABCMeta):
Args:
duration: Time to mute, use ``None`` to mute forever
Example:
>>> import datetime
>>> thread.mute(datetime.timedelta(days=2))
"""
if duration is None:
setting = "-1"
@@ -568,7 +662,11 @@ class ThreadABC(metaclass=abc.ABCMeta):
)
def unmute(self):
"""Unmute the thread."""
"""Unmute the thread.
Example:
>>> thread.unmute()
"""
return self.mute(datetime.timedelta(0))
def _mute_reactions(self, mode: bool):

View File

@@ -36,7 +36,11 @@ GENDERS = {
@attrs_default
class User(_thread.ThreadABC):
"""Represents a Facebook user. Implements `ThreadABC`."""
"""Represents a Facebook user. Implements `ThreadABC`.
Example:
>>> user = fbchat.User(session=session, id="1234")
"""
#: The session to use when making requests.
session = attr.ib(type=_session.Session)
@@ -51,22 +55,38 @@ class User(_thread.ThreadABC):
}
def confirm_friend_request(self):
"""Confirm a friend request, adding the user to your friend list."""
"""Confirm a friend request, adding the user to your friend list.
Example:
>>> user.confirm_friend_request()
"""
data = {"to_friend": self.id, "action": "confirm"}
j = self.session._payload_post("/ajax/add_friend/action.php?dpr=1", data)
def remove_friend(self):
"""Remove the user from the client's friend list."""
"""Remove the user from the client's friend list.
Example:
>>> user.remove_friend()
"""
data = {"uid": self.id}
j = self.session._payload_post("/ajax/profile/removefriendconfirm.php", data)
def block(self):
"""Block messages from the user."""
"""Block messages from the user.
Example:
>>> user.block()
"""
data = {"fbid": self.id}
j = self.session._payload_post("/messaging/block_messages/?dpr=1", data)
def unblock(self):
"""Unblock a previously blocked user."""
"""Unblock a previously blocked user.
Example:
>>> user.unblock()
"""
data = {"fbid": self.id}
j = self.session._payload_post("/messaging/unblock_messages/?dpr=1", data)