Compare commits

...

26 Commits

Author SHA1 Message Date
Mads Marquart
fea4ad9e89 Version up, Thanks to ritu99 2017-08-10 15:25:38 +02:00
Mads Marquart
68099049d4 Merge pull request #189 from ritu99/master
Added Message Count to thread information
2017-08-10 15:22:22 +02:00
Ritvik Annam
44cf08bdfd fetchThreadInfo now pulls message_count 2017-08-10 01:15:29 -05:00
Ritvik Annam
9e32cf17a4 fetchThreadList now pulls message_count 2017-08-10 00:53:06 -05:00
Mads Marquart
0661367ebb Properly fixed #182 2017-08-02 23:08:34 +02:00
Mads Marquart
3c07e42ba2 Version up, fixed #182 2017-07-26 23:13:19 +02:00
Mads Marquart
2cd6376818 Merge pull request #178 from Bankde/fix-fail-after-running-for-days
Fix issue when running for long time
2017-07-26 23:03:49 +02:00
Mads Marquart
5e7f7750de Fixed enums in python 2.7, thanks to @liamkirsh 2017-07-12 14:52:15 +02:00
Bankde@hotmail.com
2a223ec6db fix array indexing (I don't know why fb do that) 2017-07-10 10:25:23 +07:00
Mads Marquart
a99108fff6 Version up thanks to @Bankde 2017-07-09 20:55:06 +02:00
Mads Marquart
8de4698cc4 Merge pull request #174 from Bankde/fix-error-in-python2
No FileNotFoundError in py2
2017-07-09 20:53:47 +02:00
Bankde@hotmail.com
637319ec2c add token update 2017-07-10 00:51:51 +07:00
Bankde@hotmail.com
f9398564cd replace FileNotFoundError with IOError so it can work in Py2 2017-07-05 09:18:13 +07:00
Mads Marquart
b57f423eb4 Version up thanks to @aaronlewism 2017-07-01 12:48:02 +02:00
Mads Marquart
3093f1f2b6 Merge pull request #173 from aaronlewism/master
Check for alternate 2Factor page text
2017-07-01 12:46:11 +02:00
Aaron Lewis
961777e0c1 Check for alternate 2Factor page text 2017-06-29 13:21:25 -07:00
Mads Marquart
d7139701f7 Fixed typo, improved formatting. Thanks to @JarbasAI! 2017-06-29 20:04:01 +02:00
Mads Marquart
c6bac17d48 Merge pull request #172 from JarbasAI/patch-1
Add on chat presence event
2017-06-29 19:55:49 +02:00
Mads Marquart
3638fc5356 Made fetchThreadInfo able to fetch own user's info 2017-06-29 19:53:29 +02:00
Jarbas
aca9176f7f Add on chat presence event
Last_seen time stamps were handled in unknown message type, this info is freely available and potentially useful
2017-06-29 17:56:14 +01:00
Mads Marquart
0d5e4f6d3f Version up thanks to @enwar3 2017-06-29 16:03:55 +02:00
Mads Marquart
92a5ffdef8 Merge pull request #170 from OMGWINNING/master
Add extensible_attachment field to Message for fb share objects
2017-06-29 16:02:27 +02:00
Joe Lau
b3359fccdb Add last_message_timestamp to Thread objects 2017-06-28 18:08:45 -07:00
Joe Lau
d8f7366d1f Add extensible_attachment field to Message for fb share objects 2017-06-28 13:19:17 -07:00
Mads Marquart
ff94dc20af Minor cleanup 2017-06-28 16:06:13 +02:00
Mads Marquart
a8df0a548f Minor fixes 2017-06-28 14:42:11 +02:00
6 changed files with 75 additions and 38 deletions

View File

@@ -17,7 +17,7 @@ from .client import *
__copyright__ = 'Copyright 2015 - {} by Taehoon Kim'.format(datetime.now().year)
__version__ = '1.0.7'
__version__ = '1.0.18'
__license__ = 'BSD'
__author__ = 'Taehoon Kim; Moreels Pieter-Jan; Mads Marquart'
__email__ = 'carpedm20@gmail.com'

View File

@@ -3,7 +3,6 @@
from __future__ import unicode_literals
import requests
import urllib
import traceback
from uuid import uuid1
from random import choice
from datetime import datetime
@@ -72,6 +71,9 @@ class Client(object):
# 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.isLoggedIn():
self.login(email, password, max_tries)
else:
self.email = email
self.password = password
"""
INTERNAL REQUEST METHODS
@@ -202,7 +204,8 @@ class Client(object):
r = self._cleanPost(ReqUrl.LOGIN, data)
# Usually, 'Checkpoint' will refer to 2FA
if 'checkpoint' in r.url and 'Enter Security Code to Continue' in r.text:
if ('checkpoint' in r.url and
('Enter Security Code to Continue' in r.text or 'Enter Login Code to Continue' in r.text)):
r = self._2FA(r)
# Sometimes Facebook tries to show the user a "Save Device" dialog
@@ -737,9 +740,10 @@ class Client(object):
if k['thread_type'] == 1:
if k['other_user_fbid'] not in participants:
raise Exception('A thread was not in participants: {}'.format(j['payload']))
participants[k['other_user_fbid']].message_count = k['message_count']
entries.append(participants[k['other_user_fbid']])
elif k['thread_type'] == 2:
entries.append(Group(k['thread_fbid'], participants=set([p.strip('fbid:') for p in k['participants']]), photo=k['image_src'], name=k['name']))
entries.append(Group(k['thread_fbid'], participants=set([p.strip('fbid:') for p in k['participants']]), photo=k['image_src'], name=k['name'], message_count=k['message_count']))
else:
raise Exception('A thread had an unknown thread type: {}'.format(k))
@@ -828,6 +832,13 @@ class Client(object):
except (KeyError, IndexError) as e:
raise Exception('Error when sending message: No message IDs could be found: {}'.format(j))
# update JS token if receive from response
if ('jsmods' in j) and ('require' in j['jsmods']):
try:
self.payloadDefault['fb_dtsg'] = j['jsmods']['require'][0][3][0]
except (KeyError, IndexError) as e:
log.warning("Error when update fb_dtsg. Facebook might have changed protocol.")
return message_id
def sendMessage(self, message, thread_id=None, thread_type=ThreadType.USER):
@@ -940,7 +951,7 @@ class Client(object):
"""
Sends a local image to a thread
:param image_path: URL of an image to upload and send
:param image_path: Path of an image to upload and send
:param message: Additional message
:param thread_id: User/Group ID to send to. See :ref:`intro_threads`
:param thread_type: See :ref:`intro_threads`
@@ -1279,7 +1290,6 @@ class Client(object):
thread_id = str(metadata['threadKey']['threadFbId'])
self.onPeopleAdded(mid=mid, added_ids=added_ids, author_id=author_id, thread_id=thread_id,
ts=ts, msg=m)
continue
# Left/removed participants
elif 'leftParticipantFbId' in delta:
@@ -1287,7 +1297,6 @@ class Client(object):
thread_id = str(metadata['threadKey']['threadFbId'])
self.onPersonRemoved(mid=mid, removed_id=removed_id, author_id=author_id, thread_id=thread_id,
ts=ts, msg=m)
continue
# Color change
elif delta_type == "change_thread_theme":
@@ -1295,7 +1304,6 @@ class Client(object):
thread_id, thread_type = getThreadIdAndThreadType(metadata)
self.onColorChange(mid=mid, author_id=author_id, new_color=new_color, thread_id=thread_id,
thread_type=thread_type, ts=ts, metadata=metadata, msg=m)
continue
# Emoji change
elif delta_type == "change_thread_icon":
@@ -1303,7 +1311,6 @@ class Client(object):
thread_id, thread_type = getThreadIdAndThreadType(metadata)
self.onEmojiChange(mid=mid, author_id=author_id, new_emoji=new_emoji, thread_id=thread_id,
thread_type=thread_type, ts=ts, metadata=metadata, msg=m)
continue
# Thread title change
elif delta.get("class") == "ThreadName":
@@ -1311,7 +1318,6 @@ class Client(object):
thread_id, thread_type = getThreadIdAndThreadType(metadata)
self.onTitleChange(mid=mid, author_id=author_id, new_title=new_title, thread_id=thread_id,
thread_type=thread_type, ts=ts, metadata=metadata, msg=m)
continue
# Nickname change
elif delta_type == "change_thread_nickname":
@@ -1321,7 +1327,6 @@ class Client(object):
self.onNicknameChange(mid=mid, author_id=author_id, changed_for=changed_for,
new_nickname=new_nickname,
thread_id=thread_id, thread_type=thread_type, ts=ts, metadata=metadata, msg=m)
continue
# Message delivered
elif delta.get("class") == "DeliveryReceipt":
@@ -1331,7 +1336,6 @@ class Client(object):
thread_id, thread_type = getThreadIdAndThreadType(delta)
self.onMessageDelivered(msg_ids=message_ids, delivered_for=delivered_for,
thread_id=thread_id, thread_type=thread_type, ts=ts, metadata=metadata, msg=m)
continue
# Message seen
elif delta.get("class") == "ReadReceipt":
@@ -1341,7 +1345,6 @@ class Client(object):
thread_id, thread_type = getThreadIdAndThreadType(delta)
self.onMessageSeen(seen_by=seen_by, thread_id=thread_id, thread_type=thread_type,
seen_ts=seen_ts, ts=delivered_ts, metadata=metadata, msg=m)
continue
# Messages marked as seen
elif delta.get("class") == "MarkRead":
@@ -1354,18 +1357,20 @@ class Client(object):
# thread_id, thread_type = getThreadIdAndThreadType(delta)
self.onMarkedSeen(threads=threads, seen_ts=seen_ts, ts=delivered_ts, metadata=delta, msg=m)
continue
# New message
elif delta.get("class") == "NewMessage":
message = delta.get('body', '')
thread_id, thread_type = getThreadIdAndThreadType(metadata)
self.onMessage(mid=mid, author_id=author_id, message=message,
thread_id=thread_id, thread_type=thread_type, ts=ts, metadata=m, msg=m)
continue
thread_id=thread_id, thread_type=thread_type, ts=ts, metadata=metadata, msg=m)
# Unknown message type
else:
self.onUnknownMesssageType(msg=m)
# Inbox
if mtype == "inbox":
elif mtype == "inbox":
self.onInbox(unseen=m["unseen"], unread=m["unread"], recent_unread=m["recent_unread"], msg=m)
# Typing
@@ -1393,6 +1398,14 @@ class Client(object):
elif mtype == "deltaflow":
pass
# Chat timestamp
elif mtype == "chatproxy-presence":
buddylist = {}
for _id in m.get('buddyList', {}):
payload = m['buddyList'][_id]
buddylist[_id] = payload.get('lat')
self.onChatTimestamp(buddylist=buddylist, msg=m)
# Unknown message type
else:
self.onUnknownMesssageType(msg=m)
@@ -1493,7 +1506,7 @@ class Client(object):
:param exception: The exception that was encountered
"""
traceback.print_exc()
log.exception('Got exception while listening')
def onMessage(self, mid=None, author_id=None, message=None, thread_id=None, thread_type=ThreadType.USER, ts=None, metadata=None, msg={}):
@@ -1680,6 +1693,14 @@ class Client(object):
"""
pass
def onChatTimestamp(self, buddylist={}, msg={}):
"""
Called when the client receives chat online presence update
: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))
def onUnknownMesssageType(self, msg={}):
"""

