Compare commits

...

12 Commits

Author SHA1 Message Date
Mads Marquart
f8d3b571ba Version up, thanks to @ekohilas and @kapi2289 2018-12-09 21:21:00 +01:00
Mads Marquart
64b1e52d4c Merge pull request #357 from carpedm20/fixed-listening
Fixed listening
2018-12-09 19:23:33 +01:00
Mads Marquart
b650f7ee9a Merge pull request #367 from carpedm20/fix-pytest-deprecation
Fix pytest "Applying marks directly to parameters" deprecation
2018-12-09 19:23:20 +01:00
Mads Marquart
3443a233f4 Fix pytest "Applying marks directly to parameters" deprecation 2018-12-09 15:02:48 +01:00
Mads Marquart
160386be62 Added support for request_batch parsing in _parseMessage 2018-11-09 20:08:26 +01:00
Mads Marquart
64bdde8f33 Sticky and pool parameters can be set after the inital _fetchSticky 2018-11-07 20:06:10 +01:00
Mads Marquart
89a277c354 Merge pull request #354 from ekohilas/master
separate spellchecked docs
2018-10-28 12:46:48 +01:00
Mads Marquart
8238387c7d Merge pull request #353 from ekohilas/docstrings
completed todo for graphql_requests
2018-10-28 12:45:37 +01:00
ekohilas
6c829581af completed todo for graphql_requests 2018-10-27 02:02:15 +11:00
ekohilas
d180650c1b spellchecked docs 2018-10-25 18:18:19 +11:00
Mads Marquart
772bf5518f Merge pull request #346 from kapi2289/remove_unnecessary
Remove unnecessary code
2018-10-07 16:50:31 +02:00
Kacper Ziubryniewicz
153dc0bdad Remove unnecessary code 2018-10-07 16:27:19 +02:00
12 changed files with 56 additions and 68 deletions

View File

@@ -13,7 +13,7 @@ If you are looking for information on a specific function, class, or method, thi
Client Client
------ ------
This is the main class of `fbchat`, which contains all the methods you use to interract with Facebook. This is the main class of `fbchat`, which contains all the methods you use to interact with Facebook.
You can extend this class, and overwrite the events, to provide custom event handling (mainly used while listening) You can extend this class, and overwrite the events, to provide custom event handling (mainly used while listening)
.. autoclass:: Client(email, password, user_agent=None, max_tries=5, session_cookies=None, logging_level=logging.INFO) .. autoclass:: Client(email, password, user_agent=None, max_tries=5, session_cookies=None, logging_level=logging.INFO)

View File

@@ -18,7 +18,7 @@ This will show basic usage of `fbchat`
Interacting with Threads Interacting with Threads
------------------------ ------------------------
This will interract with the thread in every way `fbchat` supports This will interact with the thread in every way `fbchat` supports
.. literalinclude:: ../examples/interract.py .. literalinclude:: ../examples/interract.py

View File

@@ -8,7 +8,7 @@ FAQ
Version X broke my installation Version X broke my installation
------------------------------- -------------------------------
We try to provide backwards compatability where possible, but since we're not part of Facebook, 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 most of the things may be broken at any point in time
Downgrade to an earlier version of fbchat, run this command Downgrade to an earlier version of fbchat, run this command

View File

