Added support for deprecating items, and maybe support for python 2.7
- Changed `test_data.js` to `test_data.json` - Added `deprecated` decorator - Added `deprecation` function - Readded old functions, and marked them as deprecated - Changed parameters back to being type-in-specific (support for python 2.x) - Deprecated `info_log` and `debug` init paramters
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -23,4 +23,5 @@ develop-eggs
|
|||||||
docs/_build/
|
docs/_build/
|
||||||
|
|
||||||
# Data for tests
|
# Data for tests
|
||||||
|
test_data.json
|
||||||
tests.data
|
tests.data
|
@@ -15,7 +15,7 @@ from .client import *
|
|||||||
|
|
||||||
|
|
||||||
__copyright__ = 'Copyright 2015 by Taehoon Kim'
|
__copyright__ = 'Copyright 2015 by Taehoon Kim'
|
||||||
__version__ = '0.10.1'
|
__version__ = '0.10.2'
|
||||||
__license__ = 'BSD'
|
__license__ = 'BSD'
|
||||||
__author__ = 'Taehoon Kim; Moreels Pieter-Jan'
|
__author__ = 'Taehoon Kim; Moreels Pieter-Jan'
|
||||||
__email__ = 'carpedm20@gmail.com'
|
__email__ = 'carpedm20@gmail.com'
|
||||||
|
215
fbchat/client.py
215
fbchat/client.py
@@ -14,14 +14,12 @@
|
|||||||
import requests
|
import requests
|
||||||
import logging
|
import logging
|
||||||
from uuid import uuid1
|
from uuid import uuid1
|
||||||
import warnings
|
|
||||||
from random import choice
|
from random import choice
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from bs4 import BeautifulSoup as bs
|
from bs4 import BeautifulSoup as bs
|
||||||
from mimetypes import guess_type
|
from mimetypes import guess_type
|
||||||
from .utils import *
|
from .utils import *
|
||||||
from .models import *
|
from .models import *
|
||||||
from .stickers import *
|
|
||||||
import time
|
import time
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@@ -58,6 +56,9 @@ facebookEncoding = 'UTF-8'
|
|||||||
# Log settings
|
# Log settings
|
||||||
log = logging.getLogger("client")
|
log = logging.getLogger("client")
|
||||||
log.setLevel(logging.DEBUG)
|
log.setLevel(logging.DEBUG)
|
||||||
|
# Creates the console handler
|
||||||
|
handler = logging.StreamHandler()
|
||||||
|
log.addHandler(handler)
|
||||||
|
|
||||||
|
|
||||||
class Client(object):
|
class Client(object):
|
||||||
@@ -67,16 +68,17 @@ class Client(object):
|
|||||||
documentation for the API.
|
documentation for the API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, email, password, debug=True, info_log=True, user_agent=None, max_retries=5, session_cookies=None):
|
def __init__(self, email, password, debug=False, info_log=False, user_agent=None, max_retries=5, session_cookies=None, logging_level=logging.INFO):
|
||||||
"""A client for the Facebook Chat (Messenger).
|
"""A client for the Facebook Chat (Messenger).
|
||||||
|
|
||||||
:param email: Facebook `email` or `id` or `phone number`
|
:param email: Facebook `email` or `id` or `phone number`
|
||||||
:param password: Facebook account password
|
:param password: Facebook account password
|
||||||
:param debug: Configures the logger to `debug` logging_level
|
:param debug: Configures the logger to `debug` logging_level (deprecated)
|
||||||
:param info_log: Configures the logger to `info` logging_level
|
:param info_log: Configures the logger to `info` logging_level (deprecated)
|
||||||
:param user_agent: Custom user agent to use when sending requests. If `None`, user agent will be chosen from a premade list (see utils.py)
|
:param user_agent: Custom user agent to use when sending requests. If `None`, user agent will be chosen from a premade list (see utils.py)
|
||||||
:param max_retries: Maximum number of times to retry login
|
:param max_retries: Maximum number of times to retry login
|
||||||
:param session_cookies: Cookie dict from a previous session (Will default to login if these are invalid)
|
:param session_cookies: Cookie dict from a previous session (Will default to login if these are invalid)
|
||||||
|
:param logging_level: Configures the logger to logging_level
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.sticky, self.pool = (None, None)
|
self.sticky, self.pool = (None, None)
|
||||||
@@ -86,9 +88,8 @@ class Client(object):
|
|||||||
self.payloadDefault = {}
|
self.payloadDefault = {}
|
||||||
self.client = 'mercury'
|
self.client = 'mercury'
|
||||||
self.listening = False
|
self.listening = False
|
||||||
self.is_def_thread_set = False
|
self.default_thread_id = None
|
||||||
self.def_thread_id = None
|
self.default_thread_type = None
|
||||||
self.def_thread_type = None
|
|
||||||
self.threads = []
|
self.threads = []
|
||||||
|
|
||||||
if not user_agent:
|
if not user_agent:
|
||||||
@@ -104,21 +105,39 @@ class Client(object):
|
|||||||
|
|
||||||
# Configure the logger differently based on the 'debug' and 'info_log' parameters
|
# Configure the logger differently based on the 'debug' and 'info_log' parameters
|
||||||
if debug:
|
if debug:
|
||||||
|
deprecation('Client(debug)', deprecated_in='0.6.0', details='Use Client(logging_level) instead')
|
||||||
logging_level = logging.DEBUG
|
logging_level = logging.DEBUG
|
||||||
elif info_log:
|
elif info_log:
|
||||||
|
deprecation('Client(info_log)', deprecated_in='0.6.0', details='Use Client(logging_level) instead')
|
||||||
logging_level = logging.INFO
|
logging_level = logging.INFO
|
||||||
else:
|
|
||||||
logging_level = logging.WARNING
|
|
||||||
|
|
||||||
# Creates the console handler
|
|
||||||
handler = logging.StreamHandler()
|
|
||||||
handler.setLevel(logging_level)
|
handler.setLevel(logging_level)
|
||||||
log.addHandler(handler)
|
|
||||||
|
|
||||||
# If session cookies aren't set, not properly loaded or gives us an invalid session, then do the login
|
# If session cookies aren't set, not properly loaded or gives us an invalid session, then do the login
|
||||||
if not session_cookies or not self.setSession(session_cookies) or not self.is_logged_in():
|
if not session_cookies or not self.setSession(session_cookies) or not self.is_logged_in():
|
||||||
self.login(email, password, max_retries)
|
self.login(email, password, max_retries)
|
||||||
|
|
||||||
|
@deprecated(deprecated_in='0.6.0', details='Use log.<level> instead')
|
||||||
|
def _console(self, msg):
|
||||||
|
"""Assumes an INFO level and log it.
|
||||||
|
|
||||||
|
This method shouldn't be used anymore.
|
||||||
|
Use the log itself:
|
||||||
|
>>> import logging
|
||||||
|
>>> from fbchat.client import log
|
||||||
|
>>> log.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
You can do the same thing by adding the 'debug' argument:
|
||||||
|
>>> from fbchat import Client
|
||||||
|
>>> client = Client("...", "...", debug=True)
|
||||||
|
"""
|
||||||
|
log.debug(msg)
|
||||||
|
|
||||||
|
def _setttstamp(self):
|
||||||
|
for i in self.fb_dtsg:
|
||||||
|
self.ttstamp += str(ord(i))
|
||||||
|
self.ttstamp += '2'
|
||||||
|
|
||||||
def _generatePayload(self, query):
|
def _generatePayload(self, query):
|
||||||
"""Adds the following defaults to the payload:
|
"""Adds the following defaults to the payload:
|
||||||
__rev, __user, __a, ttstamp, fb_dtsg, __req
|
__rev, __user, __a, ttstamp, fb_dtsg, __req
|
||||||
@@ -154,7 +173,7 @@ class Client(object):
|
|||||||
self.payloadDefault = {}
|
self.payloadDefault = {}
|
||||||
self.client_id = hex(int(random()*2147483648))[2:]
|
self.client_id = hex(int(random()*2147483648))[2:]
|
||||||
self.start_time = now()
|
self.start_time = now()
|
||||||
self.uid = str(self._session.cookies['c_user'])
|
self.uid = int(self._session.cookies['c_user'])
|
||||||
self.user_channel = "p_" + str(self.uid)
|
self.user_channel = "p_" + str(self.uid)
|
||||||
self.ttstamp = ''
|
self.ttstamp = ''
|
||||||
|
|
||||||
@@ -294,7 +313,8 @@ class Client(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def login(self, email, password, max_retries=5):
|
def login(self, email, password, max_retries=5):
|
||||||
self.onLoggingIn(email=email)
|
# Logging in
|
||||||
|
log.info("Logging in {}...".format(email))
|
||||||
|
|
||||||
if not (email and password):
|
if not (email and password):
|
||||||
raise Exception("Email and password not set.")
|
raise Exception("Email and password not set.")
|
||||||
@@ -308,7 +328,7 @@ class Client(object):
|
|||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
self.onLoggedIn(email=email)
|
log.info("Login of {} successful.".format(email))
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
raise Exception("Login failed. Check email/password.")
|
raise Exception("Login failed. Check email/password.")
|
||||||
@@ -328,15 +348,18 @@ class Client(object):
|
|||||||
self.seq = "0"
|
self.seq = "0"
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def setDefaultThreadId(self, thread_id=str, thread_type=ThreadType):
|
@deprecated(deprecated_in='0.10.2', details='Use setDefaultThread instead')
|
||||||
"""Sets default recipient to send messages and images to.
|
def setDefaultRecipient(self, recipient_id, is_user=True):
|
||||||
|
self.setDefaultThread(recipient_id, thread_type=isUserToThreadType(is_user))
|
||||||
|
|
||||||
|
def setDefaultThread(self, thread_id=None, thread_type=ThreadType.USER):
|
||||||
|
"""Sets default thread to send messages and images to.
|
||||||
|
|
||||||
:param thread_id: user/group ID to default to
|
:param thread_id: user/group ID to default to
|
||||||
:param thread_type: type of thread_id
|
:param thread_type: type of thread_id
|
||||||
"""
|
"""
|
||||||
self.def_thread_id = thread_id
|
self.default_thread_id = thread_id
|
||||||
self.def_thread_type = thread_type
|
self.default_thread_type = thread_type
|
||||||
self.is_def_thread_set = True
|
|
||||||
|
|
||||||
def _adapt_user_in_chat_to_user_model(self, user_in_chat):
|
def _adapt_user_in_chat_to_user_model(self, user_in_chat):
|
||||||
""" Adapts user info from chat to User model acceptable initial dict
|
""" Adapts user info from chat to User model acceptable initial dict
|
||||||
@@ -424,7 +447,7 @@ class Client(object):
|
|||||||
SEND METHODS
|
SEND METHODS
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _send(self, thread_id=None, message=None, thread_type=None, emoji_size=None, image_id=None, add_user_ids=None, new_title=None):
|
def _send(self, thread_id=None, message=None, thread_type=ThreadType.USER, emoji_size=None, image_id=None, add_user_ids=None, new_title=None):
|
||||||
"""Send a message with given thread id
|
"""Send a message with given thread id
|
||||||
|
|
||||||
:param thread_id: the user id or thread id that you want to send a message to
|
:param thread_id: the user id or thread id that you want to send a message to
|
||||||
@@ -436,11 +459,12 @@ class Client(object):
|
|||||||
:return: a list of message ids of the sent message(s)
|
:return: a list of message ids of the sent message(s)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if thread_id is None and self.is_def_thread_set:
|
if thread_id is None:
|
||||||
thread_id = self.def_thread_id
|
if self.default_thread_id is not None:
|
||||||
thread_type = self.def_thread_type
|
thread_id = self.default_thread_id
|
||||||
elif thread_id is None and not self.is_def_thread_set:
|
thread_type = self.default_thread_type
|
||||||
raise ValueError('Default Thread ID is not set.')
|
else:
|
||||||
|
raise ValueError('Thread ID is not set.')
|
||||||
|
|
||||||
messageAndOTID = generateOfflineThreadingID()
|
messageAndOTID = generateOfflineThreadingID()
|
||||||
timestamp = now()
|
timestamp = now()
|
||||||
@@ -538,7 +562,11 @@ class Client(object):
|
|||||||
log.debug("With data {}".format(data))
|
log.debug("With data {}".format(data))
|
||||||
return message_ids
|
return message_ids
|
||||||
|
|
||||||
def sendMessage(self, message: str, thread_id: str = None, thread_type: ThreadType = None):
|
@deprecated(deprecated_in='0.10.2', details='Use specific functions (eg. sendMessage()) instead')
|
||||||
|
def send(self, recipient_id=None, message=None, is_user=True, like=None, image_id=None, add_user_ids=None):
|
||||||
|
return self._send(thread_id=recipient_id, message=message, thread_type=isUserToThreadType(is_user), emoji_size=LIKES[like], image_id=image_id, add_user_ids=add_user_ids)
|
||||||
|
|
||||||
|
def sendMessage(self, message, thread_id=None, thread_type=ThreadType.USER):
|
||||||
"""
|
"""
|
||||||
Sends a message to given (or default, if not) thread with an additional image.
|
Sends a message to given (or default, if not) thread with an additional image.
|
||||||
:param message: message to send
|
:param message: message to send
|
||||||
@@ -546,19 +574,20 @@ class Client(object):
|
|||||||
:param thread_type: specify whether thread_id is user or group chat
|
:param thread_type: specify whether thread_id is user or group chat
|
||||||
:return: a list of message ids of the sent message(s)
|
:return: a list of message ids of the sent message(s)
|
||||||
"""
|
"""
|
||||||
return self._send(thread_id, message, thread_type, None, None, None, None)
|
return self._send(thread_id=thread_id, message=message, thread_type=thread_type)
|
||||||
|
|
||||||
def sendEmoji(self, emoji_size: EmojiSize, thread_id: str = None, thread_type: ThreadType = None):
|
def sendEmoji(self, thread_id, size=Size.MEDIUM, emoji=None, thread_type=ThreadType.USER):
|
||||||
"""
|
"""
|
||||||
Sends an emoji to given (or default, if not) thread.
|
Sends an emoji to given (or default, if not) thread.
|
||||||
:param emoji_size: size of emoji to send
|
:param size: size of emoji to send
|
||||||
:param thread_id: user/group chat ID
|
:param thread_id: user/group chat ID
|
||||||
|
:param emoji: WIP
|
||||||
:param thread_type: specify whether thread_id is user or group chat
|
:param thread_type: specify whether thread_id is user or group chat
|
||||||
:return: a list of message ids of the sent message(s)
|
:return: a list of message ids of the sent message(s)
|
||||||
"""
|
"""
|
||||||
return self._send(thread_id, None, thread_type, emoji_size, None, None, None)
|
return self._send(thread_id=thread_id, thread_type=thread_type, emoji_size=size)
|
||||||
|
|
||||||
def sendRemoteImage(self, image_url: str, message: str = None, thread_id: str = None, thread_type: ThreadType = None):
|
def sendRemoteImage(self, image_url, message=None, thread_id=None, thread_type=ThreadType.USER, recipient_id=None, is_user=None, image=None):
|
||||||
"""
|
"""
|
||||||
Sends an image from given URL to given (or default, if not) thread.
|
Sends an image from given URL to given (or default, if not) thread.
|
||||||
:param image_url: URL of an image to upload and send
|
:param image_url: URL of an image to upload and send
|
||||||
@@ -567,13 +596,22 @@ class Client(object):
|
|||||||
:param thread_type: specify whether thread_id is user or group chat
|
:param thread_type: specify whether thread_id is user or group chat
|
||||||
:return: a list of message ids of the sent message(s)
|
:return: a list of message ids of the sent message(s)
|
||||||
"""
|
"""
|
||||||
|
if recipient_id is not None:
|
||||||
|
deprecation('sendRemoteImage(recipient_id)', deprecated_in='0.10.2', details='Use sendRemoteImage(thread_id) instead')
|
||||||
|
thread_id = recipient_id
|
||||||
|
if is_user is not None:
|
||||||
|
deprecation('sendRemoteImage(is_user)', deprecated_in='0.10.2', details='Use sendRemoteImage(thread_type) instead')
|
||||||
|
thread_type = isUserToThreadType(is_user)
|
||||||
|
if image is not None:
|
||||||
|
deprecation('sendRemoteImage(image)', deprecated_in='0.10.2', details='Use sendRemoteImage(image_url) instead')
|
||||||
|
image_url = image
|
||||||
mimetype = guess_type(image_url)[0]
|
mimetype = guess_type(image_url)[0]
|
||||||
remote_image = requests.get(image_url).content
|
remote_image = requests.get(image_url).content
|
||||||
image_id = self._uploadImage({'file': (image_url, remote_image, mimetype)})
|
image_id = self._uploadImage({'file': (image_url, remote_image, mimetype)})
|
||||||
return self._send(thread_id, message, thread_type, None, image_id, None, None)
|
return self._send(thread_id=thread_id, message=message, thread_type=thread_type, image_id=image_id)
|
||||||
|
|
||||||
# Doesn't upload properly
|
# Doesn't upload properly
|
||||||
def sendLocalImage(self, image_path: str, message: str = None, thread_id: str = None, thread_type: ThreadType = None):
|
def sendLocalImage(self, image_path, message=None, thread_id=None, thread_type=ThreadType.USER, recipient_id=None, is_user=None, image=None):
|
||||||
"""
|
"""
|
||||||
Sends an image from given URL to given (or default, if not) thread.
|
Sends an image from given URL to given (or default, if not) thread.
|
||||||
:param image_path: path of an image to upload and send
|
:param image_path: path of an image to upload and send
|
||||||
@@ -582,20 +620,29 @@ class Client(object):
|
|||||||
:param thread_type: specify whether thread_id is user or group chat
|
:param thread_type: specify whether thread_id is user or group chat
|
||||||
:return: a list of message ids of the sent message(s)
|
:return: a list of message ids of the sent message(s)
|
||||||
"""
|
"""
|
||||||
|
if recipient_id is not None:
|
||||||
|
deprecation('sendRemoteImage(recipient_id)', deprecated_in='0.10.2', details='Use sendLocalImage(thread_id) instead')
|
||||||
|
thread_id = recipient_id
|
||||||
|
if is_user is not None:
|
||||||
|
deprecation('sendRemoteImage(is_user)', deprecated_in='0.10.2', details='Use sendLocalImage(thread_type) instead')
|
||||||
|
thread_type = isUserToThreadType(is_user)
|
||||||
|
if image is not None:
|
||||||
|
deprecation('sendRemoteImage(image)', deprecated_in='0.10.2', details='Use sendLocalImage(image_path) instead')
|
||||||
|
image_path = image
|
||||||
mimetype = guess_type(image_path)[0]
|
mimetype = guess_type(image_path)[0]
|
||||||
image_id = self._uploadImage({'file': (image_path, open(image_path, 'rb'), mimetype)})
|
image_id = self._uploadImage({'file': (image_path, open(image_path, 'rb'), mimetype)})
|
||||||
return self._send(thread_id, message, thread_type, None, image_id, None, None)
|
return self._send(thread_id=thread_id, message=message, thread_type=thread_type, image_id=image_id)
|
||||||
|
|
||||||
def addUsersToChat(self, user_list: list, thread_id: str = None):
|
def addUsersToChat(self, user_ids, thread_id=None):
|
||||||
"""
|
"""
|
||||||
Adds users to given (or default, if not) thread.
|
Adds users to given (or default, if not) thread.
|
||||||
:param user_list: list of users to add
|
:param user_ids: list of user ids to add
|
||||||
:param thread_id: group chat ID
|
:param thread_id: group chat ID
|
||||||
:return: a list of message ids of the sent message(s)
|
:return: a list of message ids of the sent message(s)
|
||||||
"""
|
"""
|
||||||
return self._send(thread_id, None, ThreadType.GROUP, None, None, user_list, None)
|
return self._send(thread_id=thread_id, thread_type=ThreadType.GROUP, add_user_ids=users)
|
||||||
|
|
||||||
def removeUserFromChat(self, user_id: str, thread_id: str = None):
|
def removeUserFromChat(self, user_id, thread_id=None):
|
||||||
"""
|
"""
|
||||||
Adds users to given (or default, if not) thread.
|
Adds users to given (or default, if not) thread.
|
||||||
:param user_id: user ID to remove
|
:param user_id: user ID to remove
|
||||||
@@ -617,18 +664,28 @@ class Client(object):
|
|||||||
|
|
||||||
return r.ok
|
return r.ok
|
||||||
|
|
||||||
def changeThreadTitle(self, new_title: str, thread_id: str = None):
|
@deprecated(deprecated_in='0.10.2', details='Use removeUserFromChat() instead')
|
||||||
|
def add_users_to_chat(self, threadID, userID):
|
||||||
|
if not isinstance(userID, list):
|
||||||
|
userID = [userID]
|
||||||
|
return self.addUsersToChat(userID, thread_id=threadID)
|
||||||
|
|
||||||
|
@deprecated(deprecated_in='0.10.2', details='Use removeUserFromChat() instead')
|
||||||
|
def remove_user_from_chat(self, threadID, userID):
|
||||||
|
return self.removeUserFromChat(userID, thread_id=threadID)
|
||||||
|
|
||||||
|
@deprecated(deprecated_in='0.10.2', details='Use changeGroupTitle() instead')
|
||||||
|
def changeThreadTitle(self, threadID, newTitle):
|
||||||
|
return self.changeGroupTitle(newTitle, thread_id=threadID)
|
||||||
|
|
||||||
|
def changeGroupTitle(self, title, thread_id=None):
|
||||||
"""
|
"""
|
||||||
Change title of a group conversation.
|
Change title of a group conversation.
|
||||||
:param new_title: new group chat title
|
:param title: new group chat title
|
||||||
:param thread_id: group chat ID
|
:param thread_id: group chat ID
|
||||||
:return: a list of message ids of the sent message(s)
|
:return: a list of message ids of the sent message(s)
|
||||||
"""
|
"""
|
||||||
if thread_id is None and self.def_thread_type == ThreadType.GROUP:
|
return self._send(thread_id=thread_id, thread_type=ThreadType.GROUP, new_title=title)
|
||||||
thread_id = self.def_thread_id
|
|
||||||
elif thread_id is None:
|
|
||||||
raise ValueError('Default Thread ID is not set.')
|
|
||||||
return self._send(thread_id, None, ThreadType.GROUP, None, None, None, new_title)
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
END SEND METHODS
|
END SEND METHODS
|
||||||
@@ -647,7 +704,7 @@ class Client(object):
|
|||||||
# Strip the start and parse out the returned image_id
|
# Strip the start and parse out the returned image_id
|
||||||
return json.loads(response_content[9:])['payload']['metadata'][0]['image_id']
|
return json.loads(response_content[9:])['payload']['metadata'][0]['image_id']
|
||||||
|
|
||||||
def getThreadInfo(self, last_n=20, thread_id: str = None, thread_type: ThreadType = None):
|
def getThreadInfo(self, last_n=20, thread_id=None, thread_type=ThreadType.USER):
|
||||||
"""Get the info of one Thread
|
"""Get the info of one Thread
|
||||||
|
|
||||||
:param last_n: number of retrieved messages from start (default 20)
|
:param last_n: number of retrieved messages from start (default 20)
|
||||||
@@ -969,3 +1026,65 @@ class Client(object):
|
|||||||
if len(full_data)==1:
|
if len(full_data)==1:
|
||||||
full_data=full_data[0]
|
full_data=full_data[0]
|
||||||
return full_data
|
return full_data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def on_message_new(self, mid, author_id, message, metadata, recipient_id, thread_type):
|
||||||
|
"""subclass Client and override this method to add custom behavior on event
|
||||||
|
|
||||||
|
This version of on_message recieves recipient_id and thread_type.
|
||||||
|
For backwards compatability, this data is sent directly to the old on_message.
|
||||||
|
"""
|
||||||
|
self.on_message(mid, author_id, None, message, metadata)
|
||||||
|
|
||||||
|
@deprecated(deprecated_in='0.7.0', details='Use on_message_new() instead')
|
||||||
|
def on_message(self, mid, author_id, author_name, message, metadata):
|
||||||
|
"""subclass Client and override this method to add custom behavior on event"""
|
||||||
|
self.markAsDelivered(author_id, mid)
|
||||||
|
self.markAsRead(author_id)
|
||||||
|
log.info("%s said: %s" % (author_name, message))
|
||||||
|
|
||||||
|
|
||||||
|
def on_friend_request(self, from_id):
|
||||||
|
"""subclass Client and override this method to add custom behavior on event"""
|
||||||
|
log.info("Friend request from %s." % from_id)
|
||||||
|
|
||||||
|
|
||||||
|
def on_typing(self, author_id):
|
||||||
|
"""subclass Client and override this method to add custom behavior on event"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def on_read(self, author, reader, time):
|
||||||
|
"""subclass Client and override this method to add custom behavior on event"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def on_people_added(self, user_ids, actor_id, thread_id):
|
||||||
|
"""subclass Client and override this method to add custom behavior on event"""
|
||||||
|
log.info("User(s) {} was added to {} by {}".format(repr(user_ids), thread_id, actor_id))
|
||||||
|
|
||||||
|
|
||||||
|
def on_person_removed(self, user_id, actor_id, thread_id):
|
||||||
|
"""subclass Client and override this method to add custom behavior on event"""
|
||||||
|
log.info("User {} was removed from {} by {}".format(user_id, thread_id, actor_id))
|
||||||
|
|
||||||
|
|
||||||
|
def on_inbox(self, viewer, unseen, unread, other_unseen, other_unread, timestamp):
|
||||||
|
"""subclass Client and override this method to add custom behavior on event"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def on_message_error(self, exception, message):
|
||||||
|
"""subclass Client and override this method to add custom behavior on event"""
|
||||||
|
log.warning("Exception:\n{}".format(exception))
|
||||||
|
|
||||||
|
|
||||||
|
def on_qprimer(self, timestamp):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def on_unknown_type(self, m):
|
||||||
|
"""subclass Client and override this method to add custom behavior on event"""
|
||||||
|
log.debug("Unknown type {}".format(m))
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import sys
|
import sys
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
class Base():
|
class Base():
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@@ -37,7 +38,25 @@ class TypingStatus(Enum):
|
|||||||
DELETED = 0
|
DELETED = 0
|
||||||
TYPING = 1
|
TYPING = 1
|
||||||
|
|
||||||
class EmojiSize(Enum):
|
|
||||||
|
# WIP
|
||||||
|
class StickerSize(Enum):
|
||||||
LARGE = '369239383222810'
|
LARGE = '369239383222810'
|
||||||
MEDIUM = '369239343222814'
|
MEDIUM = '369239343222814'
|
||||||
SMALL = '369239263222822'
|
SMALL = '369239263222822'
|
||||||
|
|
||||||
|
#class Size(Enum):
|
||||||
|
# LARGE = 'large'
|
||||||
|
# MEDIUM = 'medium'
|
||||||
|
# SMALL = 'small'
|
||||||
|
|
||||||
|
Size = StickerSize
|
||||||
|
|
||||||
|
LIKES = {
|
||||||
|
'l': Size.LARGE,
|
||||||
|
'm': Size.MEDIUM,
|
||||||
|
's': Size.SMALL
|
||||||
|
}
|
||||||
|
LIKES['large'] = LIKES['l']
|
||||||
|
LIKES['medium'] =LIKES['m']
|
||||||
|
LIKES['small'] = LIKES['s']
|
||||||
|
@@ -1,8 +0,0 @@
|
|||||||
LIKES={
|
|
||||||
'l': '369239383222810',
|
|
||||||
'm': '369239343222814',
|
|
||||||
's': '369239263222822'
|
|
||||||
}
|
|
||||||
LIKES['large'] = LIKES['l']
|
|
||||||
LIKES['medium'] =LIKES['m']
|
|
||||||
LIKES['small'] = LIKES['s']
|
|
@@ -2,6 +2,7 @@ import re
|
|||||||
import json
|
import json
|
||||||
from time import time
|
from time import time
|
||||||
from random import random
|
from random import random
|
||||||
|
import warnings
|
||||||
USER_AGENTS = [
|
USER_AGENTS = [
|
||||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36",
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36",
|
||||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/601.1.10 (KHTML, like Gecko) Version/8.0.5 Safari/601.1.10",
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/601.1.10 (KHTML, like Gecko) Version/8.0.5 Safari/601.1.10",
|
||||||
@@ -62,3 +63,30 @@ def generateOfflineThreadingID() :
|
|||||||
msgs = bin(ret) + string
|
msgs = bin(ret) + string
|
||||||
return str(int(msgs,2))
|
return str(int(msgs,2))
|
||||||
|
|
||||||
|
def isUserToThreadType(is_user):
|
||||||
|
return ThreadType.USER if is_user else ThreadType.GROUP
|
||||||
|
|
||||||
|
def deprecation(name, deprecated_in=None, details='', stacklevel=3):
|
||||||
|
"""This is a function which should be used to mark parameters as deprecated.
|
||||||
|
It will result in a warning being emmitted when the parameter is used.
|
||||||
|
"""
|
||||||
|
warning = "{} is deprecated".format(name)
|
||||||
|
if deprecated_in:
|
||||||
|
warning += ' in v. {}'.format(deprecated_in)
|
||||||
|
if details:
|
||||||
|
warning += '. {}'.format(details)
|
||||||
|
|
||||||
|
warnings.simplefilter('always', DeprecationWarning)
|
||||||
|
warnings.warn(warning, category=DeprecationWarning, stacklevel=stacklevel)
|
||||||
|
warnings.simplefilter('default', DeprecationWarning)
|
||||||
|
|
||||||
|
def deprecated(deprecated_in=None, details=''):
|
||||||
|
"""This is a decorator which can be used to mark functions as deprecated.
|
||||||
|
It will result in a warning being emmitted when the decorated function is used.
|
||||||
|
"""
|
||||||
|
def wrap(func, *args, **kwargs):
|
||||||
|
def wrapped_func(*args, **kwargs):
|
||||||
|
deprecation(func.__qualname__, deprecated_in, details, stacklevel=2)
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
return wrapped_func
|
||||||
|
return wrap
|
||||||
|
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"email": "",
|
|
||||||
"password": "",
|
|
||||||
"user_thread_id": "",
|
|
||||||
"group_thread_id": ""
|
|
||||||
}
|
|
50
tests.py
50
tests.py
@@ -10,21 +10,27 @@ import unittest
|
|||||||
import sys
|
import sys
|
||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
# Disable logging
|
#Setup logging
|
||||||
logging.basicConfig(level=100)
|
logging.basicConfig(level=logging.INFO)
|
||||||
fbchat.log.setLevel(100)
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Tests for fbchat
|
Tests for fbchat
|
||||||
~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
To use these tests, put:
|
To use these tests, make a json file called test_data.json, put this example in it, and fill in the gaps:
|
||||||
- email
|
{
|
||||||
- password
|
"email": "example@email.com",
|
||||||
- a group_uid
|
"password": "example_password",
|
||||||
- a user_uid (the user will be kicked from the group and then added again) `test_data.js`,
|
"group_thread_id": 0,
|
||||||
or type them manually in the terminal prompts
|
"user_thread_id": 0
|
||||||
|
}
|
||||||
|
or type this information manually in the terminal prompts.
|
||||||
|
|
||||||
|
- email: Your (or a test user's) email / phone number
|
||||||
|
- password: Your (or a test user's) password
|
||||||
|
- group_thread_id: A test group that will be used to test group functionality
|
||||||
|
- user_thread_id: A person that will be used to test kick/add functionality (This user should be in the group)
|
||||||
|
|
||||||
Please remember to test both python v. 2.7 and python v. 3.6!
|
Please remember to test both python v. 2.7 and python v. 3.6!
|
||||||
|
|
||||||
@@ -101,8 +107,8 @@ class TestFbchat(unittest.TestCase):
|
|||||||
self.assertTrue(client.sendRemoteImage(image_url, 'test_send_user_images_remote', user_uid, ThreadType.USER))
|
self.assertTrue(client.sendRemoteImage(image_url, 'test_send_user_images_remote', user_uid, ThreadType.USER))
|
||||||
self.assertTrue(client.sendRemoteImage(image_url, 'test_send_group_images_remote', group_uid, ThreadType.GROUP))
|
self.assertTrue(client.sendRemoteImage(image_url, 'test_send_group_images_remote', group_uid, ThreadType.GROUP))
|
||||||
# Idk why but doesnt work, payload is null
|
# Idk why but doesnt work, payload is null
|
||||||
# self.assertTrue(client.sendLocalImage(image_local_url, 'test_send_group_images_local', user_uid, ThreadType.USER))
|
self.assertTrue(client.sendLocalImage(image_local_url, 'test_send_group_images_local', user_uid, ThreadType.USER))
|
||||||
# self.assertTrue(client.sendLocalImage(image_local_url, 'test_send_group_images_local', group_uid, ThreadType.GROUP))
|
self.assertTrue(client.sendLocalImage(image_local_url, 'test_send_group_images_local', group_uid, ThreadType.GROUP))
|
||||||
|
|
||||||
def test_getThreadInfo(self):
|
def test_getThreadInfo(self):
|
||||||
client.sendMessage('test_user_getThreadInfo', user_uid, ThreadType.USER)
|
client.sendMessage('test_user_getThreadInfo', user_uid, ThreadType.USER)
|
||||||
@@ -117,12 +123,12 @@ class TestFbchat(unittest.TestCase):
|
|||||||
self.assertEquals(info[0].author, 'fbid:' + client.uid)
|
self.assertEquals(info[0].author, 'fbid:' + client.uid)
|
||||||
self.assertEquals(info[0].body, 'test_group_getThreadInfo')
|
self.assertEquals(info[0].body, 'test_group_getThreadInfo')
|
||||||
|
|
||||||
# def test_markAs(self):
|
def test_markAs(self):
|
||||||
# # To be implemented (requires some form of manual watching)
|
# To be implemented (requires some form of manual watching)
|
||||||
# pass
|
pass
|
||||||
|
|
||||||
# def test_listen(self):
|
def test_listen(self):
|
||||||
# client.doOneListen()
|
client.doOneListen()
|
||||||
|
|
||||||
def test_getUserInfo(self):
|
def test_getUserInfo(self):
|
||||||
info = client.getUserInfo(4)
|
info = client.getUserInfo(4)
|
||||||
@@ -165,15 +171,15 @@ if __name__ == 'tests':
|
|||||||
try:
|
try:
|
||||||
with open(path.join(path.dirname(__file__), 'test_data.js'), 'r') as f:
|
with open(path.join(path.dirname(__file__), 'test_data.js'), 'r') as f:
|
||||||
json = json.load(f)
|
json = json.load(f)
|
||||||
email = json["email"]
|
email = json['email']
|
||||||
password = json["password"]
|
password = json['password']
|
||||||
user_uid = json["user_thread_id"]
|
user_uid = json['user_thread_id']
|
||||||
group_uid = json["group_thread_id"]
|
group_uid = json['group_thread_id']
|
||||||
except (IOError, IndexError) as e:
|
except (IOError, IndexError) as e:
|
||||||
email = input('Email: ')
|
email = input('Email: ')
|
||||||
password = getpass.getpass()
|
password = getpass.getpass()
|
||||||
group_uid = input('Please enter a group uid (To test group functionality): ')
|
group_uid = input('Please enter a group thread id (To test group functionality): ')
|
||||||
user_uid = input('Please enter a user uid (To test kicking/adding functionality): ')
|
user_uid = input('Please enter a user thread id (To test kicking/adding functionality): ')
|
||||||
|
|
||||||
print('Logging in...')
|
print('Logging in...')
|
||||||
client = fbchat.Client(email, password)
|
client = fbchat.Client(email, password)
|
||||||
|
Reference in New Issue
Block a user