diff --git a/docs/conf.py b/docs/conf.py index e9ca33e..9a50500 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,7 +20,7 @@ import os import sys -sys.path.insert(0, os.path.abspath('..')) +sys.path.insert(0, os.path.abspath("..")) import fbchat import tests @@ -37,27 +37,27 @@ from fbchat import __copyright__, __author__, __version__, __description__ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.intersphinx', - 'sphinx.ext.todo', - 'sphinx.ext.viewcode', + "sphinx.ext.autodoc", + "sphinx.ext.intersphinx", + "sphinx.ext.todo", + "sphinx.ext.viewcode", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] -source_suffix = '.rst' +source_suffix = ".rst" # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = 'fbchat' -title = 'fbchat Documentation' +project = "fbchat" +title = "fbchat Documentation" copyright = __copyright__ author = __author__ description = __description__ @@ -81,10 +81,10 @@ language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True @@ -96,7 +96,7 @@ todo_include_todos = True # a list of builtin themes. # -html_theme = 'alabaster' +html_theme = "alabaster" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -107,13 +107,13 @@ html_theme = 'alabaster' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. -htmlhelp_basename = project + 'doc' +htmlhelp_basename = project + "doc" # -- Options for LaTeX output --------------------------------------------- @@ -136,7 +136,7 @@ latex_elements = { # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). -latex_documents = [(master_doc, project + '.tex', title, author, 'manual')] +latex_documents = [(master_doc, project + ".tex", title, author, "manual")] # -- Options for manual page output --------------------------------------- @@ -152,27 +152,27 @@ man_pages = [(master_doc, project, title, [author], 1)] # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, project, title, author, project, description, 'Miscellaneous') + (master_doc, project, title, author, project, description, "Miscellaneous") ] # Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'https://docs.python.org/3/': None} +intersphinx_mapping = {"https://docs.python.org/3/": None} add_function_parentheses = False html_theme_options = { - 'show_powered_by': False, - 'github_user': 'carpedm20', - 'github_repo': project, - 'github_banner': True, - 'show_related': False, + "show_powered_by": False, + "github_user": "carpedm20", + "github_repo": project, + "github_banner": True, + "show_related": False, } -html_sidebars = {'**': ['sidebar.html', 'searchbox.html']} +html_sidebars = {"**": ["sidebar.html", "searchbox.html"]} html_show_sphinx = False html_show_sourcelink = False -autoclass_content = 'init' +autoclass_content = "init" html_short_title = description diff --git a/examples/basic_usage.py b/examples/basic_usage.py index aa2afd1..c712dcd 100644 --- a/examples/basic_usage.py +++ b/examples/basic_usage.py @@ -3,10 +3,10 @@ from fbchat import Client from fbchat.models import * -client = Client('', '') +client = Client("", "") -print('Own id: {}'.format(client.uid)) +print("Own id: {}".format(client.uid)) -client.send(Message(text='Hi me!'), thread_id=client.uid, thread_type=ThreadType.USER) +client.send(Message(text="Hi me!"), thread_id=client.uid, thread_type=ThreadType.USER) client.logout() diff --git a/examples/fetch.py b/examples/fetch.py index 878300d..5db97b8 100644 --- a/examples/fetch.py +++ b/examples/fetch.py @@ -3,7 +3,7 @@ from fbchat import Client from fbchat.models import * -client = Client('', '') +client = Client("", "") # Fetches a list of all users you're currently chatting with, as `User` objects users = client.fetchAllUsers() @@ -13,9 +13,9 @@ print("users' names: {}".format([user.name for user in users])) # If we have a user id, we can use `fetchUserInfo` to fetch a `User` object -user = client.fetchUserInfo('')[''] +user = client.fetchUserInfo("")[""] # We can also query both mutiple users together, which returns list of `User` objects -users = client.fetchUserInfo('<1st user id>', '<2nd user id>', '<3rd user id>') +users = client.fetchUserInfo("<1st user id>", "<2nd user id>", "<3rd user id>") print("user's name: {}".format(user.name)) print("users' names: {}".format([users[k].name for k in users])) @@ -23,9 +23,9 @@ print("users' names: {}".format([users[k].name for k in users])) # `searchForUsers` searches for the user and gives us a list of the results, # and then we just take the first one, aka. the most likely one: -user = client.searchForUsers('')[0] +user = client.searchForUsers("")[0] -print('user ID: {}'.format(user.uid)) +print("user ID: {}".format(user.uid)) print("user's name: {}".format(user.name)) print("user's photo: {}".format(user.photo)) print("Is user client's friend: {}".format(user.is_friend)) @@ -40,7 +40,7 @@ print("Threads: {}".format(threads)) # Gets the last 10 messages sent to the thread -messages = client.fetchThreadMessages(thread_id='', limit=10) +messages = client.fetchThreadMessages(thread_id="", limit=10) # Since the message come in reversed order, reverse them messages.reverse() @@ -50,13 +50,13 @@ for message in messages: # If we have a thread id, we can use `fetchThreadInfo` to fetch a `Thread` object -thread = client.fetchThreadInfo('')[''] +thread = client.fetchThreadInfo("")[""] print("thread's name: {}".format(thread.name)) print("thread's type: {}".format(thread.type)) # `searchForThreads` searches works like `searchForUsers`, but gives us a list of threads instead -thread = client.searchForThreads('')[0] +thread = client.searchForThreads("")[0] print("thread's name: {}".format(thread.name)) print("thread's type: {}".format(thread.type)) diff --git a/examples/interract.py b/examples/interract.py index bb5771f..ec91dbf 100644 --- a/examples/interract.py +++ b/examples/interract.py @@ -5,11 +5,11 @@ from fbchat.models import * client = Client("", "") -thread_id = '1234567890' +thread_id = "1234567890" thread_type = ThreadType.GROUP # Will send a message to the thread -client.send(Message(text=''), thread_id=thread_id, thread_type=thread_type) +client.send(Message(text=""), thread_id=thread_id, thread_type=thread_type) # Will send the default `like` emoji client.send( @@ -18,14 +18,14 @@ client.send( # Will send the emoji `👍` client.send( - Message(text='👍', emoji_size=EmojiSize.LARGE), + Message(text="👍", emoji_size=EmojiSize.LARGE), thread_id=thread_id, thread_type=thread_type, ) # Will send the sticker with ID `767334476626295` client.send( - Message(sticker=Sticker('767334476626295')), + Message(sticker=Sticker("767334476626295")), thread_id=thread_id, thread_type=thread_type, ) @@ -33,7 +33,7 @@ client.send( # Will send a message with a mention client.send( Message( - text='This is a @mention', mentions=[Mention(thread_id, offset=10, length=8)] + text="This is a @mention", mentions=[Mention(thread_id, offset=10, length=8)] ), thread_id=thread_id, thread_type=thread_type, @@ -41,16 +41,16 @@ client.send( # Will send the image located at `` client.sendLocalImage( - '', - message=Message(text='This is a local image'), + "", + message=Message(text="This is a local image"), thread_id=thread_id, thread_type=thread_type, ) # Will download the image at the url ``, and then send it client.sendRemoteImage( - '', - message=Message(text='This is a remote image'), + "", + message=Message(text="This is a remote image"), thread_id=thread_id, thread_type=thread_type, ) @@ -59,24 +59,24 @@ client.sendRemoteImage( # Only do these actions if the thread is a group if thread_type == ThreadType.GROUP: # Will remove the user with ID `` from the thread - client.removeUserFromGroup('', thread_id=thread_id) + client.removeUserFromGroup("", thread_id=thread_id) # Will add the user with ID `` to the thread - client.addUsersToGroup('', thread_id=thread_id) + client.addUsersToGroup("", thread_id=thread_id) # Will add the users with IDs `<1st user id>`, `<2nd user id>` and `<3th user id>` to the thread client.addUsersToGroup( - ['<1st user id>', '<2nd user id>', '<3rd user id>'], thread_id=thread_id + ["<1st user id>", "<2nd user id>", "<3rd user id>"], thread_id=thread_id ) # Will change the nickname of the user `` to `` client.changeNickname( - '', '', thread_id=thread_id, thread_type=thread_type + "", "", thread_id=thread_id, thread_type=thread_type ) # Will change the title of the thread to `` -client.changeThreadTitle('<title>', thread_id=thread_id, thread_type=thread_type) +client.changeThreadTitle("<title>", thread_id=thread_id, thread_type=thread_type) # Will set the typing status of the thread to `TYPING` client.setTypingStatus( @@ -87,7 +87,7 @@ client.setTypingStatus( client.changeThreadColor(ThreadColor.MESSENGER_BLUE, thread_id=thread_id) # Will change the thread emoji to `👍` -client.changeThreadEmoji('👍', thread_id=thread_id) +client.changeThreadEmoji("👍", thread_id=thread_id) # Will react to a message with a 😍 emoji -client.reactToMessage('<message id>', MessageReaction.LOVE) +client.reactToMessage("<message id>", MessageReaction.LOVE) diff --git a/examples/keepbot.py b/examples/keepbot.py index e881940..b3e835e 100644 --- a/examples/keepbot.py +++ b/examples/keepbot.py @@ -4,17 +4,17 @@ from fbchat import log, Client from fbchat.models import * # Change this to your group id -old_thread_id = '1234567890' +old_thread_id = "1234567890" # Change these to match your liking old_color = ThreadColor.MESSENGER_BLUE -old_emoji = '👍' -old_title = 'Old group chat name' +old_emoji = "👍" +old_title = "Old group chat name" old_nicknames = { - '12345678901': "User nr. 1's nickname", - '12345678902': "User nr. 2's nickname", - '12345678903': "User nr. 3's nickname", - '12345678904': "User nr. 4's nickname", + "12345678901": "User nr. 1's nickname", + "12345678902": "User nr. 2's nickname", + "12345678903": "User nr. 3's nickname", + "12345678904": "User nr. 4's nickname", } diff --git a/examples/removebot.py b/examples/removebot.py index b14532b..91979ff 100644 --- a/examples/removebot.py +++ b/examples/removebot.py @@ -7,8 +7,8 @@ from fbchat.models import * class RemoveBot(Client): def onMessage(self, author_id, message_object, thread_id, thread_type, **kwargs): # We can only kick people from group chats, so no need to try if it's a user chat - if message_object.text == 'Remove me!' and thread_type == ThreadType.GROUP: - log.info('{} will be removed from {}'.format(author_id, thread_id)) + if message_object.text == "Remove me!" and thread_type == ThreadType.GROUP: + log.info("{} will be removed from {}".format(author_id, thread_id)) self.removeUserFromGroup(author_id, thread_id=thread_id) else: # Sends the data to the inherited onMessage, so that we can still see when a message is recieved diff --git a/fbchat/__init__.py b/fbchat/__init__.py index 4e5a05a..c9497e6 100644 --- a/fbchat/__init__.py +++ b/fbchat/__init__.py @@ -9,14 +9,14 @@ from __future__ import unicode_literals from .client import * -__title__ = 'fbchat' -__version__ = '1.6.1' -__description__ = 'Facebook Chat (Messenger) for Python' +__title__ = "fbchat" +__version__ = "1.6.1" +__description__ = "Facebook Chat (Messenger) for Python" -__copyright__ = 'Copyright 2015 - 2019 by Taehoon Kim' -__license__ = 'BSD 3-Clause' +__copyright__ = "Copyright 2015 - 2019 by Taehoon Kim" +__license__ = "BSD 3-Clause" -__author__ = 'Taehoon Kim; Moreels Pieter-Jan; Mads Marquart' -__email__ = 'carpedm20@gmail.com' +__author__ = "Taehoon Kim; Moreels Pieter-Jan; Mads Marquart" +__email__ = "carpedm20@gmail.com" -__all__ = ['Client'] +__all__ = ["Client"] diff --git a/fbchat/client.py b/fbchat/client.py index af85344..9818907 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -67,7 +67,7 @@ class Client(object): self.seq = "0" # See `createPoll` for the reason for using `OrderedDict` here self.payloadDefault = OrderedDict() - self.client = 'mercury' + self.client = "mercury" self.default_thread_id = None self.default_thread_type = None self.req_url = ReqUrl() @@ -78,11 +78,11 @@ class Client(object): user_agent = choice(USER_AGENTS) self._header = { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Referer': self.req_url.BASE, - 'Origin': self.req_url.BASE, - 'User-Agent': user_agent, - 'Connection': 'keep-alive', + "Content-Type": "application/x-www-form-urlencoded", + "Referer": self.req_url.BASE, + "Origin": self.req_url.BASE, + "User-Agent": user_agent, + "Connection": "keep-alive", } handler.setLevel(logging_level) @@ -109,8 +109,8 @@ class Client(object): payload = self.payloadDefault.copy() if query: payload.update(query) - payload['__req'] = str_base(self.req_counter, 36) - payload['seq'] = self.seq + payload["__req"] = str_base(self.req_counter, 36) + payload["seq"] = self.seq self.req_counter += 1 return payload @@ -120,8 +120,8 @@ class Client(object): This error usually happens after 1-2 days of inactivity It may be a bad idea to do this in an exception handler, if you have a better method, please suggest it! """ - if error_code == '1357004': - log.warning('Got error #1357004. Doing a _postLogin, and resending request') + if error_code == "1357004": + log.warning("Got error #1357004. Doing a _postLogin, and resending request") self._postLogin() return True return False @@ -236,7 +236,7 @@ class Client(object): payload = self._generatePayload(query) # Removes 'Content-Type' from the header headers = dict( - (i, self._header[i]) for i in self._header if i != 'Content-Type' + (i, self._header[i]) for i in self._header if i != "Content-Type" ) r = self._session.post( url, @@ -276,9 +276,9 @@ class Client(object): return tuple( self._graphql( { - 'method': 'GET', - 'response_format': 'json', - 'queries': graphql_queries_to_json(*queries), + "method": "GET", + "response_format": "json", + "queries": graphql_queries_to_json(*queries), } ) ) @@ -310,37 +310,37 @@ class Client(object): 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') + self.uid = self._session.cookies.get_dict().get("c_user") if self.uid is None: - raise FBchatException('Could not find c_user cookie') + raise FBchatException("Could not find c_user cookie") self.uid = str(self.uid) self.user_channel = "p_" + self.uid - self.ttstamp = '' + self.ttstamp = "" r = self._get(self.req_url.BASE) soup = bs(r.text, "html.parser") - fb_dtsg_element = soup.find("input", {'name': 'fb_dtsg'}) + fb_dtsg_element = soup.find("input", {"name": "fb_dtsg"}) if fb_dtsg_element: - self.fb_dtsg = fb_dtsg_element['value'] + self.fb_dtsg = fb_dtsg_element["value"] else: self.fb_dtsg = re.search(r'name="fb_dtsg" value="(.*?)"', r.text).group(1) - fb_h_element = soup.find("input", {'name': 'h'}) + fb_h_element = soup.find("input", {"name": "h"}) if fb_h_element: - self.fb_h = fb_h_element['value'] + self.fb_h = fb_h_element["value"] for i in self.fb_dtsg: self.ttstamp += str(ord(i)) - self.ttstamp += '2' + self.ttstamp += "2" # Set default payload - self.payloadDefault['__rev'] = int( + self.payloadDefault["__rev"] = int( r.text.split('"client_revision":', 1)[1].split(",", 1)[0] ) - self.payloadDefault['__user'] = self.uid - self.payloadDefault['__a'] = '1' - self.payloadDefault['ttstamp'] = self.ttstamp - self.payloadDefault['fb_dtsg'] = self.fb_dtsg + self.payloadDefault["__user"] = self.uid + self.payloadDefault["__a"] = "1" + self.payloadDefault["ttstamp"] = self.ttstamp + self.payloadDefault["fb_dtsg"] = self.fb_dtsg def _login(self): if not (self.email and self.password): @@ -348,25 +348,25 @@ class Client(object): soup = bs(self._get(self.req_url.MOBILE).text, "html.parser") data = dict( - (elem['name'], elem['value']) + (elem["name"], elem["value"]) for elem in soup.findAll("input") - if elem.has_attr('value') and elem.has_attr('name') + if elem.has_attr("value") and elem.has_attr("name") ) - data['email'] = self.email - data['pass'] = self.password - data['login'] = 'Log In' + data["email"] = self.email + data["pass"] = self.password + data["login"] = "Log In" r = self._cleanPost(self.req_url.LOGIN, data) # Usually, 'Checkpoint' will refer to 2FA - if 'checkpoint' in r.url and ('id="approvals_code"' in r.text.lower()): + if "checkpoint" in r.url and ('id="approvals_code"' in r.text.lower()): r = self._2FA(r) # Sometimes Facebook tries to show the user a "Save Device" dialog - if 'save-device' in r.url: + if "save-device" in r.url: r = self._cleanGet(self.req_url.SAVE_DEVICE) - if 'home' in r.url: + if "home" in r.url: self._postLogin() return True, r.url else: @@ -378,56 +378,56 @@ class Client(object): s = self.on2FACode() - data['approvals_code'] = s - data['fb_dtsg'] = soup.find("input", {'name': 'fb_dtsg'})['value'] - data['nh'] = soup.find("input", {'name': 'nh'})['value'] - data['submit[Submit Code]'] = 'Submit Code' - data['codes_submitted'] = 0 - log.info('Submitting 2FA code.') + data["approvals_code"] = s + data["fb_dtsg"] = soup.find("input", {"name": "fb_dtsg"})["value"] + data["nh"] = soup.find("input", {"name": "nh"})["value"] + data["submit[Submit Code]"] = "Submit Code" + data["codes_submitted"] = 0 + log.info("Submitting 2FA code.") r = self._cleanPost(self.req_url.CHECKPOINT, data) - if 'home' in r.url: + if "home" in r.url: return r - del (data['approvals_code']) - del (data['submit[Submit Code]']) - del (data['codes_submitted']) + del (data["approvals_code"]) + del (data["submit[Submit Code]"]) + del (data["codes_submitted"]) - data['name_action_selected'] = 'save_device' - data['submit[Continue]'] = 'Continue' + data["name_action_selected"] = "save_device" + data["submit[Continue]"] = "Continue" log.info( - 'Saving browser.' + "Saving browser." ) # At this stage, we have dtsg, nh, name_action_selected, submit[Continue] r = self._cleanPost(self.req_url.CHECKPOINT, data) - if 'home' in r.url: + if "home" in r.url: return r - del (data['name_action_selected']) + del (data["name_action_selected"]) log.info( - 'Starting Facebook checkup flow.' + "Starting Facebook checkup flow." ) # At this stage, we have dtsg, nh, submit[Continue] r = self._cleanPost(self.req_url.CHECKPOINT, data) - if 'home' in r.url: + if "home" in r.url: return r - del (data['submit[Continue]']) - data['submit[This was me]'] = 'This Was Me' + del (data["submit[Continue]"]) + data["submit[This was me]"] = "This Was Me" log.info( - 'Verifying login attempt.' + "Verifying login attempt." ) # At this stage, we have dtsg, nh, submit[This was me] r = self._cleanPost(self.req_url.CHECKPOINT, data) - if 'home' in r.url: + if "home" in r.url: return r - del (data['submit[This was me]']) - data['submit[Continue]'] = 'Continue' - data['name_action_selected'] = 'save_device' + del (data["submit[This was me]"]) + data["submit[Continue]"] = "Continue" + data["name_action_selected"] = "save_device" log.info( - 'Saving device again.' + "Saving device again." ) # At this stage, we have dtsg, nh, submit[Continue], name_action_selected r = self._cleanPost(self.req_url.CHECKPOINT, data) return r @@ -441,7 +441,7 @@ class Client(object): """ # Send a request to the login url, to see if we're directed to the home page r = self._cleanGet(self.req_url.LOGIN, allow_redirects=False) - return 'Location' in r.headers and 'home' in r.headers['Location'] + return "Location" in r.headers and "home" in r.headers["Location"] def getSession(self): """Retrieves session cookies @@ -461,7 +461,7 @@ class Client(object): """ # Quick check to see if session_cookies is formatted properly - if not session_cookies or 'c_user' not in session_cookies: + if not session_cookies or "c_user" not in session_cookies: return False try: @@ -471,7 +471,7 @@ class Client(object): ) self._postLogin() except Exception as e: - log.exception('Failed loading session') + log.exception("Failed loading session") self._resetValues() return False return True @@ -489,10 +489,10 @@ class Client(object): self.onLoggingIn(email=email) if max_tries < 1: - raise FBchatUserError('Cannot login: max_tries should be at least one') + raise FBchatUserError("Cannot login: max_tries should be at least one") if not (email and password): - raise FBchatUserError('Email and password not set') + raise FBchatUserError("Email and password not set") self.email = email self.password = password @@ -501,8 +501,8 @@ class Client(object): login_successful, login_url = self._login() if not login_successful: log.warning( - 'Attempt #{} failed{}'.format( - i, {True: ', retrying'}.get(i < max_tries, '') + "Attempt #{} failed{}".format( + i, {True: ", retrying"}.get(i < max_tries, "") ) ) time.sleep(1) @@ -512,7 +512,7 @@ class Client(object): break else: raise FBchatUserError( - 'Login failed. Check email/password. (Failed on url: {})'.format( + "Login failed. Check email/password. (Failed on url: {})".format( login_url ) ) @@ -526,11 +526,11 @@ class Client(object): :rtype: bool """ - if not hasattr(self, 'fb_h'): - h_r = self._post(self.req_url.MODERN_SETTINGS_MENU, {'pmid': '4'}) + if not hasattr(self, "fb_h"): + h_r = self._post(self.req_url.MODERN_SETTINGS_MENU, {"pmid": "4"}) self.fb_h = re.search(r'name=\\"h\\" value=\\"(.*?)\\"', h_r.text).group(1) - data = {'ref': "mb", 'h': self.fb_h} + data = {"ref": "mb", "h": self.fb_h} r = self._get(self.req_url.LOGOUT, data) @@ -558,7 +558,7 @@ class Client(object): if self.default_thread_id is not None: return self.default_thread_id, self.default_thread_type else: - raise ValueError('Thread ID is not set') + raise ValueError("Thread ID is not set") else: return given_thread_id, given_thread_type @@ -588,9 +588,9 @@ class Client(object): def _forcedFetch(self, thread_id, mid): j = self.graphql_request( GraphQL( - doc_id='1768656253222505', + doc_id="1768656253222505", params={ - 'thread_and_message_id': {'thread_id': thread_id, 'message_id': mid} + "thread_and_message_id": {"thread_id": thread_id, "message_id": mid} }, ) ) @@ -686,30 +686,30 @@ class Client(object): :raises: FBchatException if request failed """ - data = {'viewer': self.uid} + data = {"viewer": self.uid} j = self._post( self.req_url.ALL_USERS, query=data, fix_request=True, as_json=True ) - if j.get('payload') is None: - raise FBchatException('Missing payload while fetching users: {}'.format(j)) + if j.get("payload") is None: + raise FBchatException("Missing payload while fetching users: {}".format(j)) users = [] - for key in j['payload']: - k = j['payload'][key] - if k['type'] in ['user', 'friend']: - if k['id'] in ['0', 0]: + for key in j["payload"]: + k = j["payload"][key] + if k["type"] in ["user", "friend"]: + if k["id"] in ["0", 0]: # Skip invalid users pass users.append( User( - k['id'], - first_name=k.get('firstName'), - url=k.get('uri'), - photo=k.get('thumbSrc'), - name=k.get('name'), - is_friend=k.get('is_friend'), - gender=GENDERS.get(k.get('gender')), + k["id"], + first_name=k.get("firstName"), + url=k.get("uri"), + photo=k.get("thumbSrc"), + name=k.get("name"), + is_friend=k.get("is_friend"), + gender=GENDERS.get(k.get("gender")), ) ) @@ -727,10 +727,10 @@ class Client(object): """ j = self.graphql_request( - GraphQL(query=GraphQL.SEARCH_USER, params={'search': name, 'limit': limit}) + GraphQL(query=GraphQL.SEARCH_USER, params={"search": name, "limit": limit}) ) - return [graphql_to_user(node) for node in j[name]['users']['nodes']] + return [graphql_to_user(node) for node in j[name]["users"]["nodes"]] def searchForPages(self, name, limit=10): """ @@ -743,10 +743,10 @@ class Client(object): """ j = self.graphql_request( - GraphQL(query=GraphQL.SEARCH_PAGE, params={'search': name, 'limit': limit}) + GraphQL(query=GraphQL.SEARCH_PAGE, params={"search": name, "limit": limit}) ) - return [graphql_to_page(node) for node in j[name]['pages']['nodes']] + return [graphql_to_page(node) for node in j[name]["pages"]["nodes"]] def searchForGroups(self, name, limit=10): """ @@ -760,10 +760,10 @@ class Client(object): """ j = self.graphql_request( - GraphQL(query=GraphQL.SEARCH_GROUP, params={'search': name, 'limit': limit}) + GraphQL(query=GraphQL.SEARCH_GROUP, params={"search": name, "limit": limit}) ) - return [graphql_to_group(node) for node in j['viewer']['groups']['nodes']] + return [graphql_to_group(node) for node in j["viewer"]["groups"]["nodes"]] def searchForThreads(self, name, limit=10): """ @@ -778,26 +778,26 @@ class Client(object): j = self.graphql_request( GraphQL( - query=GraphQL.SEARCH_THREAD, params={'search': name, 'limit': limit} + query=GraphQL.SEARCH_THREAD, params={"search": name, "limit": limit} ) ) rtn = [] - for node in j[name]['threads']['nodes']: - if node['__typename'] == 'User': + for node in j[name]["threads"]["nodes"]: + if node["__typename"] == "User": rtn.append(graphql_to_user(node)) - elif node['__typename'] == 'MessageThread': + elif node["__typename"] == "MessageThread": # MessageThread => Group thread rtn.append(graphql_to_group(node)) - elif node['__typename'] == 'Page': + elif node["__typename"] == "Page": rtn.append(graphql_to_page(node)) - elif node['__typename'] == 'Group': + elif node["__typename"] == "Group": # We don't handle Facebook "Groups" pass else: log.warning( - 'Unknown __typename: {} in {}'.format( - repr(node['__typename']), node + "Unknown __typename: {} in {}".format( + repr(node["__typename"]), node ) ) @@ -898,34 +898,34 @@ class Client(object): data = {"ids[{}]".format(i): _id for i, _id in enumerate(ids)} j = self._post(self.req_url.INFO, data, fix_request=True, as_json=True) - if j.get('payload') is None or j['payload'].get('profiles') is None: - raise FBchatException('No users/pages returned: {}'.format(j)) + if j.get("payload") is None or j["payload"].get("profiles") is None: + raise FBchatException("No users/pages returned: {}".format(j)) entries = {} - for _id in j['payload']['profiles']: - k = j['payload']['profiles'][_id] - if k['type'] in ['user', 'friend']: + for _id in j["payload"]["profiles"]: + k = j["payload"]["profiles"][_id] + if k["type"] in ["user", "friend"]: entries[_id] = { - 'id': _id, - 'type': ThreadType.USER, - 'url': k.get('uri'), - 'first_name': k.get('firstName'), - 'is_viewer_friend': k.get('is_friend'), - 'gender': k.get('gender'), - 'profile_picture': {'uri': k.get('thumbSrc')}, - 'name': k.get('name'), + "id": _id, + "type": ThreadType.USER, + "url": k.get("uri"), + "first_name": k.get("firstName"), + "is_viewer_friend": k.get("is_friend"), + "gender": k.get("gender"), + "profile_picture": {"uri": k.get("thumbSrc")}, + "name": k.get("name"), } - elif k['type'] == 'page': + elif k["type"] == "page": entries[_id] = { - 'id': _id, - 'type': ThreadType.PAGE, - 'url': k.get('uri'), - 'profile_picture': {'uri': k.get('thumbSrc')}, - 'name': k.get('name'), + "id": _id, + "type": ThreadType.PAGE, + "url": k.get("uri"), + "profile_picture": {"uri": k.get("thumbSrc")}, + "name": k.get("name"), } else: raise FBchatException( - '{} had an unknown thread type: {}'.format(_id, k) + "{} had an unknown thread type: {}".format(_id, k) ) log.debug(entries) @@ -950,7 +950,7 @@ class Client(object): if threads[k].type == ThreadType.USER: users[k] = threads[k] else: - raise FBchatUserError('Thread {} was not a user'.format(threads[k])) + raise FBchatUserError("Thread {} was not a user".format(threads[k])) return users @@ -973,7 +973,7 @@ class Client(object): if threads[k].type == ThreadType.PAGE: pages[k] = threads[k] else: - raise FBchatUserError('Thread {} was not a page'.format(threads[k])) + raise FBchatUserError("Thread {} was not a page".format(threads[k])) return pages @@ -993,7 +993,7 @@ class Client(object): if threads[k].type == ThreadType.GROUP: groups[k] = threads[k] else: - raise FBchatUserError('Thread {} was not a group'.format(threads[k])) + raise FBchatUserError("Thread {} was not a group".format(threads[k])) return groups @@ -1014,13 +1014,13 @@ class Client(object): for thread_id in thread_ids: queries.append( GraphQL( - doc_id='2147762685294928', + doc_id="2147762685294928", params={ - 'id': thread_id, - 'message_limit': 0, - 'load_messages': False, - 'load_read_receipts': False, - 'before': None, + "id": thread_id, + "message_limit": 0, + "load_messages": False, + "load_read_receipts": False, + "before": None, }, ) ) @@ -1028,17 +1028,17 @@ class Client(object): j = self.graphql_requests(*queries) for i, entry in enumerate(j): - if entry.get('message_thread') is None: + if entry.get("message_thread") is None: # If you don't have an existing thread with this person, attempt to retrieve user data anyways - j[i]['message_thread'] = { - 'thread_key': {'other_user_id': thread_ids[i]}, - 'thread_type': 'ONE_TO_ONE', + j[i]["message_thread"] = { + "thread_key": {"other_user_id": thread_ids[i]}, + "thread_type": "ONE_TO_ONE", } pages_and_user_ids = [ - k['message_thread']['thread_key']['other_user_id'] + k["message_thread"]["thread_key"]["other_user_id"] for k in j - if k['message_thread'].get('thread_type') == 'ONE_TO_ONE' + if k["message_thread"].get("thread_type") == "ONE_TO_ONE" ] pages_and_users = {} if len(pages_and_user_ids) != 0: @@ -1046,22 +1046,22 @@ class Client(object): rtn = {} for i, entry in enumerate(j): - entry = entry['message_thread'] - if entry.get('thread_type') == 'GROUP': - _id = entry['thread_key']['thread_fbid'] + entry = entry["message_thread"] + if entry.get("thread_type") == "GROUP": + _id = entry["thread_key"]["thread_fbid"] rtn[_id] = graphql_to_group(entry) - elif entry.get('thread_type') == 'ONE_TO_ONE': - _id = entry['thread_key']['other_user_id'] + elif entry.get("thread_type") == "ONE_TO_ONE": + _id = entry["thread_key"]["other_user_id"] if pages_and_users.get(_id) is None: - raise FBchatException('Could not fetch thread {}'.format(_id)) + raise FBchatException("Could not fetch thread {}".format(_id)) entry.update(pages_and_users[_id]) - if entry['type'] == ThreadType.USER: + if entry["type"] == ThreadType.USER: rtn[_id] = graphql_to_user(entry) else: rtn[_id] = graphql_to_page(entry) else: raise FBchatException( - '{} had an unknown thread type: {}'.format(thread_ids[i], entry) + "{} had an unknown thread type: {}".format(thread_ids[i], entry) ) return rtn @@ -1084,34 +1084,34 @@ class Client(object): j = self.graphql_request( GraphQL( - doc_id='1386147188135407', + doc_id="1386147188135407", params={ - 'id': thread_id, - 'message_limit': limit, - 'load_messages': True, - 'load_read_receipts': True, - 'before': before, + "id": thread_id, + "message_limit": limit, + "load_messages": True, + "load_read_receipts": True, + "before": before, }, ) ) - if j.get('message_thread') is None: - raise FBchatException('Could not fetch thread {}: {}'.format(thread_id, j)) + if j.get("message_thread") is None: + raise FBchatException("Could not fetch thread {}: {}".format(thread_id, j)) messages = list( reversed( [ graphql_to_message(message) - for message in j['message_thread']['messages']['nodes'] + for message in j["message_thread"]["messages"]["nodes"] ] ) ) - read_receipts = j['message_thread']['read_receipts']['nodes'] + read_receipts = j["message_thread"]["read_receipts"]["nodes"] for message in messages: for receipt in read_receipts: - if int(receipt['watermark']) >= int(message.timestamp): - message.read_by.append(receipt['actor']['id']) + if int(receipt["watermark"]) >= int(message.timestamp): + message.read_by.append(receipt["actor"]["id"]) return messages @@ -1133,11 +1133,11 @@ class Client(object): if offset is not None: log.warning( - 'Using `offset` in `fetchThreadList` is no longer supported, since Facebook migrated to the use of GraphQL in this request. Use `before` instead' + "Using `offset` in `fetchThreadList` is no longer supported, since Facebook migrated to the use of GraphQL in this request. Use `before` instead" ) if limit > 20 or limit < 1: - raise FBchatUserError('`limit` should be between 1 and 20') + raise FBchatUserError("`limit` should be between 1 and 20") if thread_location in ThreadLocation: loc_str = thread_location.value @@ -1146,19 +1146,19 @@ class Client(object): j = self.graphql_request( GraphQL( - doc_id='1349387578499440', + doc_id="1349387578499440", params={ - 'limit': limit, - 'tags': [loc_str], - 'before': before, - 'includeDeliveryReceipts': True, - 'includeSeqID': False, + "limit": limit, + "tags": [loc_str], + "before": before, + "includeDeliveryReceipts": True, + "includeSeqID": False, }, ) ) return [ - graphql_to_thread(node) for node in j['viewer']['message_threads']['nodes'] + graphql_to_thread(node) for node in j["viewer"]["message_threads"]["nodes"] ] def fetchUnread(self): @@ -1170,9 +1170,9 @@ class Client(object): :raises: FBchatException if request failed """ form = { - 'folders[0]': 'inbox', - 'client': 'mercury', - 'last_action_timestamp': now() - 60 * 1000 + "folders[0]": "inbox", + "client": "mercury", + "last_action_timestamp": now() - 60 * 1000 # 'last_action_timestamp': 0 } @@ -1180,7 +1180,7 @@ class Client(object): self.req_url.UNREAD_THREADS, form, fix_request=True, as_json=True ) - return j['payload']['unread_thread_fbids'][0]['other_user_fbids'] + return j["payload"]["unread_thread_fbids"][0]["other_user_fbids"] def fetchUnseen(self): """ @@ -1194,7 +1194,7 @@ class Client(object): self.req_url.UNSEEN_THREADS, None, fix_request=True, as_json=True ) - return j['payload']['unseen_thread_fbids'][0]['other_user_fbids'] + return j["payload"]["unseen_thread_fbids"][0]["other_user_fbids"] def fetchImageUrl(self, image_id): """Fetches the url to the original image from an image attachment ID @@ -1207,12 +1207,12 @@ class Client(object): """ image_id = str(image_id) j = check_request( - self._get(ReqUrl.ATTACHMENT_PHOTO, query={'photo_id': str(image_id)}) + self._get(ReqUrl.ATTACHMENT_PHOTO, query={"photo_id": str(image_id)}) ) url = get_jsmods_require(j, 3) if url is None: - raise FBchatException('Could not fetch image url from: {}'.format(j)) + raise FBchatException("Could not fetch image url from: {}".format(j)) return url def fetchMessageInfo(self, mid, thread_id=None): @@ -1261,8 +1261,8 @@ class Client(object): return plan def _getPrivateData(self): - j = self.graphql_request(GraphQL(doc_id='1868889766468115')) - return j['viewer'] + j = self.graphql_request(GraphQL(doc_id="1868889766468115")) + return j["viewer"] def getPhoneNumbers(self): """ @@ -1273,7 +1273,7 @@ class Client(object): """ data = self._getPrivateData() return [ - j['phone_number']['universal_number'] for j in data['user']['all_phones'] + j["phone_number"]["universal_number"] for j in data["user"]["all_phones"] ] def getEmails(self): @@ -1284,7 +1284,7 @@ class Client(object): :rtype: list """ data = self._getPrivateData() - return [j['display_email'] for j in data['all_emails']] + return [j["display_email"] for j in data["all_emails"]] def getUserActiveStatus(self, user_id): """ @@ -1316,45 +1316,45 @@ class Client(object): messageAndOTID = generateOfflineThreadingID() timestamp = now() data = { - 'client': self.client, - 'author': 'fbid:' + str(self.uid), - 'timestamp': timestamp, - 'source': 'source:chat:web', - 'offline_threading_id': messageAndOTID, - 'message_id': messageAndOTID, - 'threading_id': generateMessageID(self.client_id), - 'ephemeral_ttl_mode:': '0', + "client": self.client, + "author": "fbid:" + str(self.uid), + "timestamp": timestamp, + "source": "source:chat:web", + "offline_threading_id": messageAndOTID, + "message_id": messageAndOTID, + "threading_id": generateMessageID(self.client_id), + "ephemeral_ttl_mode:": "0", } # Set recipient if thread_type in [ThreadType.USER, ThreadType.PAGE]: - data['other_user_fbid'] = thread_id + data["other_user_fbid"] = thread_id elif thread_type == ThreadType.GROUP: - data['thread_fbid'] = thread_id + data["thread_fbid"] = thread_id if message is None: message = Message() if message.text or message.sticker or message.emoji_size: - data['action_type'] = 'ma-type:user-generated-message' + data["action_type"] = "ma-type:user-generated-message" if message.text: - data['body'] = message.text + data["body"] = message.text for i, mention in enumerate(message.mentions): - data['profile_xmd[{}][id]'.format(i)] = mention.thread_id - data['profile_xmd[{}][offset]'.format(i)] = mention.offset - data['profile_xmd[{}][length]'.format(i)] = mention.length - data['profile_xmd[{}][type]'.format(i)] = 'p' + data["profile_xmd[{}][id]".format(i)] = mention.thread_id + data["profile_xmd[{}][offset]".format(i)] = mention.offset + data["profile_xmd[{}][length]".format(i)] = mention.length + data["profile_xmd[{}][type]".format(i)] = "p" if message.emoji_size: if message.text: - data['tags[0]'] = 'hot_emoji_size:' + message.emoji_size.name.lower() + data["tags[0]"] = "hot_emoji_size:" + message.emoji_size.name.lower() else: - data['sticker_id'] = message.emoji_size.value + data["sticker_id"] = message.emoji_size.value if message.sticker: - data['sticker_id'] = message.sticker.uid + data["sticker_id"] = message.sticker.uid if message.quick_replies: xmd = {"quick_replies": []} @@ -1373,7 +1373,7 @@ class Client(object): xmd["quick_replies"].append(q) if len(message.quick_replies) == 1 and message.quick_replies[0].is_response: xmd["quick_replies"] = xmd["quick_replies"][0] - data['platform_xmd'] = json.dumps(xmd) + data["platform_xmd"] = json.dumps(xmd) return data @@ -1384,13 +1384,13 @@ class Client(object): # update JS token if received in response fb_dtsg = get_jsmods_require(j, 2) if fb_dtsg is not None: - self.payloadDefault['fb_dtsg'] = fb_dtsg + self.payloadDefault["fb_dtsg"] = fb_dtsg try: message_ids = [ - (action['message_id'], action['thread_fbid']) - for action in j['payload']['actions'] - if 'message_id' in action + (action["message_id"], action["thread_fbid"]) + for action in j["payload"]["actions"] + if "message_id" in action ] if len(message_ids) != 1: log.warning("Got multiple message ids' back: {}".format(message_ids)) @@ -1400,7 +1400,7 @@ class Client(object): return message_ids[0][0] except (KeyError, IndexError, TypeError) as e: raise FBchatException( - 'Error when sending message: No message IDs could be found: {}'.format( + "Error when sending message: No message IDs could be found: {}".format( j ) ) @@ -1461,13 +1461,13 @@ class Client(object): """ thread_id, thread_type = self._getThread(thread_id, thread_type) data = self._getSendData(thread_id=thread_id, thread_type=thread_type) - data['action_type'] = 'ma-type:user-generated-message' - data['lightweight_action_attachment[lwa_state]'] = ( + data["action_type"] = "ma-type:user-generated-message" + data["lightweight_action_attachment[lwa_state]"] = ( "INITIATED" if wave_first else "RECIPROCATED" ) - data['lightweight_action_attachment[lwa_type]'] = "WAVE" + data["lightweight_action_attachment[lwa_type]"] = "WAVE" if thread_type == ThreadType.USER: - data['specific_to_list[0]'] = "fbid:{}".format(thread_id) + data["specific_to_list[0]"] = "fbid:{}".format(thread_id) return self._doSendRequest(data) def quickReply(self, quick_reply, payload=None, thread_id=None, thread_type=None): @@ -1515,17 +1515,17 @@ class Client(object): :param mid: :ref:`Message ID <intro_message_ids>` of the message to unsend """ - data = {'message_id': mid} + data = {"message_id": mid} r = self._post(self.req_url.UNSEND, data) r.raise_for_status() def _sendLocation(self, location, current=True, thread_id=None, thread_type=None): thread_id, thread_type = self._getThread(thread_id, thread_type) data = self._getSendData(thread_id=thread_id, thread_type=thread_type) - data['action_type'] = 'ma-type:user-generated-message' - data['location_attachment[coordinates][latitude]'] = location.latitude - data['location_attachment[coordinates][longitude]'] = location.longitude - data['location_attachment[is_current_location]'] = current + data["action_type"] = "ma-type:user-generated-message" + data["location_attachment[coordinates][latitude]"] = location.latitude + data["location_attachment[coordinates][longitude]"] = location.longitude + data["location_attachment[is_current_location]"] = current return self._doSendRequest(data) def sendLocation(self, location, thread_id=None, thread_type=None): @@ -1575,7 +1575,7 @@ class Client(object): Returns a list of tuples with a file's ID and mimetype """ - file_dict = {'upload_{}'.format(i): f for i, f in enumerate(files)} + file_dict = {"upload_{}".format(i): f for i, f in enumerate(files)} data = {"voice_clip": voice_clip} @@ -1587,14 +1587,14 @@ class Client(object): as_json=True, ) - if len(j['payload']['metadata']) != len(files): + if len(j["payload"]["metadata"]) != len(files): raise FBchatException( "Some files could not be uploaded: {}, {}".format(j, files) ) return [ - (data[mimetype_to_key(data['filetype'])], data['filetype']) - for data in j['payload']['metadata'] + (data[mimetype_to_key(data["filetype"])], data["filetype"]) + for data in j["payload"]["metadata"] ] def _sendFiles( @@ -1612,11 +1612,11 @@ class Client(object): thread_type=thread_type, ) - data['action_type'] = 'ma-type:user-generated-message' - data['has_attachment'] = True + data["action_type"] = "ma-type:user-generated-message" + data["has_attachment"] = True for i, (file_id, mimetype) in enumerate(files): - data['{}s[{}]'.format(mimetype_to_key(mimetype), i)] = file_id + data["{}s[{}]".format(mimetype_to_key(mimetype), i)] = file_id return self._doSendRequest(data) @@ -1769,7 +1769,7 @@ class Client(object): raise FBchatUserError("Error when creating group: Not enough participants") for i, user_id in enumerate(user_ids + [self.uid]): - data['specific_to_list[{}]'.format(i)] = 'fbid:{}'.format(user_id) + data["specific_to_list[{}]".format(i)] = "fbid:{}".format(user_id) message_id, thread_id = self._doSendRequest(data, get_thread_id=True) if not thread_id: @@ -1790,19 +1790,19 @@ class Client(object): thread_id, thread_type = self._getThread(thread_id, None) data = self._getSendData(thread_id=thread_id, thread_type=ThreadType.GROUP) - data['action_type'] = 'ma-type:log-message' - data['log_message_type'] = 'log:subscribe' + data["action_type"] = "ma-type:log-message" + data["log_message_type"] = "log:subscribe" user_ids = require_list(user_ids) for i, user_id in enumerate(user_ids): if user_id == self.uid: raise FBchatUserError( - 'Error when adding users: Cannot add self to group thread' + "Error when adding users: Cannot add self to group thread" ) else: data[ - 'log_message_data[added_participants][' + str(i) + ']' + "log_message_data[added_participants][" + str(i) + "]" ] = "fbid:" + str(user_id) return self._doSendRequest(data) @@ -1830,7 +1830,7 @@ class Client(object): admin_ids = require_list(admin_ids) for i, admin_id in enumerate(admin_ids): - data['admin_ids[' + str(i) + ']'] = str(admin_id) + data["admin_ids[" + str(i) + "]"] = str(admin_id) j = self._post(self.req_url.SAVE_ADMINS, data, fix_request=True, as_json=True) @@ -1875,15 +1875,15 @@ class Client(object): j = self.graphql_request( GraphQL( - doc_id='1574519202665847', + doc_id="1574519202665847", params={ - 'data': { - 'client_mutation_id': '0', - 'actor_id': self.uid, - 'thread_fbid': thread_id, - 'user_ids': user_ids, - 'response': 'ACCEPT' if approve else 'DENY', - 'surface': 'ADMIN_MODEL_APPROVAL_CENTER', + "data": { + "client_mutation_id": "0", + "actor_id": self.uid, + "thread_fbid": thread_id, + "user_ids": user_ids, + "response": "ACCEPT" if approve else "DENY", + "surface": "ADMIN_MODEL_APPROVAL_CENTER", } }, ) @@ -1920,7 +1920,7 @@ class Client(object): thread_id, thread_type = self._getThread(thread_id, None) - data = {'thread_image_id': image_id, 'thread_id': thread_id} + data = {"thread_image_id": image_id, "thread_id": thread_id} j = self._post(self.req_url.THREAD_IMAGE, data, fix_request=True, as_json=True) return image_id @@ -1971,7 +1971,7 @@ class Client(object): title, thread_id, thread_id=thread_id, thread_type=thread_type ) - data = {'thread_name': title, 'thread_id': thread_id} + data = {"thread_name": title, "thread_id": thread_id} j = self._post(self.req_url.THREAD_NAME, data, fix_request=True, as_json=True) @@ -1991,9 +1991,9 @@ class Client(object): thread_id, thread_type = self._getThread(thread_id, thread_type) data = { - 'nickname': nickname, - 'participant_id': user_id, - 'thread_or_other_fbid': thread_id, + "nickname": nickname, + "participant_id": user_id, + "thread_or_other_fbid": thread_id, } j = self._post( @@ -2012,8 +2012,8 @@ class Client(object): thread_id, thread_type = self._getThread(thread_id, None) data = { - 'color_choice': color.value if color != ThreadColor.MESSENGER_BLUE else '', - 'thread_or_other_fbid': thread_id, + "color_choice": color.value if color != ThreadColor.MESSENGER_BLUE else "", + "thread_or_other_fbid": thread_id, } j = self._post(self.req_url.THREAD_COLOR, data, fix_request=True, as_json=True) @@ -2030,7 +2030,7 @@ class Client(object): """ thread_id, thread_type = self._getThread(thread_id, None) - data = {'emoji_choice': emoji, 'thread_or_other_fbid': thread_id} + data = {"emoji_choice": emoji, "thread_or_other_fbid": thread_id} j = self._post(self.req_url.THREAD_EMOJI, data, fix_request=True, as_json=True) @@ -2075,8 +2075,8 @@ class Client(object): "event_time": plan.time, "title": plan.title, "thread_id": thread_id, - "location_id": plan.location_id or '', - "location_name": plan.location or '', + "location_id": plan.location_id or "", + "location_name": plan.location or "", "acontext": { "action_history": [ {"surface": "messenger_chat_tab", "mechanism": "messenger_composer"} @@ -2101,8 +2101,8 @@ class Client(object): "event_reminder_id": plan.uid, "delete": "false", "date": new_plan.time, - "location_name": new_plan.location or '', - "location_id": new_plan.location_id or '', + "location_name": new_plan.location or "", + "location_id": new_plan.location_id or "", "title": new_plan.title, "acontext": { "action_history": [ @@ -2158,7 +2158,7 @@ class Client(object): self.req_url.PLAN_PARTICIPATION, full_data, fix_request=True, as_json=True ) - def eventReminder(self, thread_id, time, title, location='', location_id=''): + def eventReminder(self, thread_id, time, title, location="", location_id=""): """ Deprecated. Use :func:`fbchat.Client.createPlan` instead """ @@ -2263,10 +2263,10 @@ class Client(object): def _readStatus(self, read, thread_ids): thread_ids = require_list(thread_ids) - data = {"watermarkTimestamp": now(), "shouldSendReadReceipt": 'true'} + data = {"watermarkTimestamp": now(), "shouldSendReadReceipt": "true"} for thread_id in thread_ids: - data["ids[{}]".format(thread_id)] = 'true' if read else 'false' + data["ids[{}]".format(thread_id)] = "true" if read else "false" r = self._post(self.req_url.READ_STATUS, data) return r.ok @@ -2336,7 +2336,7 @@ class Client(object): :return: Whether the request was successful :raises: FBchatException if request failed """ - data = {'fbid': user_id} + data = {"fbid": user_id} r = self._post(self.req_url.BLOCK_USER, data) return r.ok @@ -2348,7 +2348,7 @@ class Client(object): :return: Whether the request was successful :raises: FBchatException if request failed """ - data = {'fbid': user_id} + data = {"fbid": user_id} r = self._post(self.req_url.UNBLOCK_USER, data) return r.ok @@ -2370,8 +2370,8 @@ class Client(object): data_archive = dict() data_unpin = dict() for thread_id in thread_ids: - data_archive["ids[{}]".format(thread_id)] = 'true' - data_unpin["ids[{}]".format(thread_id)] = 'false' + data_archive["ids[{}]".format(thread_id)] = "true" + data_unpin["ids[{}]".format(thread_id)] = "false" r_archive = self._post(self.req_url.ARCHIVED_STATUS, data_archive) r_unpin = self._post(self.req_url.PINNED_STATUS, data_unpin) return r_archive.ok and r_unpin.ok @@ -2494,15 +2494,15 @@ class Client(object): def _ping(self): data = { - 'channel': self.user_channel, - 'clientid': self.client_id, - 'partition': -2, - 'cap': 0, - 'uid': self.uid, - 'sticky_token': self.sticky, - 'sticky_pool': self.pool, - 'viewer_uid': self.uid, - 'state': 'active', + "channel": self.user_channel, + "clientid": self.client_id, + "partition": -2, + "cap": 0, + "uid": self.uid, + "sticky_token": self.sticky, + "sticky_pool": self.pool, + "viewer_uid": self.uid, + "state": "active", } self._get(self.req_url.PING, data, fix_request=True, as_json=False) @@ -2514,26 +2514,26 @@ class Client(object): "sticky_token": self.sticky, "sticky_pool": self.pool, "clientid": self.client_id, - 'state': 'active' if self._markAlive else 'offline', + "state": "active" if self._markAlive else "offline", } 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 def _parseMessage(self, 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 "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']: + if "batches" in content: + for batch in content["batches"]: self._parseMessage(batch) - if 'ms' not in content: + if "ms" not in content: return for m in content["ms"]: @@ -2546,11 +2546,11 @@ class Client(object): """Returns a tuple consisting of thread ID and thread type""" id_thread = None type_thread = None - if 'threadFbId' in msg_metadata['threadKey']: - id_thread = str(msg_metadata['threadKey']['threadFbId']) + if "threadFbId" in msg_metadata["threadKey"]: + id_thread = str(msg_metadata["threadKey"]["threadFbId"]) type_thread = ThreadType.GROUP - elif 'otherUserFbId' in msg_metadata['threadKey']: - id_thread = str(msg_metadata['threadKey']['otherUserFbId']) + elif "otherUserFbId" in msg_metadata["threadKey"]: + id_thread = str(msg_metadata["threadKey"]["otherUserFbId"]) type_thread = ThreadType.USER return id_thread, type_thread @@ -2561,15 +2561,15 @@ class Client(object): if metadata: mid = metadata["messageId"] - author_id = str(metadata['actorFbId']) + author_id = str(metadata["actorFbId"]) ts = int(metadata.get("timestamp")) # Added participants - if 'addedParticipants' in delta: + if "addedParticipants" in delta: added_ids = [ - str(x['userFbId']) for x in delta['addedParticipants'] + str(x["userFbId"]) for x in delta["addedParticipants"] ] - thread_id = str(metadata['threadKey']['threadFbId']) + thread_id = str(metadata["threadKey"]["threadFbId"]) self.onPeopleAdded( mid=mid, added_ids=added_ids, @@ -2580,9 +2580,9 @@ class Client(object): ) # Left/removed participants - elif 'leftParticipantFbId' in delta: - removed_id = str(delta['leftParticipantFbId']) - thread_id = str(metadata['threadKey']['threadFbId']) + elif "leftParticipantFbId" in delta: + removed_id = str(delta["leftParticipantFbId"]) + thread_id = str(metadata["threadKey"]["threadFbId"]) self.onPersonRemoved( mid=mid, removed_id=removed_id, @@ -2645,7 +2645,7 @@ class Client(object): if mid is None: self.onUnknownMesssageType(msg=m) else: - thread_id = str(delta['threadKey']['threadFbId']) + thread_id = str(delta["threadKey"]["threadFbId"]) fetch_info = self._forcedFetch(thread_id, mid) fetch_data = fetch_info["message"] author_id = fetch_data["message_sender"]["id"] @@ -2714,7 +2714,7 @@ class Client(object): # Group approval mode change elif delta_type == "change_thread_approval_mode": thread_id, thread_type = getThreadIdAndThreadType(metadata) - approval_mode = bool(int(delta['untypedData']['APPROVAL_MODE'])) + approval_mode = bool(int(delta["untypedData"]["APPROVAL_MODE"])) self.onApprovalModeChange( mid=mid, approval_mode=approval_mode, @@ -2979,13 +2979,13 @@ class Client(object): # Client payload (that weird numbers) elif delta_class == "ClientPayload": - payload = json.loads("".join(chr(z) for z in delta['payload'])) + payload = json.loads("".join(chr(z) for z in delta["payload"])) ts = m.get("ofd_ts") - for d in payload.get('deltas', []): + for d in payload.get("deltas", []): # Message reaction - if d.get('deltaMessageReaction'): - i = d['deltaMessageReaction'] + if d.get("deltaMessageReaction"): + i = d["deltaMessageReaction"] thread_id, thread_type = getThreadIdAndThreadType(i) mid = i["messageId"] author_id = str(i["userId"]) @@ -3016,8 +3016,8 @@ class Client(object): ) # Viewer status change - elif d.get('deltaChangeViewerStatus'): - i = d['deltaChangeViewerStatus'] + elif d.get("deltaChangeViewerStatus"): + i = d["deltaChangeViewerStatus"] thread_id, thread_type = getThreadIdAndThreadType(i) author_id = str(i["actorFbid"]) reason = i["reason"] @@ -3041,10 +3041,10 @@ class Client(object): ) # Live location info - elif d.get('liveLocationData'): - i = d['liveLocationData'] + elif d.get("liveLocationData"): + i = d["liveLocationData"] thread_id, thread_type = getThreadIdAndThreadType(i) - for l in i['messageLiveLocations']: + for l in i["messageLiveLocations"]: mid = l["messageId"] author_id = str(l["senderId"]) location = graphql_to_live_location(l) @@ -3059,12 +3059,12 @@ class Client(object): ) # Message deletion - elif d.get('deltaRecallMessageData'): - i = d['deltaRecallMessageData'] + elif d.get("deltaRecallMessageData"): + i = d["deltaRecallMessageData"] thread_id, thread_type = getThreadIdAndThreadType(i) - mid = i['messageID'] - ts = i['deletionTimestamp'] - author_id = str(i['senderID']) + mid = i["messageID"] + ts = i["deletionTimestamp"] + author_id = str(i["senderID"]) self.onMessageUnsent( mid=mid, author_id=author_id, @@ -3077,54 +3077,54 @@ class Client(object): # New message elif delta.get("class") == "NewMessage": mentions = [] - if delta.get('data') and delta['data'].get('prng'): + if delta.get("data") and delta["data"].get("prng"): try: mentions = [ Mention( - str(mention.get('i')), - offset=mention.get('o'), - length=mention.get('l'), + str(mention.get("i")), + offset=mention.get("o"), + length=mention.get("l"), ) - for mention in parse_json(delta['data']['prng']) + for mention in parse_json(delta["data"]["prng"]) ] except Exception: log.exception( - 'An exception occured while reading attachments' + "An exception occured while reading attachments" ) sticker = None attachments = [] unsent = False - if delta.get('attachments'): + if delta.get("attachments"): try: - for a in delta['attachments']: - mercury = a['mercury'] - if mercury.get('blob_attachment'): - image_metadata = a.get('imageMetadata', {}) - attach_type = mercury['blob_attachment'][ - '__typename' + for a in delta["attachments"]: + mercury = a["mercury"] + if mercury.get("blob_attachment"): + image_metadata = a.get("imageMetadata", {}) + attach_type = mercury["blob_attachment"][ + "__typename" ] attachment = graphql_to_attachment( - mercury['blob_attachment'] + mercury["blob_attachment"] ) if attach_type in [ - 'MessageFile', - 'MessageVideo', - 'MessageAudio', + "MessageFile", + "MessageVideo", + "MessageAudio", ]: # TODO: Add more data here for audio files - attachment.size = int(a['fileSize']) + attachment.size = int(a["fileSize"]) attachments.append(attachment) - elif mercury.get('sticker_attachment'): + elif mercury.get("sticker_attachment"): sticker = graphql_to_sticker( - mercury['sticker_attachment'] + mercury["sticker_attachment"] ) - elif mercury.get('extensible_attachment'): + elif mercury.get("extensible_attachment"): attachment = graphql_to_extensible_attachment( - mercury['extensible_attachment'] + mercury["extensible_attachment"] ) if isinstance(attachment, UnsentMessage): unsent = True @@ -3133,16 +3133,16 @@ class Client(object): except Exception: log.exception( - 'An exception occured while reading attachments: {}'.format( - delta['attachments'] + "An exception occured while reading attachments: {}".format( + delta["attachments"] ) ) - if metadata and metadata.get('tags'): - emoji_size = get_emojisize_from_tags(metadata.get('tags')) + if metadata and metadata.get("tags"): + emoji_size = get_emojisize_from_tags(metadata.get("tags")) message = Message( - text=delta.get('body'), + text=delta.get("body"), mentions=mentions, emoji_size=emoji_size, sticker=sticker, @@ -3157,7 +3157,7 @@ class Client(object): self.onMessage( mid=mid, author_id=author_id, - message=delta.get('body', ''), + message=delta.get("body", ""), message_object=message, thread_id=thread_id, thread_type=thread_type, @@ -3208,8 +3208,8 @@ class Client(object): # # self.onSeen(m.get('realtime_viewer_fbid'), m.get('reader'), m.get('time')) - elif mtype in ['jewel_requests_add']: - from_id = m['from'] + elif mtype in ["jewel_requests_add"]: + from_id = m["from"] self.onFriendRequest(from_id=from_id, msg=m) # Happens on every login @@ -3223,12 +3223,12 @@ class Client(object): # Chat timestamp elif mtype == "chatproxy-presence": buddylist = dict() - for _id in m.get('buddyList', {}): - payload = m['buddyList'][_id] + for _id in m.get("buddyList", {}): + payload = m["buddyList"][_id] - last_active = payload.get('lat') - active = payload.get('p') in [2, 3] - in_game = int(_id) in m.get('gamers', {}) + last_active = payload.get("lat") + active = payload.get("p") in [2, 3] + in_game = int(_id) in m.get("gamers", {}) buddylist[_id] = last_active @@ -3246,11 +3246,11 @@ class Client(object): # Buddylist overlay elif mtype == "buddylist_overlay": statuses = dict() - for _id in m.get('overlay', {}): - payload = m['overlay'][_id] + for _id in m.get("overlay", {}): + payload = m["overlay"][_id] - last_active = payload.get('la') - active = payload.get('a') in [2, 3] + last_active = payload.get("la") + active = payload.get("a") in [2, 3] in_game = ( self._buddylist[_id].in_game if self._buddylist.get(_id) @@ -3376,7 +3376,7 @@ class Client(object): def on2FACode(self): """Called when a 2FA code is needed to progress""" - return input('Please enter your 2FA code --> ') + return input("Please enter your 2FA code --> ") def onLoggedIn(self, email=None): """ @@ -3397,7 +3397,7 @@ class Client(object): :param exception: The exception that was encountered :return: Whether the loop should keep running """ - log.exception('Got exception while listening') + log.exception("Got exception while listening") return True def onMessage( @@ -3767,7 +3767,7 @@ class Client(object): :param msg: A full set of the data recieved """ log.info( - "{} added: {} in {}".format(author_id, ', '.join(added_ids), thread_id) + "{} added: {} in {}".format(author_id, ", ".join(added_ids), thread_id) ) def onPersonRemoved( @@ -3810,7 +3810,7 @@ class Client(object): :param recent_unread: -- :param msg: A full set of the data recieved """ - log.info('Inbox event: {}, {}, {}'.format(unseen, unread, recent_unread)) + log.info("Inbox event: {}, {}, {}".format(unseen, unread, recent_unread)) def onTyping( self, author_id=None, status=None, thread_id=None, thread_type=None, msg=None @@ -3859,7 +3859,7 @@ class Client(object): :type thread_type: models.ThreadType """ log.info( - "{} played \"{}\" in {} ({})".format( + '{} played "{}" in {} ({})'.format( author_id, game_name, thread_id, thread_type.name ) ) @@ -4316,7 +4316,7 @@ class Client(object): :param buddylist: A list of dicts with friend id and last seen timestamp :param msg: A full set of the data recieved """ - log.debug('Chat Timestamps received: {}'.format(buddylist)) + log.debug("Chat Timestamps received: {}".format(buddylist)) def onBuddylistOverlay(self, statuses=None, msg=None): """ @@ -4326,7 +4326,7 @@ class Client(object): :param msg: A full set of the data recieved :type statuses: dict """ - log.debug('Buddylist overlay received: {}'.format(statuses)) + log.debug("Buddylist overlay received: {}".format(statuses)) def onUnknownMesssageType(self, msg=None): """ @@ -4334,7 +4334,7 @@ class Client(object): :param msg: A full set of the data recieved """ - log.debug('Unknown message received: {}'.format(msg)) + log.debug("Unknown message received: {}".format(msg)) def onMessageError(self, exception=None, msg=None): """ @@ -4343,7 +4343,7 @@ class Client(object): :param exception: The exception that was encountered :param msg: A full set of the data recieved """ - log.exception('Exception in parsing of {}'.format(msg)) + log.exception("Exception in parsing of {}".format(msg)) """ END EVENTS diff --git a/fbchat/graphql.py b/fbchat/graphql.py index b736342..8a22c4f 100644 --- a/fbchat/graphql.py +++ b/fbchat/graphql.py @@ -8,7 +8,7 @@ from .utils import * # Shameless copy from https://stackoverflow.com/a/8730674 FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL -WHITESPACE = re.compile(r'[ \t\n\r]*', FLAGS) +WHITESPACE = re.compile(r"[ \t\n\r]*", FLAGS) class ConcatJSONDecoder(json.JSONDecoder): @@ -33,220 +33,220 @@ def graphql_color_to_enum(color): if not color: return ThreadColor.MESSENGER_BLUE color = color[2:] # Strip the alpha value - color_value = '#{}'.format(color.lower()) + color_value = "#{}".format(color.lower()) return enum_extend_if_invalid(ThreadColor, color_value) def get_customization_info(thread): - if thread is None or thread.get('customization_info') is None: + if thread is None or thread.get("customization_info") is None: return {} - info = thread['customization_info'] + info = thread["customization_info"] rtn = { - 'emoji': info.get('emoji'), - 'color': graphql_color_to_enum(info.get('outgoing_bubble_color')), + "emoji": info.get("emoji"), + "color": graphql_color_to_enum(info.get("outgoing_bubble_color")), } if ( - thread.get('thread_type') == 'GROUP' - or thread.get('is_group_thread') - or thread.get('thread_key', {}).get('thread_fbid') + thread.get("thread_type") == "GROUP" + or thread.get("is_group_thread") + or thread.get("thread_key", {}).get("thread_fbid") ): - rtn['nicknames'] = {} - for k in info.get('participant_customizations', []): - rtn['nicknames'][k['participant_id']] = k.get('nickname') - elif info.get('participant_customizations'): - uid = thread.get('thread_key', {}).get('other_user_id') or thread.get('id') - pc = info['participant_customizations'] + rtn["nicknames"] = {} + for k in info.get("participant_customizations", []): + rtn["nicknames"][k["participant_id"]] = k.get("nickname") + elif info.get("participant_customizations"): + uid = thread.get("thread_key", {}).get("other_user_id") or thread.get("id") + pc = info["participant_customizations"] if len(pc) > 0: - if pc[0].get('participant_id') == uid: - rtn['nickname'] = pc[0].get('nickname') + if pc[0].get("participant_id") == uid: + rtn["nickname"] = pc[0].get("nickname") else: - rtn['own_nickname'] = pc[0].get('nickname') + rtn["own_nickname"] = pc[0].get("nickname") if len(pc) > 1: - if pc[1].get('participant_id') == uid: - rtn['nickname'] = pc[1].get('nickname') + if pc[1].get("participant_id") == uid: + rtn["nickname"] = pc[1].get("nickname") else: - rtn['own_nickname'] = pc[1].get('nickname') + rtn["own_nickname"] = pc[1].get("nickname") return rtn def graphql_to_sticker(s): if not s: return None - sticker = Sticker(uid=s['id']) - if s.get('pack'): - sticker.pack = s['pack'].get('id') - if s.get('sprite_image'): + sticker = Sticker(uid=s["id"]) + if s.get("pack"): + sticker.pack = s["pack"].get("id") + if s.get("sprite_image"): sticker.is_animated = True - sticker.medium_sprite_image = s['sprite_image'].get('uri') - sticker.large_sprite_image = s['sprite_image_2x'].get('uri') - sticker.frames_per_row = s.get('frames_per_row') - sticker.frames_per_col = s.get('frames_per_column') - sticker.frame_rate = s.get('frame_rate') - sticker.url = s.get('url') - sticker.width = s.get('width') - sticker.height = s.get('height') - if s.get('label'): - sticker.label = s['label'] + sticker.medium_sprite_image = s["sprite_image"].get("uri") + sticker.large_sprite_image = s["sprite_image_2x"].get("uri") + sticker.frames_per_row = s.get("frames_per_row") + sticker.frames_per_col = s.get("frames_per_column") + sticker.frame_rate = s.get("frame_rate") + sticker.url = s.get("url") + sticker.width = s.get("width") + sticker.height = s.get("height") + if s.get("label"): + sticker.label = s["label"] return sticker def graphql_to_attachment(a): - _type = a['__typename'] - if _type in ['MessageImage', 'MessageAnimatedImage']: + _type = a["__typename"] + if _type in ["MessageImage", "MessageAnimatedImage"]: return ImageAttachment( - original_extension=a.get('original_extension') - or (a['filename'].split('-')[0] if a.get('filename') else None), - width=a.get('original_dimensions', {}).get('width'), - height=a.get('original_dimensions', {}).get('height'), - is_animated=_type == 'MessageAnimatedImage', - thumbnail_url=a.get('thumbnail', {}).get('uri'), - preview=a.get('preview') or a.get('preview_image'), - large_preview=a.get('large_preview'), - animated_preview=a.get('animated_image'), - uid=a.get('legacy_attachment_id'), + original_extension=a.get("original_extension") + or (a["filename"].split("-")[0] if a.get("filename") else None), + width=a.get("original_dimensions", {}).get("width"), + height=a.get("original_dimensions", {}).get("height"), + is_animated=_type == "MessageAnimatedImage", + thumbnail_url=a.get("thumbnail", {}).get("uri"), + preview=a.get("preview") or a.get("preview_image"), + large_preview=a.get("large_preview"), + animated_preview=a.get("animated_image"), + uid=a.get("legacy_attachment_id"), ) - elif _type == 'MessageVideo': + elif _type == "MessageVideo": return VideoAttachment( - width=a.get('original_dimensions', {}).get('width'), - height=a.get('original_dimensions', {}).get('height'), - duration=a.get('playable_duration_in_ms'), - preview_url=a.get('playable_url'), - small_image=a.get('chat_image'), - medium_image=a.get('inbox_image'), - large_image=a.get('large_image'), - uid=a.get('legacy_attachment_id'), + width=a.get("original_dimensions", {}).get("width"), + height=a.get("original_dimensions", {}).get("height"), + duration=a.get("playable_duration_in_ms"), + preview_url=a.get("playable_url"), + small_image=a.get("chat_image"), + medium_image=a.get("inbox_image"), + large_image=a.get("large_image"), + uid=a.get("legacy_attachment_id"), ) - elif _type == 'MessageAudio': + elif _type == "MessageAudio": return AudioAttachment( - filename=a.get('filename'), - url=a.get('playable_url'), - duration=a.get('playable_duration_in_ms'), - audio_type=a.get('audio_type'), + filename=a.get("filename"), + url=a.get("playable_url"), + duration=a.get("playable_duration_in_ms"), + audio_type=a.get("audio_type"), ) - elif _type == 'MessageFile': + elif _type == "MessageFile": return FileAttachment( - url=a.get('url'), - name=a.get('filename'), - is_malicious=a.get('is_malicious'), - uid=a.get('message_file_fbid'), + url=a.get("url"), + name=a.get("filename"), + is_malicious=a.get("is_malicious"), + uid=a.get("message_file_fbid"), ) else: - return Attachment(uid=a.get('legacy_attachment_id')) + return Attachment(uid=a.get("legacy_attachment_id")) def graphql_to_extensible_attachment(a): - story = a.get('story_attachment') + story = a.get("story_attachment") if story: - target = story.get('target') + target = story.get("target") if target: - _type = target['__typename'] - if _type == 'MessageLocation': + _type = target["__typename"] + if _type == "MessageLocation": latitude, longitude = get_url_parameter( - get_url_parameter(story['url'], 'u'), 'where1' + get_url_parameter(story["url"], "u"), "where1" ).split(", ") rtn = LocationAttachment( - uid=int(story['deduplication_key']), + uid=int(story["deduplication_key"]), latitude=float(latitude), longitude=float(longitude), ) - if story['media']: - rtn.image_url = story['media']['image']['uri'] - rtn.image_width = story['media']['image']['width'] - rtn.image_height = story['media']['image']['height'] - rtn.url = story['url'] + if story["media"]: + rtn.image_url = story["media"]["image"]["uri"] + rtn.image_width = story["media"]["image"]["width"] + rtn.image_height = story["media"]["image"]["height"] + rtn.url = story["url"] return rtn - elif _type == 'MessageLiveLocation': + elif _type == "MessageLiveLocation": rtn = LiveLocationAttachment( - uid=int(story['target']['live_location_id']), - latitude=story['target']['coordinate']['latitude'] - if story['target'].get('coordinate') + uid=int(story["target"]["live_location_id"]), + latitude=story["target"]["coordinate"]["latitude"] + if story["target"].get("coordinate") else None, - longitude=story['target']['coordinate']['longitude'] - if story['target'].get('coordinate') + longitude=story["target"]["coordinate"]["longitude"] + if story["target"].get("coordinate") else None, - name=story['title_with_entities']['text'], - expiration_time=story['target']['expiration_time'] - if story['target'].get('expiration_time') + name=story["title_with_entities"]["text"], + expiration_time=story["target"]["expiration_time"] + if story["target"].get("expiration_time") else None, - is_expired=story['target']['is_expired'], + is_expired=story["target"]["is_expired"], ) - if story['media']: - rtn.image_url = story['media']['image']['uri'] - rtn.image_width = story['media']['image']['width'] - rtn.image_height = story['media']['image']['height'] - rtn.url = story['url'] + if story["media"]: + rtn.image_url = story["media"]["image"]["uri"] + rtn.image_width = story["media"]["image"]["width"] + rtn.image_height = story["media"]["image"]["height"] + rtn.url = story["url"] return rtn - elif _type in ['ExternalUrl', 'Story']: + elif _type in ["ExternalUrl", "Story"]: return ShareAttachment( - uid=a.get('legacy_attachment_id'), - author=story['target']['actors'][0]['id'] - if story['target'].get('actors') + uid=a.get("legacy_attachment_id"), + author=story["target"]["actors"][0]["id"] + if story["target"].get("actors") else None, - url=story['url'], - original_url=get_url_parameter(story['url'], 'u') - if "/l.php?u=" in story['url'] - else story['url'], - title=story['title_with_entities'].get('text'), - description=story['description'].get('text') - if story.get('description') + url=story["url"], + original_url=get_url_parameter(story["url"], "u") + if "/l.php?u=" in story["url"] + else story["url"], + title=story["title_with_entities"].get("text"), + description=story["description"].get("text") + if story.get("description") else None, - source=story['source']['text'], - image_url=story['media']['image']['uri'] - if story.get('media') + source=story["source"]["text"], + image_url=story["media"]["image"]["uri"] + if story.get("media") else None, original_image_url=( - get_url_parameter(story['media']['image']['uri'], 'url') - if "/safe_image.php" in story['media']['image']['uri'] - else story['media']['image']['uri'] + get_url_parameter(story["media"]["image"]["uri"], "url") + if "/safe_image.php" in story["media"]["image"]["uri"] + else story["media"]["image"]["uri"] ) - if story.get('media') + if story.get("media") else None, - image_width=story['media']['image']['width'] - if story.get('media') + image_width=story["media"]["image"]["width"] + if story.get("media") else None, - image_height=story['media']['image']['height'] - if story.get('media') + image_height=story["media"]["image"]["height"] + if story.get("media") else None, attachments=[ graphql_to_subattachment(attachment) - for attachment in story.get('subattachments') + for attachment in story.get("subattachments") ], ) else: - return UnsentMessage(uid=a.get('legacy_attachment_id')) + return UnsentMessage(uid=a.get("legacy_attachment_id")) def graphql_to_subattachment(a): - _type = a['target']['__typename'] - if _type == 'Video': + _type = a["target"]["__typename"] + if _type == "Video": return VideoAttachment( - duration=a['media'].get('playable_duration_in_ms'), - preview_url=a['media'].get('playable_url'), - medium_image=a['media'].get('image'), - uid=a['target'].get('video_id'), + duration=a["media"].get("playable_duration_in_ms"), + preview_url=a["media"].get("playable_url"), + medium_image=a["media"].get("image"), + uid=a["target"].get("video_id"), ) def graphql_to_live_location(a): return LiveLocationAttachment( - uid=a['id'], - latitude=a['coordinate']['latitude'] / (10 ** 8) - if not a.get('stopReason') + uid=a["id"], + latitude=a["coordinate"]["latitude"] / (10 ** 8) + if not a.get("stopReason") else None, - longitude=a['coordinate']['longitude'] / (10 ** 8) - if not a.get('stopReason') + longitude=a["coordinate"]["longitude"] / (10 ** 8) + if not a.get("stopReason") else None, - name=a.get('locationTitle'), - expiration_time=a['expirationTime'], - is_expired=bool(a.get('stopReason')), + name=a.get("locationTitle"), + expiration_time=a["expirationTime"], + is_expired=bool(a.get("stopReason")), ) def graphql_to_poll(a): rtn = Poll( - title=a.get('title') if a.get('title') else a.get('text'), - options=[graphql_to_poll_option(m) for m in a.get('options')], + title=a.get("title") if a.get("title") else a.get("text"), + options=[graphql_to_poll_option(m) for m in a.get("options")], ) rtn.uid = int(a["id"]) rtn.options_count = a.get("total_count") @@ -254,90 +254,90 @@ def graphql_to_poll(a): def graphql_to_poll_option(a): - if a.get('viewer_has_voted') is None: + if a.get("viewer_has_voted") is None: vote = None - elif isinstance(a['viewer_has_voted'], bool): - vote = a['viewer_has_voted'] + 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=vote) + vote = a["viewer_has_voted"] == "true" + rtn = PollOption(text=a.get("text"), 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') + [m.get("node").get("id") for m in a.get("voters").get("edges")] + if isinstance(a.get("voters"), dict) + else a.get("voters") ) rtn.votes_count = ( - a.get('voters').get('count') - if isinstance(a.get('voters'), dict) - else a.get('total_count') + a.get("voters").get("count") + if isinstance(a.get("voters"), dict) + else a.get("total_count") ) return rtn def graphql_to_plan(a): - if a.get('event_members'): + if a.get("event_members"): rtn = Plan( - time=a.get('event_time'), - title=a.get('title'), - location=a.get('location_name'), + time=a.get("event_time"), + title=a.get("title"), + location=a.get("location_name"), ) - if a.get('location_id') != 0: - rtn.location_id = str(a.get('location_id')) - rtn.uid = a.get('oid') - rtn.author_id = a.get('creator_id') + if a.get("location_id") != 0: + rtn.location_id = str(a.get("location_id")) + rtn.uid = a.get("oid") + rtn.author_id = a.get("creator_id") guests = a.get("event_members") rtn.going = [uid for uid in guests if guests[uid] == "GOING"] rtn.declined = [uid for uid in guests if guests[uid] == "DECLINED"] rtn.invited = [uid for uid in guests if guests[uid] == "INVITED"] return rtn - elif a.get('id') is None: + elif a.get("id") is None: rtn = Plan( - time=a.get('event_time'), - title=a.get('event_title'), - location=a.get('event_location_name'), - location_id=a.get('event_location_id'), + time=a.get("event_time"), + title=a.get("event_title"), + location=a.get("event_location_name"), + location_id=a.get("event_location_id"), ) - rtn.uid = a.get('event_id') - rtn.author_id = a.get('event_creator_id') - guests = json.loads(a.get('guest_state_list')) + rtn.uid = a.get("event_id") + rtn.author_id = a.get("event_creator_id") + guests = json.loads(a.get("guest_state_list")) else: rtn = Plan( - time=a.get('time'), - title=a.get('event_title'), - location=a.get('location_name'), + time=a.get("time"), + title=a.get("event_title"), + location=a.get("location_name"), ) - rtn.uid = a.get('id') - rtn.author_id = a.get('lightweight_event_creator').get('id') - guests = a.get('event_reminder_members').get('edges') + rtn.uid = a.get("id") + rtn.author_id = a.get("lightweight_event_creator").get("id") + guests = a.get("event_reminder_members").get("edges") rtn.going = [ - m.get('node').get('id') for m in guests if m.get('guest_list_state') == "GOING" + m.get("node").get("id") for m in guests if m.get("guest_list_state") == "GOING" ] rtn.declined = [ - m.get('node').get('id') + m.get("node").get("id") for m in guests - if m.get('guest_list_state') == "DECLINED" + if m.get("guest_list_state") == "DECLINED" ] rtn.invited = [ - m.get('node').get('id') + m.get("node").get("id") for m in guests - if m.get('guest_list_state') == "INVITED" + if m.get("guest_list_state") == "INVITED" ] return rtn def graphql_to_quick_reply(q, is_response=False): data = dict() - _type = q.get('content_type').lower() - if q.get('payload'): + _type = q.get("content_type").lower() + if q.get("payload"): data["payload"] = q["payload"] - if q.get('data'): + if q.get("data"): data["data"] = q["data"] - if q.get('image_url') and _type is not QuickReplyLocation._type: + if q.get("image_url") and _type is not QuickReplyLocation._type: data["image_url"] = q["image_url"] data["is_response"] = is_response if _type == QuickReplyText._type: - if q.get('title') is not None: + if q.get("title") is not None: data["title"] = q["title"] rtn = QuickReplyText(**data) elif _type == QuickReplyLocation._type: @@ -350,48 +350,48 @@ def graphql_to_quick_reply(q, is_response=False): def graphql_to_message(message): - if message.get('message_sender') is None: - message['message_sender'] = {} - if message.get('message') is None: - message['message'] = {} + if message.get("message_sender") is None: + message["message_sender"] = {} + if message.get("message") is None: + message["message"] = {} rtn = Message( - text=message.get('message').get('text'), + text=message.get("message").get("text"), mentions=[ Mention( - m.get('entity', {}).get('id'), - offset=m.get('offset'), - length=m.get('length'), + m.get("entity", {}).get("id"), + offset=m.get("offset"), + length=m.get("length"), ) - for m in message.get('message').get('ranges', []) + for m in message.get("message").get("ranges", []) ], - emoji_size=get_emojisize_from_tags(message.get('tags_list')), - sticker=graphql_to_sticker(message.get('sticker')), + emoji_size=get_emojisize_from_tags(message.get("tags_list")), + sticker=graphql_to_sticker(message.get("sticker")), ) - rtn.uid = str(message.get('message_id')) - rtn.author = str(message.get('message_sender').get('id')) - rtn.timestamp = message.get('timestamp_precise') + rtn.uid = str(message.get("message_id")) + rtn.author = str(message.get("message_sender").get("id")) + rtn.timestamp = message.get("timestamp_precise") rtn.unsent = False - if message.get('unread') is not None: - rtn.is_read = not message['unread'] + if message.get("unread") is not None: + rtn.is_read = not message["unread"] rtn.reactions = { - str(r['user']['id']): enum_extend_if_invalid(MessageReaction, r['reaction']) - for r in message.get('message_reactions') + str(r["user"]["id"]): enum_extend_if_invalid(MessageReaction, r["reaction"]) + for r in message.get("message_reactions") } - if message.get('blob_attachments') is not None: + if message.get("blob_attachments") is not None: rtn.attachments = [ graphql_to_attachment(attachment) - for attachment in message['blob_attachments'] + for attachment in message["blob_attachments"] ] - if message.get('platform_xmd_encoded'): - quick_replies = json.loads(message['platform_xmd_encoded']).get('quick_replies') + if message.get("platform_xmd_encoded"): + quick_replies = json.loads(message["platform_xmd_encoded"]).get("quick_replies") if isinstance(quick_replies, list): rtn.quick_replies = [graphql_to_quick_reply(q) for q in quick_replies] elif isinstance(quick_replies, dict): rtn.quick_replies = [ graphql_to_quick_reply(quick_replies, is_response=True) ] - if message.get('extensible_attachment') is not None: - attachment = graphql_to_extensible_attachment(message['extensible_attachment']) + if message.get("extensible_attachment") is not None: + attachment = graphql_to_extensible_attachment(message["extensible_attachment"]) if isinstance(attachment, UnsentMessage): rtn.unsent = True elif attachment: @@ -400,157 +400,157 @@ def graphql_to_message(message): def graphql_to_user(user): - if user.get('profile_picture') is None: - user['profile_picture'] = {} + if user.get("profile_picture") is None: + user["profile_picture"] = {} c_info = get_customization_info(user) plan = None - if user.get('event_reminders'): + if user.get("event_reminders"): plan = ( - graphql_to_plan(user['event_reminders']['nodes'][0]) - if user['event_reminders'].get('nodes') + graphql_to_plan(user["event_reminders"]["nodes"][0]) + if user["event_reminders"].get("nodes") else None ) return User( - user['id'], - url=user.get('url'), - first_name=user.get('first_name'), - last_name=user.get('last_name'), - is_friend=user.get('is_viewer_friend'), - gender=GENDERS.get(user.get('gender')), - affinity=user.get('affinity'), - nickname=c_info.get('nickname'), - color=c_info.get('color'), - emoji=c_info.get('emoji'), - own_nickname=c_info.get('own_nickname'), - photo=user['profile_picture'].get('uri'), - name=user.get('name'), - message_count=user.get('messages_count'), + user["id"], + url=user.get("url"), + first_name=user.get("first_name"), + last_name=user.get("last_name"), + is_friend=user.get("is_viewer_friend"), + gender=GENDERS.get(user.get("gender")), + affinity=user.get("affinity"), + nickname=c_info.get("nickname"), + color=c_info.get("color"), + emoji=c_info.get("emoji"), + own_nickname=c_info.get("own_nickname"), + photo=user["profile_picture"].get("uri"), + name=user.get("name"), + message_count=user.get("messages_count"), plan=plan, ) def graphql_to_thread(thread): - if thread['thread_type'] == 'GROUP': + if thread["thread_type"] == "GROUP": return graphql_to_group(thread) - elif thread['thread_type'] == 'ONE_TO_ONE': - if thread.get('big_image_src') is None: - thread['big_image_src'] = {} + elif thread["thread_type"] == "ONE_TO_ONE": + if thread.get("big_image_src") is None: + thread["big_image_src"] = {} c_info = get_customization_info(thread) participants = [ - node['messaging_actor'] for node in thread['all_participants']['nodes'] + node["messaging_actor"] for node in thread["all_participants"]["nodes"] ] user = next( - p for p in participants if p['id'] == thread['thread_key']['other_user_id'] + p for p in participants if p["id"] == thread["thread_key"]["other_user_id"] ) last_message_timestamp = None - if 'last_message' in thread: - last_message_timestamp = thread['last_message']['nodes'][0][ - 'timestamp_precise' + if "last_message" in thread: + last_message_timestamp = thread["last_message"]["nodes"][0][ + "timestamp_precise" ] - first_name = user.get('short_name') + first_name = user.get("short_name") if first_name is None: last_name = None else: - last_name = user.get('name').split(first_name, 1).pop().strip() + last_name = user.get("name").split(first_name, 1).pop().strip() plan = None - if thread.get('event_reminders'): + if thread.get("event_reminders"): plan = ( - graphql_to_plan(thread['event_reminders']['nodes'][0]) - if thread['event_reminders'].get('nodes') + graphql_to_plan(thread["event_reminders"]["nodes"][0]) + if thread["event_reminders"].get("nodes") else None ) return User( - user['id'], - url=user.get('url'), - name=user.get('name'), + user["id"], + url=user.get("url"), + name=user.get("name"), first_name=first_name, last_name=last_name, - is_friend=user.get('is_viewer_friend'), - gender=GENDERS.get(user.get('gender')), - affinity=user.get('affinity'), - nickname=c_info.get('nickname'), - color=c_info.get('color'), - emoji=c_info.get('emoji'), - own_nickname=c_info.get('own_nickname'), - photo=user['big_image_src'].get('uri'), - message_count=thread.get('messages_count'), + is_friend=user.get("is_viewer_friend"), + gender=GENDERS.get(user.get("gender")), + affinity=user.get("affinity"), + nickname=c_info.get("nickname"), + color=c_info.get("color"), + emoji=c_info.get("emoji"), + own_nickname=c_info.get("own_nickname"), + photo=user["big_image_src"].get("uri"), + message_count=thread.get("messages_count"), last_message_timestamp=last_message_timestamp, plan=plan, ) else: raise FBchatException( - 'Unknown thread type: {}, with data: {}'.format( - thread.get('thread_type'), thread + "Unknown thread type: {}, with data: {}".format( + thread.get("thread_type"), thread ) ) def graphql_to_group(group): - if group.get('image') is None: - group['image'] = {} + if group.get("image") is None: + group["image"] = {} c_info = get_customization_info(group) last_message_timestamp = None - if 'last_message' in group: - last_message_timestamp = group['last_message']['nodes'][0]['timestamp_precise'] + if "last_message" in group: + last_message_timestamp = group["last_message"]["nodes"][0]["timestamp_precise"] plan = None - if group.get('event_reminders'): + if group.get("event_reminders"): plan = ( - graphql_to_plan(group['event_reminders']['nodes'][0]) - if group['event_reminders'].get('nodes') + graphql_to_plan(group["event_reminders"]["nodes"][0]) + if group["event_reminders"].get("nodes") else None ) return Group( - group['thread_key']['thread_fbid'], + group["thread_key"]["thread_fbid"], participants=set( [ - node['messaging_actor']['id'] - for node in group['all_participants']['nodes'] + node["messaging_actor"]["id"] + for node in group["all_participants"]["nodes"] ] ), - nicknames=c_info.get('nicknames'), - color=c_info.get('color'), - emoji=c_info.get('emoji'), - admins=set([node.get('id') for node in group.get('thread_admins')]), - approval_mode=bool(group.get('approval_mode')) - if group.get('approval_mode') is not None + nicknames=c_info.get("nicknames"), + color=c_info.get("color"), + emoji=c_info.get("emoji"), + admins=set([node.get("id") for node in group.get("thread_admins")]), + approval_mode=bool(group.get("approval_mode")) + if group.get("approval_mode") is not None else None, approval_requests=set( - node["requester"]['id'] for node in group['group_approval_queue']['nodes'] + node["requester"]["id"] for node in group["group_approval_queue"]["nodes"] ) - if group.get('group_approval_queue') + if group.get("group_approval_queue") else None, - join_link=group['joinable_mode'].get('link'), - photo=group['image'].get('uri'), - name=group.get('name'), - message_count=group.get('messages_count'), + join_link=group["joinable_mode"].get("link"), + photo=group["image"].get("uri"), + name=group.get("name"), + message_count=group.get("messages_count"), last_message_timestamp=last_message_timestamp, plan=plan, ) def graphql_to_page(page): - if page.get('profile_picture') is None: - page['profile_picture'] = {} - if page.get('city') is None: - page['city'] = {} + if page.get("profile_picture") is None: + page["profile_picture"] = {} + if page.get("city") is None: + page["city"] = {} plan = None - if page.get('event_reminders'): + if page.get("event_reminders"): plan = ( - graphql_to_plan(page['event_reminders']['nodes'][0]) - if page['event_reminders'].get('nodes') + graphql_to_plan(page["event_reminders"]["nodes"][0]) + if page["event_reminders"].get("nodes") else None ) return Page( - page['id'], - url=page.get('url'), - city=page.get('city').get('name'), - category=page.get('category_type'), - photo=page['profile_picture'].get('uri'), - name=page.get('name'), - message_count=page.get('messages_count'), + page["id"], + url=page.get("url"), + city=page.get("city").get("name"), + category=page.get("category_type"), + photo=page["profile_picture"].get("uri"), + name=page.get("name"), + message_count=page.get("messages_count"), plan=plan, ) @@ -561,7 +561,7 @@ def graphql_queries_to_json(*queries): """ rtn = {} for i, query in enumerate(queries): - rtn['q{}'.format(i)] = query.value + rtn["q{}".format(i)] = query.value return json.dumps(rtn) @@ -570,20 +570,20 @@ def graphql_response_to_json(content): try: j = json.loads(content, cls=ConcatJSONDecoder) except Exception: - raise FBchatException('Error while parsing JSON: {}'.format(repr(content))) + raise FBchatException("Error while parsing JSON: {}".format(repr(content))) rtn = [None] * (len(j)) for x in j: - if 'error_results' in x: + if "error_results" in x: del rtn[-1] continue check_json(x) [(key, value)] = x.items() check_json(value) - if 'response' in value: - rtn[int(key[1:])] = value['response'] + if "response" in value: + rtn[int(key[1:])] = value["response"] else: - rtn[int(key[1:])] = value['data'] + rtn[int(key[1:])] = value["data"] log.debug(rtn) @@ -595,11 +595,11 @@ class GraphQL(object): if params is None: params = {} if query is not None: - self.value = {'priority': 0, 'q': query, 'query_params': params} + self.value = {"priority": 0, "q": query, "query_params": params} elif doc_id is not None: - self.value = {'doc_id': doc_id, 'query_params': params} + self.value = {"doc_id": doc_id, "query_params": params} else: - raise FBchatUserError('A query or doc_id must be specified') + raise FBchatUserError("A query or doc_id must be specified") FRAGMENT_USER = """ QueryFragment User: User { diff --git a/fbchat/models.py b/fbchat/models.py index 8e74a40..6089104 100644 --- a/fbchat/models.py +++ b/fbchat/models.py @@ -74,7 +74,7 @@ class Thread(object): return self.__unicode__() def __unicode__(self): - return '<{} {} ({})>'.format(self.type.name, self.name, self.uid) + return "<{} {} ({})>".format(self.type.name, self.name, self.uid) class User(Thread): @@ -282,7 +282,7 @@ class Message(object): return self.__unicode__() def __unicode__(self): - return '<Message ({}): {}, mentions={} emoji_size={} attachments={}>'.format( + return "<Message ({}): {}, mentions={} emoji_size={} attachments={}>".format( self.uid, repr(self.text), self.mentions, self.emoji_size, self.attachments ) @@ -305,7 +305,7 @@ class Message(object): offset = 0 f = Formatter() field_names = [field_name[1] for field_name in f.parse(text)] - automatic = '' in field_names + automatic = "" in field_names i = 0 for (literal_text, field_name, format_spec, conversion) in f.parse(text): @@ -315,7 +315,7 @@ class Message(object): if field_name is None: continue - if field_name == '': + if field_name == "": field_name = str(i) i += 1 elif automatic and field_name.isdigit(): @@ -584,21 +584,21 @@ class ImageAttachment(Attachment): if preview is None: preview = {} - self.preview_url = preview.get('uri') - self.preview_width = preview.get('width') - self.preview_height = preview.get('height') + self.preview_url = preview.get("uri") + self.preview_width = preview.get("width") + self.preview_height = preview.get("height") if large_preview is None: large_preview = {} - self.large_preview_url = large_preview.get('uri') - self.large_preview_width = large_preview.get('width') - self.large_preview_height = large_preview.get('height') + self.large_preview_url = large_preview.get("uri") + self.large_preview_width = large_preview.get("width") + self.large_preview_height = large_preview.get("height") if animated_preview is None: animated_preview = {} - self.animated_preview_url = animated_preview.get('uri') - self.animated_preview_width = animated_preview.get('width') - self.animated_preview_height = animated_preview.get('height') + self.animated_preview_url = animated_preview.get("uri") + self.animated_preview_width = animated_preview.get("width") + self.animated_preview_height = animated_preview.get("height") class VideoAttachment(Attachment): @@ -656,21 +656,21 @@ class VideoAttachment(Attachment): if small_image is None: small_image = {} - self.small_image_url = small_image.get('uri') - self.small_image_width = small_image.get('width') - self.small_image_height = small_image.get('height') + self.small_image_url = small_image.get("uri") + self.small_image_width = small_image.get("width") + self.small_image_height = small_image.get("height") if medium_image is None: medium_image = {} - self.medium_image_url = medium_image.get('uri') - self.medium_image_width = medium_image.get('width') - self.medium_image_height = medium_image.get('height') + self.medium_image_url = medium_image.get("uri") + self.medium_image_width = medium_image.get("width") + self.medium_image_height = medium_image.get("height") if large_image is None: large_image = {} - self.large_image_url = large_image.get('uri') - self.large_image_width = large_image.get('width') - self.large_image_height = large_image.get('height') + self.large_image_url = large_image.get("uri") + self.large_image_width = large_image.get("width") + self.large_image_height = large_image.get("height") class Mention(object): @@ -691,7 +691,7 @@ class Mention(object): return self.__unicode__() def __unicode__(self): - return '<Mention {}: offset={} length={}>'.format( + return "<Mention {}: offset={} length={}>".format( self.thread_id, self.offset, self.length ) @@ -716,7 +716,7 @@ class QuickReply(object): return self.__unicode__() def __unicode__(self): - return '<{}: payload={!r}>'.format(self.__class__.__name__, self.payload) + return "<{}: payload={!r}>".format(self.__class__.__name__, self.payload) class QuickReplyText(QuickReply): @@ -787,7 +787,7 @@ class Poll(object): return self.__unicode__() def __unicode__(self): - return '<Poll ({}): {} options={}>'.format( + return "<Poll ({}): {} options={}>".format( self.uid, repr(self.title), self.options ) @@ -813,7 +813,7 @@ class PollOption(object): return self.__unicode__() def __unicode__(self): - return '<PollOption ({}): {} voters={}>'.format( + return "<PollOption ({}): {} voters={}>".format( self.uid, repr(self.text), self.voters ) @@ -842,8 +842,8 @@ class Plan(object): """Represents a plan""" self.time = int(time) self.title = title - self.location = location or '' - self.location_id = location_id or '' + self.location = location or "" + self.location_id = location_id or "" self.author_id = None self.going = [] self.declined = [] @@ -853,7 +853,7 @@ class Plan(object): return self.__unicode__() def __unicode__(self): - return '<Plan ({}): {} time={}, location={}, location_id={}>'.format( + return "<Plan ({}): {} time={}, location={}, location_id={}>".format( self.uid, repr(self.title), self.time, @@ -879,7 +879,7 @@ class ActiveStatus(object): return self.__unicode__() def __unicode__(self): - return '<ActiveStatus: active={} last_active={} in_game={}>'.format( + return "<ActiveStatus: active={} last_active={} in_game={}>".format( self.active, self.last_active, self.in_game ) @@ -889,7 +889,7 @@ class Enum(aenum.Enum): def __repr__(self): # For documentation: - return '{}.{}'.format(type(self).__name__, self.name) + return "{}.{}".format(type(self).__name__, self.name) class ThreadType(Enum): @@ -904,10 +904,10 @@ class ThreadType(Enum): class ThreadLocation(Enum): """Used to specify where a thread is located (inbox, pending, archived, other).""" - INBOX = 'INBOX' - PENDING = 'PENDING' - ARCHIVED = 'ARCHIVED' - OTHER = 'OTHER' + INBOX = "INBOX" + PENDING = "PENDING" + ARCHIVED = "ARCHIVED" + OTHER = "OTHER" class TypingStatus(Enum): @@ -920,38 +920,38 @@ class TypingStatus(Enum): class EmojiSize(Enum): """Used to specify the size of a sent emoji""" - LARGE = '369239383222810' - MEDIUM = '369239343222814' - SMALL = '369239263222822' + LARGE = "369239383222810" + MEDIUM = "369239343222814" + SMALL = "369239263222822" class ThreadColor(Enum): """Used to specify a thread colors""" - MESSENGER_BLUE = '#0084ff' - VIKING = '#44bec7' - GOLDEN_POPPY = '#ffc300' - RADICAL_RED = '#fa3c4c' - SHOCKING = '#d696bb' - PICTON_BLUE = '#6699cc' - FREE_SPEECH_GREEN = '#13cf13' - PUMPKIN = '#ff7e29' - LIGHT_CORAL = '#e68585' - MEDIUM_SLATE_BLUE = '#7646ff' - DEEP_SKY_BLUE = '#20cef5' - FERN = '#67b868' - CAMEO = '#d4a88c' - BRILLIANT_ROSE = '#ff5ca1' - BILOBA_FLOWER = '#a695c7' + MESSENGER_BLUE = "#0084ff" + VIKING = "#44bec7" + GOLDEN_POPPY = "#ffc300" + RADICAL_RED = "#fa3c4c" + SHOCKING = "#d696bb" + PICTON_BLUE = "#6699cc" + FREE_SPEECH_GREEN = "#13cf13" + PUMPKIN = "#ff7e29" + LIGHT_CORAL = "#e68585" + MEDIUM_SLATE_BLUE = "#7646ff" + DEEP_SKY_BLUE = "#20cef5" + FERN = "#67b868" + CAMEO = "#d4a88c" + BRILLIANT_ROSE = "#ff5ca1" + BILOBA_FLOWER = "#a695c7" class MessageReaction(Enum): """Used to specify a message reaction""" - LOVE = '😍' - SMILE = '😆' - WOW = '😮' - SAD = '😢' - ANGRY = '😠' - YES = '👍' - NO = '👎' + LOVE = "😍" + SMILE = "😆" + WOW = "😮" + SAD = "😢" + ANGRY = "😠" + YES = "👍" + NO = "👎" diff --git a/fbchat/utils.py b/fbchat/utils.py index 3433c56..8705442 100644 --- a/fbchat/utils.py +++ b/fbchat/utils.py @@ -48,37 +48,37 @@ USER_AGENTS = [ ] LIKES = { - 'large': EmojiSize.LARGE, - 'medium': EmojiSize.MEDIUM, - 'small': EmojiSize.SMALL, - 'l': EmojiSize.LARGE, - 'm': EmojiSize.MEDIUM, - 's': EmojiSize.SMALL, + "large": EmojiSize.LARGE, + "medium": EmojiSize.MEDIUM, + "small": EmojiSize.SMALL, + "l": EmojiSize.LARGE, + "m": EmojiSize.MEDIUM, + "s": EmojiSize.SMALL, } GENDERS = { # For standard requests - 0: 'unknown', - 1: 'female_singular', - 2: 'male_singular', - 3: 'female_singular_guess', - 4: 'male_singular_guess', - 5: 'mixed', - 6: 'neuter_singular', - 7: 'unknown_singular', - 8: 'female_plural', - 9: 'male_plural', - 10: 'neuter_plural', - 11: 'unknown_plural', + 0: "unknown", + 1: "female_singular", + 2: "male_singular", + 3: "female_singular_guess", + 4: "male_singular_guess", + 5: "mixed", + 6: "neuter_singular", + 7: "unknown_singular", + 8: "female_plural", + 9: "male_plural", + 10: "neuter_plural", + 11: "unknown_plural", # For graphql requests - 'UNKNOWN': 'unknown', - 'FEMALE': 'female_singular', - 'MALE': 'male_singular', + "UNKNOWN": "unknown", + "FEMALE": "female_singular", + "MALE": "male_singular", # '': 'female_singular_guess', # '': 'male_singular_guess', # '': 'mixed', - 'NEUTER': 'neuter_singular', + "NEUTER": "neuter_singular", # '': 'unknown_singular', # '': 'female_plural', # '': 'male_plural', @@ -168,7 +168,7 @@ class ReqUrl(object): ) -facebookEncoding = 'UTF-8' +facebookEncoding = "UTF-8" def now(): @@ -177,9 +177,9 @@ def now(): def strip_to_json(text): try: - return text[text.index('{') :] + return text[text.index("{") :] except ValueError: - raise FBchatException('No JSON object found: {!r}'.format(text)) + raise FBchatException("No JSON object found: {!r}".format(text)) def get_decoded_r(r): @@ -201,12 +201,12 @@ def get_json(r): def digitToChar(digit): if digit < 10: return str(digit) - return chr(ord('a') + digit - 10) + return chr(ord("a") + digit - 10) def str_base(number, base): if number < 0: - return '-' + str_base(-number, base) + return "-" + str_base(-number, base) (d, m) = divmod(number, base) if d > 0: return str_base(d, base) + digitToChar(m) @@ -226,55 +226,55 @@ def getSignatureID(): def generateOfflineThreadingID(): ret = now() value = int(random() * 4294967295) - string = ("0000000000000000000000" + format(value, 'b'))[-22:] - msgs = format(ret, 'b') + string + string = ("0000000000000000000000" + format(value, "b"))[-22:] + msgs = format(ret, "b") + string return str(int(msgs, 2)) def check_json(j): - if j.get('error') is None: + if j.get("error") is None: return - if 'errorDescription' in j: + if "errorDescription" in j: # 'errorDescription' is in the users own language! raise FBchatFacebookError( - 'Error #{} when sending request: {}'.format( - j['error'], j['errorDescription'] + "Error #{} when sending request: {}".format( + j["error"], j["errorDescription"] ), - fb_error_code=j['error'], - fb_error_message=j['errorDescription'], + fb_error_code=j["error"], + fb_error_message=j["errorDescription"], ) - elif 'debug_info' in j['error'] and 'code' in j['error']: + elif "debug_info" in j["error"] and "code" in j["error"]: raise FBchatFacebookError( - 'Error #{} when sending request: {}'.format( - j['error']['code'], repr(j['error']['debug_info']) + "Error #{} when sending request: {}".format( + j["error"]["code"], repr(j["error"]["debug_info"]) ), - fb_error_code=j['error']['code'], - fb_error_message=j['error']['debug_info'], + fb_error_code=j["error"]["code"], + fb_error_message=j["error"]["debug_info"], ) else: raise FBchatFacebookError( - 'Error {} when sending request'.format(j['error']), fb_error_code=j['error'] + "Error {} when sending request".format(j["error"]), fb_error_code=j["error"] ) def check_request(r, as_json=True): if not r.ok: raise FBchatFacebookError( - 'Error when sending request: Got {} response'.format(r.status_code), + "Error when sending request: Got {} response".format(r.status_code), request_status_code=r.status_code, ) content = get_decoded_r(r) if content is None or len(content) == 0: - raise FBchatFacebookError('Error when sending request: Got empty response') + raise FBchatFacebookError("Error when sending request: Got empty response") if as_json: content = strip_to_json(content) try: j = json.loads(content) except ValueError: - raise FBchatFacebookError('Error while parsing JSON: {!r}'.format(content)) + raise FBchatFacebookError("Error while parsing JSON: {!r}".format(content)) check_json(j) log.debug(j) return j @@ -283,12 +283,12 @@ def check_request(r, as_json=True): def get_jsmods_require(j, index): - if j.get('jsmods') and j['jsmods'].get('require'): + if j.get("jsmods") and j["jsmods"].get("require"): try: - return j['jsmods']['require'][0][index][0] + return j["jsmods"]["require"][0][index][0] except (KeyError, IndexError) as e: log.warning( - 'Error when getting jsmods_require: {}. Facebook might have changed protocol'.format( + "Error when getting jsmods_require: {}. Facebook might have changed protocol".format( j ) ) @@ -298,13 +298,13 @@ def get_jsmods_require(j, index): def get_emojisize_from_tags(tags): if tags is None: return None - tmp = [tag for tag in tags if tag.startswith('hot_emoji_size:')] + tmp = [tag for tag in tags if tag.startswith("hot_emoji_size:")] if len(tmp) > 0: try: - return LIKES[tmp[0].split(':')[1]] + return LIKES[tmp[0].split(":")[1]] except (KeyError, IndexError): log.exception( - 'Could not determine emoji size from {} - {}'.format(tags, tmp) + "Could not determine emoji size from {} - {}".format(tags, tmp) ) return None @@ -337,7 +337,7 @@ def get_files_from_urls(file_urls): ( basename(file_url), r.content, - r.headers.get('Content-Type') or guess_type(file_url)[0], + r.headers.get("Content-Type") or guess_type(file_url)[0], ) ) return files @@ -348,7 +348,7 @@ def get_files_from_paths(filenames): files = [] for filename in filenames: files.append( - (basename(filename), open(filename, 'rb'), guess_type(filename)[0]) + (basename(filename), open(filename, "rb"), guess_type(filename)[0]) ) yield files for fn, fp, ft in files: diff --git a/tests/conftest.py b/tests/conftest.py index a1e88cd..2e1f486 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -115,14 +115,14 @@ def compare(client, thread): def message_with_mentions(request, client, client2, group): text = "Hi there [" mentions = [] - if 'me' in request.param: + if "me" in request.param: mentions.append(Mention(thread_id=client.uid, offset=len(text), length=2)) text += "me, " - if 'other' in request.param: + if "other" in request.param: mentions.append(Mention(thread_id=client2.uid, offset=len(text), length=5)) text += "other, " # Unused, because Facebook don't properly support sending mentions with groups as targets - if 'group' in request.param: + if "group" in request.param: mentions.append(Mention(thread_id=group["id"], offset=len(text), length=5)) text += "group, " text += "nothing]" diff --git a/tests/test_send.py b/tests/test_send.py index 3990600..b5dec30 100644 --- a/tests/test_send.py +++ b/tests/test_send.py @@ -120,6 +120,6 @@ def test_send_remote_files(client, catch_event, compare): assert len(x.res["message_object"].attachments) == len(files) -@pytest.mark.parametrize('wave_first', [True, False]) +@pytest.mark.parametrize("wave_first", [True, False]) def test_wave(client, wave_first): client.wave(wave_first) diff --git a/tests/test_tests.py b/tests/test_tests.py index cfb2600..1367628 100644 --- a/tests/test_tests.py +++ b/tests/test_tests.py @@ -9,4 +9,4 @@ def test_catch_event(client2, catch_event): mid = "test" with catch_event("onMessage") as x: client2.onMessage(mid=mid) - assert x.res['mid'] == mid + assert x.res["mid"] == mid diff --git a/tests/test_thread_interraction.py b/tests/test_thread_interraction.py index 8a34530..041346b 100644 --- a/tests/test_thread_interraction.py +++ b/tests/test_thread_interraction.py @@ -135,7 +135,7 @@ def test_typing_status(client, catch_event, compare, status): assert compare(x, status=status) -@pytest.mark.parametrize('require_admin_approval', [True, False]) +@pytest.mark.parametrize("require_admin_approval", [True, False]) def test_change_approval_mode(client1, group, catch_event, require_admin_approval): with catch_event("onApprovalModeChange") as x: client1.changeGroupApprovalMode(require_admin_approval, group["id"]) diff --git a/tests/utils.py b/tests/utils.py index 241a6ad..52863b5 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -106,7 +106,7 @@ def load_client(n, cache): client = Client( load_variable("client{}_email".format(n), cache), load_variable("client{}_password".format(n), cache), - user_agent='Mozilla/5.0 (Windows NT 6.3; WOW64; ; NCT50_AAP285C84A1328) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36', + user_agent="Mozilla/5.0 (Windows NT 6.3; WOW64; ; NCT50_AAP285C84A1328) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36", session_cookies=cache.get("client{}_session".format(n), None), max_tries=1, )