@@ -6,7 +6,7 @@ Introduction
============ ============
`fbchat` uses your email and password to communicate with the Facebook server. `fbchat` uses your email and password to communicate with the Facebook server.
That means that you should always store your password in a seperate file, in case e.g. someone looks over your shoulder while you're writing code. 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 You should also make sure that the file's access control is appropriately restrictive
@@ -16,7 +16,7 @@ Logging In
---------- ----------
Simply create an instance of :class:`Client`. If you have two factor authentication enabled, type the code in the terminal prompt Simply create an instance of :class:`Client`. If you have two factor authentication enabled, type the code in the terminal prompt
(If you want to supply the code in another fasion, overwrite :func:`Client.on2FACode`):: (If you want to supply the code in another fashion, overwrite :func:`Client.on2FACode`)::
from fbchat import Client from fbchat import Client
from fbchat.models import * from fbchat.models import *
@@ -50,7 +50,7 @@ A thread can refer to two things: A Messenger group chat or a single Facebook us
:class:`models.ThreadType` is an enumerator with two values: ``USER`` and ``GROUP``. :class:`models.ThreadType` is an enumerator with two values: ``USER`` and ``GROUP``.
These will specify whether the thread is a single user chat or a group chat. These will specify whether the thread is a single user chat or a group chat.
This is required for many of `fbchat`'s functions, since Facebook differetiates between these two internally This is required for many of `fbchat`'s functions, since Facebook differentiates between these two internally
Searching for group chats and finding their ID can be done via. :func:`Client.searchForGroups`, Searching for group chats and finding their ID can be done via. :func:`Client.searchForGroups`,
and searching for users is possible via. :func:`Client.searchForUsers`. See :ref:`intro_fetching` and searching for users is possible via. :func:`Client.searchForUsers`. See :ref:`intro_fetching`
@@ -141,7 +141,7 @@ Sessions
-------- --------
`fbchat` provides functions to retrieve and set the session cookies. `fbchat` provides functions to retrieve and set the session cookies.
This will enable you to store the session cookies in a seperate file, so that you don't have to login each time you start your script. 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 :func:`Client.getSession` to retrieve the cookies:: Use :func:`Client.getSession` to retrieve the cookies::
session_cookies = client.getSession() session_cookies = client.getSession()

View File

@@ -15,7 +15,7 @@ To use the tests, copy ``tests/data.json`` to ``tests/my_data.json`` or type the
Please remember to test all supported python versions. 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'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 commandline (not including the `test_` prefix). Example:: If you only want to execute specific tests, pass the function names in the command line (not including the `test_` prefix). Example::
$ python tests.py sendMessage sessions sendEmoji $ python tests.py sendMessage sessions sendEmoji

View File

@@ -15,7 +15,7 @@ from __future__ import unicode_literals
from .client import * from .client import *
__title__ = 'fbchat' __title__ = 'fbchat'
__version__ = '1.4.1' __version__ = '1.4.2'
__description__ = 'Facebook Chat (Messenger) for Python' __description__ = 'Facebook Chat (Messenger) for Python'
__copyright__ = 'Copyright 2015 - 2018 by Taehoon Kim' __copyright__ = 'Copyright 2015 - 2018 by Taehoon Kim'

View File

