Skip to content

Commit 84b6309

Browse files
committed
Fix for api change with fbchat #55
1 parent 11599e8 commit 84b6309

File tree

2 files changed

+65
-159
lines changed

2 files changed

+65
-159
lines changed

fbchat/client.py

Lines changed: 55 additions & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
# URLs
2424
LoginURL ="https://m.facebook.com/login.php?login_attempt=1"
2525
SearchURL ="https://www.facebook.com/ajax/typeahead/search.php"
26-
SendURL ="https://www.facebook.com/ajax/mercury/send_messages.php"
26+
#SendURL ="https://www.facebook.com/ajax/mercury/send_messages.php"
27+
SendURL ="https://www.facebook.com/messaging/send/"
2728
ThreadsURL ="https://www.facebook.com/ajax/mercury/threadlist_info.php"
2829
ThreadSyncURL="https://www.facebook.com/ajax/mercury/thread_sync.php"
2930
MessagesURL ="https://www.facebook.com/ajax/mercury/thread_info.php"
@@ -220,63 +221,75 @@ def getUsers(self, name):
220221

221222
return users # have bug TypeError: __repr__ returned non-string (type bytes)
222223

223-
def send(self, thread_id, message=None, like=None):
224+
def send(self, recipient_id, message=None, message_type='user', like=None, image_id=None):
224225
"""Send a message with given thread id
225-
226-
:param thread_id: a thread id that you want to send a message
226+
:param recipient_id: the user id or thread id that you want to send a message to
227227
:param message: a text that you want to send
228+
:param message_type: determines if the recipient_id is for user or thread
228229
:param like: size of the like sticker you want to send
230+
:param image_id: id for the image to send, gotten from the UploadURL
229231
"""
230232

233+
if message_type.lower() == 'group':
234+
thread_id = recipient_id
235+
user_id = None
236+
else:
237+
thread_id = None
238+
user_id = recipient_id
239+
240+
messageAndOTID=generateOfflineThreadingID()
231241
timestamp = now()
232242
date = datetime.now()
233-
## see https://github.com/Schmavery/facebook-chat-api/blob/master/src/sendMessage.js
234243
data = {
235-
'client' : self.client,
236-
'message_batch[0][action_type]' : 'ma-type:user-generated-message',
237-
'message_batch[0][author]' : 'fbid:' + str(self.uid),
238-
#'message_batch[0][specific_to_list][0]' : 'fbid:' + str(thread_id),
239-
#'message_batch[0][specific_to_list][1]' : 'fbid:' + str(self.uid),
240-
'message_batch[0][timestamp]' : timestamp,
241-
'message_batch[0][timestamp_absolute]' : 'Today',
242-
'message_batch[0][timestamp_relative]' : str(date.hour) + ":" + str(date.minute).zfill(2),
243-
'message_batch[0][timestamp_time_passed]' : '0',
244-
'message_batch[0][is_unread]' : False,
245-
'message_batch[0][is_cleared]' : False,
246-
'message_batch[0][is_forward]' : False,
247-
'message_batch[0][is_filtered_content]' : False,
248-
'message_batch[0][is_spoof_warning]' : False,
249-
'message_batch[0][source]' : 'source:chat:web',
250-
'message_batch[0][source_tags][0]' : 'source:chat',
251-
'message_batch[0][body]' : message,
252-
'message_batch[0][html_body]' : False,
253-
'message_batch[0][ui_push_phase]' : 'V3',
254-
'message_batch[0][status]' : '0',
255-
'message_batch[0][message_id]' : generateMessageID(self.client_id),
256-
'message_batch[0][manual_retry_cnt]' : '0',
257-
'message_batch[0][thread_fbid]' : None,
258-
'message_batch[0][has_attachment]' : False,
244+
'client': self.client,
245+
'action_type' : 'ma-type:user-generated-message',
246+
'author' : 'fbid:' + str(self.uid),
247+
'timestamp' : timestamp,
248+
'timestamp_absolute' : 'Today',
249+
'timestamp_relative' : str(date.hour) + ":" + str(date.minute).zfill(2),
250+
'timestamp_time_passed' : '0',
251+
'is_unread' : False,
252+
'is_cleared' : False,
253+
'is_forward' : False,
254+
'is_filtered_content' : False,
255+
'is_filtered_content_bh': False,
256+
'is_filtered_content_account': False,
257+
'is_filtered_content_quasar': False,
258+
'is_filtered_content_invalid_app': False,
259+
'is_spoof_warning' : False,
260+
'source' : 'source:chat:web',
261+
'source_tags[0]' : 'source:chat',
262+
'body' : message,
263+
'html_body' : False,
264+
'ui_push_phase' : 'V3',
265+
'status' : '0',
266+
'offline_threading_id':messageAndOTID,
267+
'message_id' : messageAndOTID,
268+
'threading_id':generateMessageID(self.client_id),
269+
'ephemeral_ttl_mode:': '0',
270+
'manual_retry_cnt' : '0',
271+
'signatureID' : getSignatureID(),
272+
'has_attachment' : image_id != None,
273+
'other_user_fbid' : recipient_id,
274+
'specific_to_list[0]' : 'fbid:' + str(recipient_id),
275+
'specific_to_list[1]' : 'fbid:' + str(self.uid),
259276
}
260-
if self.last_isgroup:
261-
data['message_batch[0][thread_fbid]'] = thread_id
262-
else:
263-
data['message_batch[0][other_user_fbid]'] = thread_id
264-
data['message_batch[0][specific_to_list][0]'] = 'fbid:' + str(thread_id)
265-
data['message_batch[0][specific_to_list][1]'] = 'fbid:' + str(self.uid)
266-
267-
277+
278+
if image_id:
279+
data['message_batch[0][image_ids][0]'] = image_id
280+
268281
if like:
269282
try:
270283
sticker = LIKES[like.lower()]
271284
except KeyError:
272285
# if user doesn't enter l or m or s, then use the large one
273286
sticker = LIKES['l']
274287
data["message_batch[0][sticker_id]"] = sticker
275-
276-
open("send_data.txt","a").write(str(data)+"\n")
288+
277289
r = self._post(SendURL, data)
278290
return r.ok
279291

