Very primitive ability to send fb messages
This commit is contained in:
parent
1316a3031b
commit
810e75a34d
3 changed files with 141 additions and 60 deletions
|
@ -130,7 +130,7 @@ type Event struct {
|
|||
Room RoomID `json:"room"`
|
||||
|
||||
// Message text or action text
|
||||
Text string `json:"text`
|
||||
Text string `json:"text"`
|
||||
|
||||
// Attached files such as images
|
||||
Attachments []SMediaObject `json:"attachments"`
|
||||
|
|
7
connector/external/external.go
vendored
7
connector/external/external.go
vendored
|
@ -162,7 +162,7 @@ func (ext *External) setupProc() error {
|
|||
}
|
||||
|
||||
func (ext *External) restartLoop(generation int) {
|
||||
for {
|
||||
for i := 0; i < 2; i++ {
|
||||
if ext.proc == nil {
|
||||
break
|
||||
}
|
||||
|
@ -178,6 +178,7 @@ func (ext *External) restartLoop(generation int) {
|
|||
break
|
||||
}
|
||||
}
|
||||
log.Warnf("More than 3 attempts (%s); abandonning.", ext.command)
|
||||
}
|
||||
|
||||
func (m *extMessageWithData) UnmarshalJSON(jj []byte) error {
|
||||
|
@ -294,7 +295,7 @@ func (ext *External) cmd(msg extMessage, data interface{}) (*extMessageWithData,
|
|||
} else {
|
||||
return rep, nil
|
||||
}
|
||||
case <-time.After(5 * time.Second):
|
||||
case <-time.After(30 * time.Second):
|
||||
return nil, fmt.Errorf("(%s) timeout", msg.MsgType)
|
||||
}
|
||||
}
|
||||
|
@ -379,7 +380,7 @@ func (ext *External) Join(room RoomID) error {
|
|||
|
||||
func (ext *External) Invite(user UserID, room RoomID) error {
|
||||
_, err := ext.cmd(extMessage{
|
||||
MsgType: LEAVE,
|
||||
MsgType: INVITE,
|
||||
User: user,
|
||||
Room: room,
|
||||
}, nil)
|
||||
|
|
192
external/messenger.py
vendored
192
external/messenger.py
vendored
|
@ -4,6 +4,8 @@ import sys
|
|||
import json
|
||||
import signal
|
||||
import threading
|
||||
import queue
|
||||
import pickle
|
||||
|
||||
import hashlib
|
||||
|
||||
|
@ -47,11 +49,6 @@ EVENT_ACTION = "action"
|
|||
|
||||
# ---- MESSENGER CLIENT CLASS THAT HANDLES EVENTS ----
|
||||
|
||||
def getUserId(user):
|
||||
if user.url is not None and not "?" in user.url:
|
||||
return user.url.split("/")[-1]
|
||||
else:
|
||||
return user.uid
|
||||
|
||||
def mediaObjectOfURL(url):
|
||||
return {
|
||||
|
@ -60,11 +57,12 @@ def mediaObjectOfURL(url):
|
|||
}
|
||||
|
||||
|
||||
class MessengerBridgeClient(fbchat.Client):
|
||||
def __init__(self, bridge, *args, **kwargs):
|
||||
self.bridge = bridge
|
||||
# class MessengerBridgeClient(fbchat.Client):
|
||||
# def __init__(self, bridge, *args, **kwargs):
|
||||
# super(MessengerBridgeClient, self).__init__(*args, **kwargs)
|
||||
#
|
||||
# # TODO: handle events
|
||||
|
||||
super(MessengerBridgeClient, self).__init__(*args, **kwargs)
|
||||
|
||||
class InitialSyncThread(threading.Thread):
|
||||
def __init__(self, client, bridge, *args, **kwargs):
|
||||
|
@ -78,59 +76,107 @@ class InitialSyncThread(threading.Thread):
|
|||
sys.stderr.write("fb thread list: {}\n".format(threads))
|
||||
for thread in threads:
|
||||
sys.stderr.write("fb thread: {}\n".format(thread))
|
||||
if thread.type != ThreadType.GROUP:
|
||||
continue
|
||||
self.bridge.write({
|
||||
"_type": JOINED,
|
||||
"room": thread.uid,
|
||||
})
|
||||
if thread.type == ThreadType.GROUP:
|
||||
members = self.client.fetchAllUsersFromThreads([thread])
|
||||
|
||||
room_info = {
|
||||
"name": thread.name,
|
||||
}
|
||||
if thread.photo is not None:
|
||||
room_info["picture"] = mediaObjectOfURL(thread.photo)
|
||||
self.bridge.write({
|
||||
"_type": ROOM_INFO_UPDATED,
|
||||
"room": thread.uid,
|
||||
"data": room_info,
|
||||
})
|
||||
|
||||
members = self.client.fetchAllUsersFromThreads([thread])
|
||||
for member in members:
|
||||
sys.stderr.write("fb thread member: {}\n".format(member))
|
||||
self.bridge.write({
|
||||
"_type": EVENT,
|
||||
"data": {
|
||||
"type": EVENT_JOIN,
|
||||
"author": getUserId(member),
|
||||
"room": thread.uid,
|
||||
}
|
||||
"_type": JOINED,
|
||||
"room": thread.uid,
|
||||
})
|
||||
|
||||
self.send_room_info(thread, members)
|
||||
self.send_room_members(thread, members)
|
||||
elif thread.type == ThreadType.USER:
|
||||
self.bridge.getUserId(thread)
|
||||
|
||||
user_info = {
|
||||
"display_name": member.name,
|
||||
self.backlog_room(thread)
|
||||
|
||||
|
||||
def send_room_info(self, thread, members):
|
||||
room_info = {}
|
||||
if thread.name is not None:
|
||||
room_info["name"] = thread.name
|
||||
else:
|
||||
who = [m for m in members if m.uid != self.client.uid]
|
||||
if len(who) > 3:
|
||||
room_info["name"] = ", ".join([self.bridge.getUserShortName(m) for m in who[:3]] + ["..."])
|
||||
else:
|
||||
room_info["name"] = ", ".join([self.bridge.getUserShortName(m) for m in who])
|
||||
|
||||
if thread.photo is not None:
|
||||
room_info["picture"] = mediaObjectOfURL(thread.photo)
|
||||
else:
|
||||
for m in members:
|
||||
if m.uid != self.client.uid and m.photo is not None:
|
||||
room_info["picture"] = mediaObjectOfURL(m.photo)
|
||||
break
|
||||
|
||||
self.bridge.write({
|
||||
"_type": ROOM_INFO_UPDATED,
|
||||
"room": thread.uid,
|
||||
"data": room_info,
|
||||
})
|
||||
|
||||
def send_room_members(self, thread, members):
|
||||
for member in members:
|
||||
sys.stderr.write("fb thread member: {}\n".format(member))
|
||||
self.bridge.write({
|
||||
"_type": EVENT,
|
||||
"data": {
|
||||
"type": EVENT_JOIN,
|
||||
"author": self.bridge.getUserId(member),
|
||||
"room": thread.uid,
|
||||
}
|
||||
if member.photo is not None:
|
||||
user_info["avatar"] = mediaObjectOfURL(member.photo)
|
||||
self.bridge.write({
|
||||
"_type": USER_INFO_UPDATED,
|
||||
"user": getUserId(member),
|
||||
"data": user_info,
|
||||
})
|
||||
})
|
||||
|
||||
user_info = {
|
||||
"display_name": member.name,
|
||||
}
|
||||
if member.photo is not None:
|
||||
user_info["avatar"] = mediaObjectOfURL(member.photo)
|
||||
self.bridge.write({
|
||||
"_type": USER_INFO_UPDATED,
|
||||
"user": self.bridge.getUserId(member),
|
||||
"data": user_info,
|
||||
})
|
||||
|
||||
def backlog_room(self, thread):
|
||||
pass # TODO
|
||||
|
||||
# TODO: handle events
|
||||
|
||||
|
||||
# ---- MAIN LOOP THAT HANDLES REQUESTS FROM BRIDGE ----
|
||||
|
||||
class MessengerBridge:
|
||||
def __init__(self):
|
||||
pass
|
||||
self.rev_uid = {}
|
||||
|
||||
def getUserId(self, user):
|
||||
if user.url is not None and not "?" in user.url:
|
||||
user_id = user.url.split("/")[-1]
|
||||
self.rev_uid[user_id] = user.uid
|
||||
return user_id
|
||||
else:
|
||||
return user.uid
|
||||
|
||||
def revUserId(self, user_id):
|
||||
if user_id in self.rev_uid:
|
||||
return self.rev_uid[user_id]
|
||||
else:
|
||||
return user_id
|
||||
|
||||
|
||||
def getUserShortName(self, user):
|
||||
if user.first_name != None:
|
||||
return user.first_name
|
||||
else:
|
||||
return user.name
|
||||
|
||||
def run(self):
|
||||
self.client = None
|
||||
self.keep_running = True
|
||||
self.cache_gets = {}
|
||||
self.num = 0
|
||||
|
||||
while self.keep_running:
|
||||
line = sys.stdin.readline()
|
||||
|
@ -161,23 +207,24 @@ class MessengerBridge:
|
|||
def handle_cmd(self, cmd):
|
||||
ty = cmd["_type"]
|
||||
if ty == CONFIGURE:
|
||||
cookies_file = "/tmp/cookies_" + hashlib.sha224(cmd["data"]["email"].encode("utf-8")).hexdigest()
|
||||
client_file = "/tmp/fbclient_" + hashlib.sha224(cmd["data"]["email"].encode("utf-8")).hexdigest()
|
||||
|
||||
try:
|
||||
f = open(cookies_file, "r")
|
||||
cookies = json.load(f)
|
||||
f = open(client_file, "rb")
|
||||
self.client = pickle.load(f)
|
||||
f.close()
|
||||
sys.stderr.write("(python messenger) using previous cookies: {}\n".format(cookies))
|
||||
sys.stderr.write("(python messenger) using previous client: {}\n".format(client_file))
|
||||
except:
|
||||
cookies = None
|
||||
self.client = None
|
||||
|
||||
self.client = MessengerBridgeClient(self, cmd["data"]["email"], cmd["data"]["password"], session_cookies=cookies)
|
||||
if self.client is None:
|
||||
email, password = cmd["data"]["email"], cmd["data"]["password"]
|
||||
self.client = fbchat.Client(email=email, password=password, max_tries=1)
|
||||
|
||||
if self.client.isLoggedIn():
|
||||
cookies = self.client.getSession()
|
||||
try:
|
||||
f = open(cookies_file, "w")
|
||||
json.dump(cookies, f)
|
||||
f = open(client_file, "wb")
|
||||
pickle.dump(self.client, f)
|
||||
f.close()
|
||||
except:
|
||||
pass
|
||||
|
@ -185,15 +232,48 @@ class MessengerBridge:
|
|||
InitialSyncThread(self.client, self).start()
|
||||
|
||||
elif ty == CLOSE:
|
||||
self.client.logout()
|
||||
self.keep_running = False
|
||||
|
||||
elif ty == GET_USER:
|
||||
return {"_type": REP_OK, "user": self.client.uid}
|
||||
|
||||
elif ty == INVITE and cmd["room"] == "":
|
||||
return {"_type": REP_OK}
|
||||
|
||||
elif ty == SEND:
|
||||
event = cmd["data"]
|
||||
if event["type"] in [EVENT_MESSAGE, EVENT_ACTION]:
|
||||
# TODO: attachments
|
||||
msg = Message(event["text"])
|
||||
if event["type"] == EVENT_ACTION:
|
||||
msg.text = "* " + event["text"]
|
||||
|
||||
if event["room"] != "":
|
||||
msg_id = self.client.send(msg, thread_id=event["room"], thread_type=ThreadType.GROUP)
|
||||
elif event["recipient"] != "":
|
||||
uid = self.revUserId(event["recipient"])
|
||||
msg_id = self.client.send(msg, thread_id=uid, thread_type=ThreadType.USER)
|
||||
else:
|
||||
return {"_type": REP_ERROR, "error": "Invalid message"}
|
||||
|
||||
return {"_type": REP_OK, "event_id": msg_id}
|
||||
|
||||
elif ty == REP_OK and cmd["_id"] in self.cache_gets:
|
||||
self.cache_gets[cmd["_id"]].put(cmd["value"])
|
||||
|
||||
else:
|
||||
return {"_type": REP_ERROR, "error": "Not implemented"}
|
||||
|
||||
def cache_get(self, key):
|
||||
self.num += 1
|
||||
num = self.num
|
||||
q = queue.Queue(1)
|
||||
self.cache_gets[num] = q
|
||||
self.write({"_type": CACHE_GET, "_id": num, "key": key})
|
||||
rep = q.get(block=True, timeout=30)
|
||||
del self.cache_gets[num]
|
||||
return rep
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
bridge = MessengerBridge()
|
||||
|
|
Loading…
Reference in a new issue