@@ -168,10 +168,12 @@ class Client(object):
def graphql_requests(self, *queries): def graphql_requests(self, *queries):
""" """
.. todo:: :param queries: Zero or more GraphQL objects
Documenting this :type queries: GraphQL
:raises: FBchatException if request failed :raises: FBchatException if request failed
:return: A tuple containing json graphql queries
:rtype: tuple
""" """
return tuple(self._graphql({ return tuple(self._graphql({
@@ -238,22 +240,6 @@ class Client(object):
self.payloadDefault['ttstamp'] = self.ttstamp self.payloadDefault['ttstamp'] = self.ttstamp
self.payloadDefault['fb_dtsg'] = self.fb_dtsg self.payloadDefault['fb_dtsg'] = self.fb_dtsg
self.form = {
'channel' : self.user_channel,
'partition' : '-2',
'clientid' : self.client_id,
'viewer_uid' : self.uid,
'uid' : self.uid,
'state' : 'active',
'format' : 'json',
'idle' : 0,
'cap' : '8'
}
self.prev = now()
self.tmp_prev = now()
self.last_sync = now()
def _login(self): def _login(self):
if not (self.email and self.password): if not (self.email and self.password):
raise FBchatUserError("Email and password not found.") raise FBchatUserError("Email and password not found.")
@@ -457,7 +443,8 @@ class Client(object):
return given_thread_id, given_thread_type return given_thread_id, given_thread_type
def setDefaultThread(self, thread_id, thread_type): def setDefaultThread(self, thread_id, thread_type):
"""Sets default thread to send messages to """
Sets default thread to send messages to
:param thread_id: User/Group ID to default to. See :ref:`intro_threads` :param thread_id: User/Group ID to default to. See :ref:`intro_threads`
:param thread_type: See :ref:`intro_threads` :param thread_type: See :ref:`intro_threads`
@@ -1969,48 +1956,32 @@ class Client(object):
LISTEN METHODS LISTEN METHODS
""" """
def _ping(self, sticky, pool): def _ping(self):
data = { data = {
'channel': self.user_channel, 'channel': self.user_channel,
'clientid': self.client_id, 'clientid': self.client_id,
'partition': -2, 'partition': -2,
'cap': 0, 'cap': 0,
'uid': self.uid, 'uid': self.uid,
'sticky_token': sticky, 'sticky_token': self.sticky,
'sticky_pool': pool, 'sticky_pool': self.pool,
'viewer_uid': self.uid, 'viewer_uid': self.uid,
'state': 'active', 'state': 'active',
} }
self._get(self.req_url.PING, data, fix_request=True, as_json=False) self._get(self.req_url.PING, data, fix_request=True, as_json=False)
def _fetchSticky(self): def _pullMessage(self, markAlive=True):
"""Call pull api to get sticky and pool parameter, newer api needs these parameters to work"""
data = {
"msgs_recv": 0,
"channel": self.user_channel,
"clientid": self.client_id
}
j = self._get(self.req_url.STICKY, data, fix_request=True, as_json=True)
if j.get('lb_info') is None:
raise FBchatException('Missing lb_info: {}'.format(j))
return j['lb_info']['sticky'], j['lb_info']['pool']
def _pullMessage(self, sticky, pool, markAlive=True):
"""Call pull api with seq value to get message data.""" """Call pull api with seq value to get message data."""
data = { data = {
"msgs_recv": 0, "msgs_recv": 0,
"sticky_token": sticky, "sticky_token": self.sticky,
"sticky_pool": pool, "sticky_pool": self.pool,
"clientid": self.client_id, "clientid": self.client_id,
'state': 'active' if markAlive else 'offline', 'state': 'active' if markAlive else 'offline',
} }
j = self._get(ReqUrl.STICKY, data, fix_request=True, as_json=True) j = self._get(self.req_url.STICKY, data, fix_request=True, as_json=True)
self.seq = j.get('seq', '0') self.seq = j.get('seq', '0')
return j return j
@@ -2018,6 +1989,14 @@ class Client(object):
def _parseMessage(self, content): def _parseMessage(self, content):
"""Get message and author name from content. May contain multiple messages in the content.""" """Get message and author name from content. May contain multiple messages in the content."""
if 'lb_info' in content:
self.sticky = content['lb_info']['sticky']
self.pool = content['lb_info']['pool']
if 'batches' in content:
for batch in content['batches']:
self._parseMessage(batch)
if 'ms' not in content: return if 'ms' not in content: return
for m in content["ms"]: for m in content["ms"]:
@@ -2363,7 +2342,6 @@ class Client(object):
:raises: FBchatException if request failed :raises: FBchatException if request failed
""" """
self.listening = True self.listening = True
self.sticky, self.pool = self._fetchSticky()
def doOneListen(self, markAlive=True): def doOneListen(self, markAlive=True):
""" """
@@ -2377,8 +2355,8 @@ class Client(object):
""" """
try: try:
if markAlive: if markAlive:
self._ping(self.sticky, self.pool) self._ping()
content = self._pullMessage(self.sticky, self.pool, markAlive) content = self._pullMessage(markAlive)
if content: if content:
self._parseMessage(content) self._parseMessage(content)
except KeyboardInterrupt: except KeyboardInterrupt:

View File

@@ -20,7 +20,9 @@ def group(pytestconfig):
return {"id": load_variable("group_id", pytestconfig.cache), "type": ThreadType.GROUP} return {"id": load_variable("group_id", pytestconfig.cache), "type": ThreadType.GROUP}
@pytest.fixture(scope="session", params=["user", "group", pytest.mark.xfail("none")]) @pytest.fixture(scope="session", params=[
"user", "group", pytest.param("none", marks=[pytest.mark.xfail()])
])
def thread(request, user, group): def thread(request, user, group):
return { return {
"user": user, "user": user,

View File

@@ -11,8 +11,14 @@ from time import time
@pytest.fixture(scope="module", params=[ @pytest.fixture(scope="module", params=[
Plan(int(time()) + 100, random_hex()), Plan(int(time()) + 100, random_hex()),
pytest.mark.xfail(Plan(int(time()), random_hex()), raises=FBchatFacebookError), pytest.param(
pytest.mark.xfail(Plan(0, None)), Plan(int(time()), random_hex()),
marks=[pytest.mark.xfail(raises=FBchatFacebookError)]
),
pytest.param(
Plan(0, None),
marks=[pytest.mark.xfail()],
),
]) ])
def plan_data(request, client, user, thread, catch_event, compare): def plan_data(request, client, user, thread, catch_event, compare):
with catch_event("onPlanCreated") as x: with catch_event("onPlanCreated") as x:

View File

@@ -26,7 +26,9 @@ from utils import random_hex, subset
PollOption(random_hex()), PollOption(random_hex()),
PollOption(random_hex()), PollOption(random_hex()),
]), ]),
pytest.mark.xfail(Poll(title=None, options=[]), raises=ValueError), pytest.param(
Poll(title=None, options=[]), marks=[pytest.mark.xfail(raises=ValueError)]
),
]) ])
def poll_data(request, client1, group, catch_event): def poll_data(request, client1, group, catch_event):
with catch_event("onPollCreated") as x: with catch_event("onPollCreated") as x:

View File

@@ -72,8 +72,8 @@ def test_change_nickname(client, client_all, catch_event, compare):
"😂", "😂",
"😕", "😕",
"😍", "😍",
pytest.mark.xfail("🙃", raises=FBchatFacebookError), pytest.param("🙃", marks=[pytest.mark.xfail(raises=FBchatFacebookError)]),
pytest.mark.xfail("not an emoji", raises=FBchatFacebookError) pytest.param("not an emoji", marks=[pytest.mark.xfail(raises=FBchatFacebookError)]),
]) ])
def test_change_emoji(client, catch_event, compare, emoji): def test_change_emoji(client, catch_event, compare, emoji):
with catch_event("onEmojiChange") as x: with catch_event("onEmojiChange") as x:
@@ -101,7 +101,7 @@ def test_change_image_remote(client1, group, catch_event):
[ [
x x
if x in [ThreadColor.MESSENGER_BLUE, ThreadColor.PUMPKIN] if x in [ThreadColor.MESSENGER_BLUE, ThreadColor.PUMPKIN]
else pytest.mark.expensive(x) else pytest.param(x, marks=[pytest.mark.expensive()])
for x in ThreadColor for x in ThreadColor
], ],
) )

View File

@@ -23,15 +23,15 @@ EMOJI_LIST = [
("😆", EmojiSize.LARGE), ("😆", EmojiSize.LARGE),
# These fail in `catch_event` because the emoji is made into a sticker # These fail in `catch_event` because the emoji is made into a sticker
# This should be fixed # This should be fixed
pytest.mark.xfail((None, EmojiSize.SMALL)), pytest.param(None, EmojiSize.SMALL, marks=[pytest.mark.xfail()]),
pytest.mark.xfail((None, EmojiSize.MEDIUM)), pytest.param(None, EmojiSize.MEDIUM, marks=[pytest.mark.xfail()]),
pytest.mark.xfail((None, EmojiSize.LARGE)), pytest.param(None, EmojiSize.LARGE, marks=[pytest.mark.xfail()]),
] ]
STICKER_LIST = [ STICKER_LIST = [
Sticker("767334476626295"), Sticker("767334476626295"),
pytest.mark.xfail(Sticker("0"), raises=FBchatFacebookError), pytest.param(Sticker("0"), marks=[pytest.mark.xfail(raises=FBchatFacebookError)]),
pytest.mark.xfail(Sticker(None), raises=FBchatFacebookError), pytest.param(Sticker(None), marks=[pytest.mark.xfail(raises=FBchatFacebookError)]),
] ]
TEXT_LIST = [ TEXT_LIST = [
@@ -40,8 +40,8 @@ TEXT_LIST = [
"\\\n\t%?&'\"", "\\\n\t%?&'\"",
"ˁҭʚ¹Ʋջوװ՞ޱɣࠚԹБɑȑңКએ֭ʗыԈٌʼőԈ×௴nચϚࠖణٔє܅Ԇޑط", "ˁҭʚ¹Ʋջوװ՞ޱɣࠚԹБɑȑңКએ֭ʗыԈٌʼőԈ×௴nચϚࠖణٔє܅Ԇޑط",
"a" * 20000, # Maximum amount of characters you can send "a" * 20000, # Maximum amount of characters you can send
pytest.mark.xfail("a" * 20001, raises=FBchatFacebookError), pytest.param("a" * 20001, marks=[pytest.mark.xfail(raises=FBchatFacebookError)]),
pytest.mark.xfail(None, raises=FBchatFacebookError), pytest.param(None, marks=[pytest.mark.xfail(raises=FBchatFacebookError)]),
] ]