292+
280293
def getThreadInfo(self, userID, start, end=None):
281294
"""Get the info of one Thread
282295
@@ -452,131 +465,12 @@ def _parseTimeInMessage(self,metadata):
452465

453466
return self._parseTime(msgtime)
454467

455-
""" for testing new _parseMessage() code
456-
def _parseGroupMessage(self,metadata):
457-
if 'delta' in metadata['type']:
458-
thread_fbid = metadata['delta']['messageMetadata']['threadKey']['threadFbId']
459-
message=metadata['delta']['body']
460-
mid = metadata['delta']['messageMetadata']['messageId']
461-
sender_fbid = metadata['delta']['messageMetadata']['actorFbId']
462-
thread_name = thread_fbid
463-
sender_name = sender_fbid
464-
if thread_fbid in self.roster.keys(): self.last_tname = self._roster(thread_fbid)
465-
if sender_fbid in self.roster.keys(): sender_name = self._roster(fbid)
466-
elif 'messaging' in metadata['type']:
467-
thread_fbid = metadata['message']['thread_fbid']
468-
message=metadata['message']['body']
469-
mid = metadata['message']['mid']
470-
thread_name = metadata['message']['group_thread_info']['name']
471-
sender_fbid = metadata['message']['sender_fbid']
472-
sender_name = metadata['message']['sender_name']
473-
self._roster(thread_fbid,thread_name)
474-
self._roster(sender_fbid,sender_name)
475-
#print("_parsePersonalMessage(): get "+message)
476-
#print(mid)
477-
return message,mid,thread_fbid,sender_fbid
478-
479-
def _parsePersonalMessage(self,metadata):
480-
if metadata['type'] in ['delta']:
481-
mid = metadata['delta']['messageMetadata']['messageId']
482-
fbid = metadata['delta']['messageMetadata']['actorFbId']
483-
if 'body' in metadata['delta'].keys():
484-
message = metadata['delta']['body']
485-
else:
486-
print("get sticker")
487-
message = ['delta']['attachments'][0]['mercury']['url']
488-
elif metadata['type'] in ['m_messaging', 'messaging']:
489-
mid = metadata['message']['mid']
490-
message=metadata['message']['body']
491-
if 'sender_fbid' in metadata['message'].keys():
492-
fbid = metadata['message']['sender_fbid']
493-
else:
494-
fbid = metadata['message']['threadKey']['otherUserFbId']
495-
if 'sender_name' in metadata['message'].keys():
496-
name = metadata['message']['sender_name']
497-
self._roster(fbid,name)
498-
#print("_parsePersonalMessage(): get "+message)
499-
#print(mid)
500-
return message,mid,fbid
501-
502-
def _isDeltaMsg(self,m):
503-
if m['type'] in ['delta'] : return True
504-
return False
505-
def _parseMessage(self, content):
506-
'''
507-
Get message and author name from content.
508-
May contains multiple messages in the content.
509-
'''
510-
if 'ms' not in content: return
511-
for m in content['ms']:
512-
if m['type'] in ['m_messaging', 'messaging'] or self._isDeltaMsg(m):
513-
thread_id = ''
514-
mid = ''
515-
sender_fbid = ''
516-
try:
517-
message,mid,thread_id,sender_fbid = self._parseGroupMessage(m)
518-
except:
519-
print("_parseGroupMessage():")
520-
print(exc_info())
521-
print(m)
522-
pass
523-
524-
if not mid:
525-
try:
526-
message,mid,sender_fbid = self._parsePersonalMessage(m)
527-
except:
528-
print("_parsePersonalMessage():")
529-
print(exc_info())
530-
print(m)
531-
pass
532-
if not mid: # not message
533-
#print "not message"
534-
return
535-
536-
stickurl = ""
537-
if not message:
538-
try:
539-
stickurl = metadata['message']['attachments'][0]['url']
540-
541-
except:
542-
pass
543-
if mid == self.last_mid:
544-
pass
545-
else:
546-
self.last_mid = mid
547-
self.on_message(mid,message,sender_fbid,thread_id,self._parseTimeInMessage(m), stickurl)
548-
elif m['type'] in ['typ']:
549-
try:
550-
fbid = m["from"]
551-
self.on_typing(fbid)
552-
except:
553-
self._output_errlog('log.txt',str(m),str(exc_info()))
554-
elif m['type'] in ['m_read_receipt']:
555-
try:
556-
# no author include in json # author = m['author']
557-
reader = m['reader']
558-
msgtime = m['time']
559-
self.on_read(reader, self._parseTime(msgtime),m)
560-
except:
561-
self._output_errlog('log.txt',str(m),str(exc_info()))
562-
elif m['type'] in ['notification_json']:
563-
try:
564-
likemsg = m['nodes'][0]['unaggregatedTitle']['text']
565-
#likemsg = m['nodes'][0]['unaggregatedTitle']['text']
566-
self.on_notify(likemsg,m)
567-
except :
568-
self._output_errlog('like_err.log',str(m),str(exc_info()))
569-
self.on_notify(u'some notification appear, see like_err.log',m)
570-
else:
571-
self._output_errlog('log.txt',str(m),"")
572-
"""
573-
574468
def _parse_pass(self,m):
575469
pass
576470
def _parse_delta_ReadReceipt(self,m):
577471
'''actionTimestampMs,irisSeqId,threadKey[otherUserFbId],watermarkTimestampMs'''
578472
try:
579-
readerid = m['delta']['threadKey']['otherUserFbId']
473+
readerid = m['delta']['threadKey'].get('otherUserFbId') or m['delta'].get('actorFbId')
580474
msgtime = m['delta']['actionTimestampMs']
581475
self.on_read(readerid, self._parseTime(msgtime),m)
582476
except :
@@ -630,6 +524,7 @@ def _parse_typ(self,m):
630524
''' typing '''
631525
pass
632526
def _parseMessage(self,content):
527+
if not content: return
633528
''' action by type '''
634529
adic = dict()
635530
adic['delta'] = self._parse_delta
@@ -657,6 +552,7 @@ def _parseMessage(self,content):
657552
adic['share_reply'] = self._parse_pass
658553
adic['sticker'] = self._parse_pass
659554
adic['user'] = self._parse_pass
555+
adic['ttyp'] = self._parse_pass
660556

661557
if "refresh" in content.get("t"):
662558
print("need refreshed?")

fbchat/utils.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,13 @@ def generateMessageID(client_id=None):
4545
k = now()
4646
l = int(random() * 4294967295)
4747
return ("<%s:%s-%s@mail.projektitan.com>" % (k, l, client_id));
48+
49+
def getSignatureID():
50+
return hex(int(random() * 2147483648))
51+
52+
def generateOfflineThreadingID() :
53+
ret = now()
54+
value = int(random() * 4294967295);
55+
string = ("0000000000000000000000" + bin(value))[-22:]
56+
msgs = bin(ret) + string
57+
return str(int(msgs,2))

0 commit comments

Comments
 (0)