From 348db90f7b9de1d0c1239d55455c6ef09e4c1d69 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 29 Aug 2018 23:50:35 +0200 Subject: [PATCH] Fixes for Python 2.7 compatibility --- fbchat/client.py | 27 ++++++++++++++++++--------- fbchat/graphql.py | 8 +++++++- tests/test_polls.py | 3 ++- tests/test_send.py | 6 +++--- 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/fbchat/client.py b/fbchat/client.py index 6de91fd..1aaea7c 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -7,6 +7,7 @@ from uuid import uuid1 from random import choice from bs4 import BeautifulSoup as bs from mimetypes import guess_type +from collections import OrderedDict from .utils import * from .models import * from .graphql import * @@ -55,7 +56,8 @@ class Client(object): self._session = requests.session() self.req_counter = 1 self.seq = "0" - self.payloadDefault = {} + # See `createPoll` for the reason for using `OrderedDict` here + self.payloadDefault = OrderedDict() self.client = 'mercury' self.default_thread_id = None self.default_thread_type = None @@ -195,14 +197,14 @@ class Client(object): """ def _resetValues(self): - self.payloadDefault={} + self.payloadDefault = OrderedDict() self._session = requests.session() self.req_counter = 1 self.seq = "0" self.uid = None def _postLogin(self): - self.payloadDefault = {} + self.payloadDefault = OrderedDict() self.client_id = hex(int(random()*2147483648))[2:] self.start_time = now() self.uid = self._session.cookies.get_dict().get('c_user') @@ -610,7 +612,7 @@ class Client(object): "snippetOffset": offset, "snippetLimit": limit, "identifier": "thread_fbid", - "thread_fbid": thread_id + "thread_fbid": thread_id, } j = self._post(self.req_url.SEARCH_MESSAGES, data, fix_request=True, as_json=True) @@ -643,7 +645,7 @@ class Client(object): def search(self, query, fetch_messages=False, thread_limit=5, message_limit=5): """ Searches for messages in all threads - + :param query: Text to search for :param fetch_messages: Whether to fetch :class:`models.Message` objects or IDs only :param thread_limit: Max. number of threads to retrieve @@ -1624,10 +1626,17 @@ class Client(object): """ thread_id, thread_type = self._getThread(thread_id, None) - data = { - "question_text": poll.title, - "target_id": thread_id - } + # We're using ordered dicts, because the Facebook endpoint that parses the POST + # parameters is badly implemented, and deals with ordering the options wrongly. + # This also means we had to change `client.payloadDefault` to an ordered dict, + # since that's being copied in between this point and the `requests` call + # + # If you can find a way to fix this for the endpoint, or if you find another + # endpoint, please do suggest it ;) + data = OrderedDict([ + ("question_text", poll.title), + ("target_id", thread_id), + ]) for i, option in enumerate(poll.options): data["option_text_array[{}]".format(i)] = option.text diff --git a/fbchat/graphql.py b/fbchat/graphql.py index 75c386a..ba73903 100644 --- a/fbchat/graphql.py +++ b/fbchat/graphql.py @@ -138,9 +138,15 @@ def graphql_to_poll(a): return rtn def graphql_to_poll_option(a): + if a.get('viewer_has_voted') is None: + vote = None + elif isinstance(a['viewer_has_voted'], bool): + vote = a['viewer_has_voted'] + else: + vote = a['viewer_has_voted'] == 'true' rtn = PollOption( text=a.get('text'), - vote=a.get('viewer_has_voted') == 'true' if isinstance(a.get('viewer_has_voted'), str) else a.get('viewer_has_voted') + vote=vote ) rtn.uid = int(a["id"]) rtn.voters = [m.get('node').get('id') for m in a.get('voters').get('edges')] if isinstance(a.get('voters'), dict) else a.get('voters') diff --git a/tests/test_polls.py b/tests/test_polls.py index ef53917..96dab76 100644 --- a/tests/test_polls.py +++ b/tests/test_polls.py @@ -45,7 +45,7 @@ def test_create_poll(client1, group, catch_event, poll_data): ) assert subset(vars(event["poll"]), title=poll.title, options_count=len(poll.options)) for recv_option in event["poll"].options: # The recieved options may not be the full list - old_option = list(filter(lambda o: o.text == recv_option.text, poll.options))[0] + old_option, = list(filter(lambda o: o.text == recv_option.text, poll.options)) voters = [client1.uid] if old_option.vote else [] assert subset(vars(recv_option), voters=voters, votes_count=len(voters), vote=False) @@ -57,6 +57,7 @@ def test_fetch_poll_options(client1, group, catch_event, poll_data): assert subset(vars(option)) +@pytest.mark.trylast def test_update_poll_vote(client1, group, catch_event, poll_data): event, poll, options = poll_data new_vote_ids = [o.uid for o in options[0:len(options):2] if not o.vote] diff --git a/tests/test_send.py b/tests/test_send.py index 887d8f0..3b53059 100644 --- a/tests/test_send.py +++ b/tests/test_send.py @@ -54,7 +54,7 @@ def test_send_sticker(client, catch_event, compare, sticker): assert subset(vars(x.res["message_object"].sticker), uid=sticker.uid) -# Kept for backwards compatability +# Kept for backwards compatibility @pytest.mark.parametrize( "method_name, url", [ @@ -80,7 +80,7 @@ def test_send_local_files(client, catch_event, compare): text = "Files sent locally" with catch_event("onMessage") as x: mid = client.sendLocalFiles( - [path.join(path.dirname(__file__), "resources", x) for x in files], + [path.join(path.dirname(__file__), "resources", f) for f in files], message=Message(text), ) @@ -95,7 +95,7 @@ def test_send_remote_files(client, catch_event, compare): text = "Files sent from remote" with catch_event("onMessage") as x: mid = client.sendRemoteFiles( - ["https://github.com/carpedm20/fbchat/raw/master/tests/{}".format(x) for x in files], + ["https://github.com/carpedm20/fbchat/raw/master/tests/{}".format(f) for f in files], message=Message(text), )