View File

@@ -30,7 +30,7 @@ def graphql_color_to_enum(color):
return ThreadColor.MESSENGER_BLUE
try:
return ThreadColor('#{}'.format(color[2:].lower()))
except KeyError, ValueError:
except ValueError:
raise Exception('Could not get ThreadColor from color: {}'.format(color))
def get_customization_info(thread):
@@ -44,18 +44,21 @@ def get_customization_info(thread):
}
if 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['participant_customizations']:
for k in info.get('participant_customizations', []):
rtn['nicknames'][k['participant_id']] = k.get('nickname')
elif info.get('participant_customizations'):
_id = thread.get('thread_key', {}).get('other_user_id') or thread.get('id')
if info['participant_customizations'][0]['participant_id'] == _id:
rtn['nickname'] = info['participant_customizations'][0]
rtn['own_nickname'] = info['participant_customizations'][1]
elif info['participant_customizations'][1]['participant_id'] == _id:
rtn['nickname'] = info['participant_customizations'][1]
rtn['own_nickname'] = info['participant_customizations'][0]
else:
raise Exception('No participant matching the user {} found: {}'.format(_id, info['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')
else:
rtn['own_nickname'] = pc[0].get('nickname')
if len(pc) > 1:
if pc[1].get('participant_id') == uid:
rtn['nickname'] = pc[1].get('nickname')
else:
rtn['own_nickname'] = pc[1].get('nickname')
return rtn
def graphql_to_message(message):
@@ -75,7 +78,8 @@ def graphql_to_message(message):
text=message.get('message').get('text'),
mentions=[Mention(m.get('entity', {}).get('id'), offset=m.get('offset'), length=m.get('length')) for m in message.get('message').get('ranges', [])],
sticker=message.get('sticker'),
attachments=message.get('blob_attachments')
attachments=message.get('blob_attachments'),
extensible_attachment=message.get('extensible_attachment')
)
def graphql_to_user(user):
@@ -95,7 +99,8 @@ def graphql_to_user(user):
emoji=c_info.get('emoji'),
own_nickname=c_info.get('own_nickname'),
photo=user['profile_picture'].get('uri'),
name=user.get('name')
name=user.get('name'),
message_count=user.get('messages_count')
)
def graphql_to_group(group):
@@ -109,7 +114,8 @@ def graphql_to_group(group):
color=c_info.get('color'),
emoji=c_info.get('emoji'),
photo=group['image'].get('uri'),
name=group.get('name')
name=group.get('name'),
message_count=group.get('messages_count')
)
def graphql_to_page(page):
@@ -123,7 +129,8 @@ def graphql_to_page(page):
city=page.get('city').get('name'),
category=page.get('category_type'),
photo=page['profile_picture'].get('uri'),
name=page.get('name')
name=page.get('name'),
message_count=page.get('messages_count')
)
def graphql_queries_to_json(*queries):

