Removed deprecations and new event system, improved other things
Removed deprecations Removed new event system Added documentation for all events Added FAQ Changed Client.uid to Client.id Improved User model Prepared for support of pages
This commit is contained in:
@@ -14,7 +14,7 @@ from datetime import datetime
|
||||
from .client import *
|
||||
|
||||
__copyright__ = 'Copyright 2015 - {} by Taehoon Kim'.format(datetime.now().year)
|
||||
__version__ = '0.10.4'
|
||||
__version__ = '1.0.0'
|
||||
__license__ = 'BSD'
|
||||
__author__ = 'Taehoon Kim; Moreels Pieter-Jan; Mads Marquart'
|
||||
__email__ = 'carpedm20@gmail.com'
|
||||
|
978
fbchat/client.py
978
fbchat/client.py
File diff suppressed because it is too large
Load Diff
@@ -1,28 +0,0 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
class EventHook(object):
|
||||
"""
|
||||
A simple implementation of the Observer-Pattern.
|
||||
All listeners added to this will be called, regardless of parameters
|
||||
"""
|
||||
|
||||
def __init__(self, *args):
|
||||
self._handlers = list(args)
|
||||
|
||||
def add(self, handler):
|
||||
return self.__iadd__(handler)
|
||||
|
||||
def remove(self, handler):
|
||||
return self.__isub__(handler)
|
||||
|
||||
def __iadd__(self, handler):
|
||||
self._handlers.append(handler)
|
||||
return self
|
||||
|
||||
def __isub__(self, handler):
|
||||
self._handlers.remove(handler)
|
||||
return self
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
for handler in self._handlers:
|
||||
handler(*args, **kwargs)
|
118
fbchat/models.py
118
fbchat/models.py
@@ -3,81 +3,76 @@
|
||||
from __future__ import unicode_literals
|
||||
import enum
|
||||
|
||||
class User(object):
|
||||
"""Represents a Facebook User"""
|
||||
|
||||
class Thread(object):
|
||||
#: The unique identifier of the user. Can be used a `thread_id`. See :ref:`intro_threads` for more info
|
||||
uid = None
|
||||
#: Currently always set to `user`. Might change in the future
|
||||
type = 'user'
|
||||
#: The profile picture of the user
|
||||
id = None
|
||||
#: Specifies the type of thread. Uses ThreadType
|
||||
type = None
|
||||
#: The thread's picture
|
||||
photo = None
|
||||
#: The profile url
|
||||
url = None
|
||||
#: The name of the user
|
||||
#: The name of the thread
|
||||
name = None
|
||||
#: Only used by :any:`Client.getUsers`. Each user is assigned a score between 0 and 1, based on how likely it is that they were the person being searched for
|
||||
score = None
|
||||
#: Dictionary containing raw userdata from when the :class:`User` was created
|
||||
data = None
|
||||
|
||||
def __init__(self, data):
|
||||
"""Represents a Facebook User"""
|
||||
if data['type'] != 'user':
|
||||
raise Exception("[!] %s <%s> is not a user" % (data['text'], data['path']))
|
||||
self.uid = data['uid']
|
||||
self.type = data['type']
|
||||
self.photo = data['photo']
|
||||
self.url = data['path']
|
||||
self.name = data['text']
|
||||
self.score = float(data['score'])
|
||||
self.data = data
|
||||
def __init__(self, _type, _id, photo=None, name=None):
|
||||
"""Represents a Facebook thread"""
|
||||
self.id = str(_id)
|
||||
self.type = _type
|
||||
self.photo = photo
|
||||
self.name = name
|
||||
|
||||
def __repr__(self):
|
||||
return self.__unicode__()
|
||||
|
||||
def __unicode__(self):
|
||||
return u'<%s %s (%s)>' % (self.type.upper(), self.name, self.url)
|
||||
return '<{} {} ({})>'.format(self.type.name, self.name, self.id)
|
||||
|
||||
@staticmethod
|
||||
def _adaptFromChat(user_in_chat):
|
||||
"""Adapts user info from chat to User model acceptable initial dict
|
||||
|
||||
:param user_in_chat: user info from chat
|
||||
:return: :class:`User` object
|
||||
class User(Thread):
|
||||
#: The profile url
|
||||
url = None
|
||||
#: The users first name
|
||||
first_name = None
|
||||
#: The users last name
|
||||
last_name = None
|
||||
#: Whether the user and the client are friends
|
||||
is_friend = None
|
||||
#: The user's gender
|
||||
gender = None
|
||||
|
||||
'dir': None,
|
||||
'mThumbSrcSmall': None,
|
||||
'is_friend': False,
|
||||
'is_nonfriend_messenger_contact': True,
|
||||
'alternateName': '',
|
||||
'i18nGender': 16777216,
|
||||
'vanity': '',
|
||||
'type': 'friend',
|
||||
'searchTokens': ['Voznesenskij', 'Sergej'],
|
||||
'thumbSrc': 'https://fb-s-b-a.akamaihd.net/h-ak-xfa1/v/t1.0-1/c9.0.32.32/p32x32/10354686_10150004552801856_220367501106153455_n.jpg?oh=71a87d76d4e4d17615a20c43fb8dbb47&oe=59118CE4&__gda__=1493753268_ae75cef40e9785398e744259ccffd7ff',
|
||||
'mThumbSrcLarge': None,
|
||||
'firstName': 'Sergej',
|
||||
'name': 'Sergej Voznesenskij',
|
||||
'uri': 'https://www.facebook.com/profile.php?id=100014812758264',
|
||||
'id': '100014812758264',
|
||||
'gender': 2
|
||||
"""
|
||||
def __init__(self, _id, url=None, first_name=None, last_name=None, is_friend=None, gender=None, **kwargs):
|
||||
"""Represents a Facebook user. Inherits `Thread`"""
|
||||
super(User, self).__init__(ThreadType.USER, _id, **kwargs)
|
||||
self.url = url
|
||||
self.first_name = first_name
|
||||
self.last_name = last_name
|
||||
self.is_friend = is_friend
|
||||
self.gender = gender
|
||||
|
||||
return {
|
||||
'type': 'user',
|
||||
'uid': user_in_chat['id'],
|
||||
'photo': user_in_chat['thumbSrc'],
|
||||
'path': user_in_chat['uri'],
|
||||
'text': user_in_chat['name'],
|
||||
'score': 1,
|
||||
'data': user_in_chat,
|
||||
}
|
||||
|
||||
class Thread(object):
|
||||
"""Represents a thread. Currently just acts as a dict"""
|
||||
def __init__(self, **entries):
|
||||
self.__dict__.update(entries)
|
||||
class Group(Thread):
|
||||
def __init__(self, _id, **kwargs):
|
||||
"""Represents a Facebook group. Inherits `Thread`"""
|
||||
super(Group, self).__init__(ThreadType.GROUP, _id, **kwargs)
|
||||
|
||||
|
||||
class Page(Thread):
|
||||
#: The page's custom url
|
||||
url = None
|
||||
#: The name of the page's location city
|
||||
city = None
|
||||
#: Amount of likes the page has
|
||||
likees = None
|
||||
#: Some extra information about the page
|
||||
sub_text = None
|
||||
|
||||
def __init__(self, _id, url=None, city=None, likees=None, sub_text=None, **kwargs):
|
||||
"""Represents a Facebook page. Inherits `Thread`"""
|
||||
super(Page, self).__init__(ThreadType.PAGE, _id, **kwargs)
|
||||
self.url = url
|
||||
self.city = city
|
||||
self.likees = likees
|
||||
self.sub_text = sub_text
|
||||
|
||||
|
||||
class Message(object):
|
||||
"""Represents a message. Currently just acts as a dict"""
|
||||
@@ -94,6 +89,7 @@ class ThreadType(Enum):
|
||||
"""Used to specify what type of Facebook thread is being used. See :ref:`intro_threads` for more info"""
|
||||
USER = 1
|
||||
GROUP = 2
|
||||
PAGE = 3
|
||||
|
||||
class TypingStatus(Enum):
|
||||
"""Used to specify whether the user is typing or has stopped typing"""
|
||||
|
@@ -6,8 +6,22 @@ import json
|
||||
from time import time
|
||||
from random import random
|
||||
import warnings
|
||||
import logging
|
||||
from .models import *
|
||||
|
||||
# Python 2's `input` executes the input, whereas `raw_input` just returns the input
|
||||
try:
|
||||
input = raw_input
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
# Log settings
|
||||
log = logging.getLogger("client")
|
||||
log.setLevel(logging.DEBUG)
|
||||
# Creates the console handler
|
||||
handler = logging.StreamHandler()
|
||||
log.addHandler(handler)
|
||||
|
||||
#: Default list of 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",
|
||||
@@ -49,7 +63,7 @@ class ReqUrl(object):
|
||||
STICKY = "https://0-edge-chat.facebook.com/pull"
|
||||
PING = "https://0-channel-proxy-06-ash2.facebook.com/active_ping"
|
||||
UPLOAD = "https://upload.facebook.com/ajax/mercury/upload.php"
|
||||
USER_INFO = "https://www.facebook.com/chat/user_info/"
|
||||
INFO = "https://www.facebook.com/chat/user_info/"
|
||||
CONNECT = "https://www.facebook.com/ajax/add_friend/action.php?dpr=1"
|
||||
REMOVE_USER = "https://www.facebook.com/chat/remove_participants/"
|
||||
LOGOUT = "https://www.facebook.com/logout.php"
|
||||
@@ -82,7 +96,7 @@ def get_decoded(r):
|
||||
def get_json(r):
|
||||
return json.loads(strip_to_json(get_decoded(r)))
|
||||
|
||||
def digit_to_char(digit):
|
||||
def digitToChar(digit):
|
||||
if digit < 10:
|
||||
return str(digit)
|
||||
return chr(ord('a') + digit - 10)
|
||||
@@ -92,8 +106,8 @@ def str_base(number, base):
|
||||
return '-' + str_base(-number, base)
|
||||
(d, m) = divmod(number, base)
|
||||
if d > 0:
|
||||
return str_base(d, base) + digit_to_char(m)
|
||||
return digit_to_char(m)
|
||||
return str_base(d, base) + digitToChar(m)
|
||||
return digitToChar(m)
|
||||
|
||||
def generateMessageID(client_id=None):
|
||||
k = now()
|
||||
@@ -110,31 +124,20 @@ def generateOfflineThreadingID():
|
||||
msgs = format(ret, 'b') + string
|
||||
return str(int(msgs, 2))
|
||||
|
||||
def isUserToThreadType(is_user):
|
||||
return ThreadType.USER if is_user else ThreadType.GROUP
|
||||
def checkRequest(r, check_json=True):
|
||||
if not r.ok:
|
||||
raise Exception('Error when sending request: Got {} response'.format(r.status_code))
|
||||
|
||||
def raise_exception(e):
|
||||
raise e
|
||||
content = get_decoded(r)
|
||||
|
||||
def deprecation(name, deprecated_in=None, removed_in=None, details='', stacklevel=3):
|
||||
"""Used to mark parameters as deprecated. Will result in a warning being emmitted when the parameter is used."""
|
||||
warning = "Client.{} is deprecated".format(name)
|
||||
if deprecated_in:
|
||||
warning += ' in v. {}'.format(deprecated_in)
|
||||
if removed_in:
|
||||
warning += ' and will be removed in v. {}'.format(removed_in)
|
||||
if details:
|
||||
warning += '. {}'.format(details)
|
||||
if content is None or len(content) == 0:
|
||||
raise Exception('Error when sending request: Got empty response')
|
||||
|
||||
warnings.simplefilter('always', DeprecationWarning)
|
||||
warnings.warn(warning, category=DeprecationWarning, stacklevel=stacklevel)
|
||||
warnings.simplefilter('default', DeprecationWarning)
|
||||
|
||||
def deprecated(deprecated_in=None, removed_in=None, details=''):
|
||||
"""A decorator used to mark functions as deprecated. 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.__name__, deprecated_in=deprecated_in, removed_in=removed_in, details=details, stacklevel=3)
|
||||
return func(*args, **kwargs)
|
||||
return wrapped_func
|
||||
return wrap
|
||||
if check_json:
|
||||
j = json.loads(strip_to_json(content))
|
||||
if 'error' in j:
|
||||
# 'errorDescription' is in the users own language!
|
||||
raise Exception('Error #{} when sending request: {}'.format(j['error'], j['errorDescription']))
|
||||
return j
|
||||
else:
|
||||
return r
|
||||
|
Reference in New Issue
Block a user