View File

@@ -13,13 +13,18 @@ class Thread(object):
photo = str
#: The name of the thread
name = str
def __init__(self, _type, uid, photo=None, name=None):
#: Timestamp of last message
last_message_timestamp = str
#: Number of messages in the thread
message_count = int
def __init__(self, _type, uid, photo=None, name=None, last_message_timestamp=None, message_count=None):
"""Represents a Facebook thread"""
self.uid = str(uid)
self.type = _type
self.photo = photo
self.name = name
self.last_message_timestamp = last_message_timestamp
self.message_count = message_count
def __repr__(self):
return self.__unicode__()
@@ -125,8 +130,10 @@ class Message(object):
sticker = str
#: A list of attachments
attachments = list
#: An extensible attachment, e.g. share object
extensible_attachment = dict
def __init__(self, uid, author=None, timestamp=None, is_read=None, reactions=[], text=None, mentions=[], sticker=None, attachments=[]):
def __init__(self, uid, author=None, timestamp=None, is_read=None, reactions=[], text=None, mentions=[], sticker=None, attachments=[], extensible_attachment={}):
"""Represents a Facebook message"""
self.uid = uid
self.author = author
@@ -137,6 +144,7 @@ class Message(object):
self.mentions = mentions
self.sticker = sticker
self.attachments = attachments
self.extensible_attachment = extensible_attachment
class Mention(object):

View File

@@ -1,3 +1,4 @@
requests
lxml
beautifulsoup4
enum34

View File

@@ -18,7 +18,7 @@ with open('README.rst') as f:
try:
requirements = [line.rstrip('\n') for line in open(os.path.join('fbchat.egg-info', 'requires.txt'))]
except FileNotFoundError:
except IOError:
requirements = [line.rstrip('\n') for line in open('requirements.txt')]
version = None