aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2020-03-01 13:08:32 +0100
committerAlex Auvolat <alex@adnab.me>2020-03-01 13:08:32 +0100
commit810e75a34dddd88279bea1cd2ea38816fe872d52 (patch)
tree780a18b5488dcdac708a4428776e588095442d61
parent1316a3031b829daa28eda205bd3cdf14b192a0b4 (diff)
downloadeasybridge-810e75a34dddd88279bea1cd2ea38816fe872d52.tar.gz
easybridge-810e75a34dddd88279bea1cd2ea38816fe872d52.zip
Very primitive ability to send fb messages
-rw-r--r--connector/connector.go2
-rw-r--r--connector/external/external.go7
-rwxr-xr-xexternal/messenger.py188
3 files changed, 139 insertions, 58 deletions
diff --git a/connector/connector.go b/connector/connector.go
index 210d4ad..48fc9e7 100644
--- a/connector/connector.go
+++ b/connector/connector.go
@@ -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"`
diff --git a/connector/external/external.go b/connector/external/external.go
index 26f3c40..741802c 100644
--- a/connector/external/external.go
+++ b/connector/external/external.go
@@ -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)
diff --git a/external/messenger.py b/external/messenger.py
index 8403a03..6ec6e9c 100755
--- a/external/messenger.py
+++ b/external/messenger.py
@@ -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
+ if thread.type == ThreadType.GROUP:
+ members = self.client.fetchAllUsersFromThreads([thread])
+
+ self.bridge.write({
+ "_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)
+
+ 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": JOINED,
- "room": thread.uid,
+ "_type": EVENT,
+ "data": {
+ "type": EVENT_JOIN,
+ "author": self.bridge.getUserId(member),
+ "room": thread.uid,
+ }
})
- room_info = {
- "name": thread.name,
+ user_info = {
+ "display_name": member.name,
}
- if thread.photo is not None:
- room_info["picture"] = mediaObjectOfURL(thread.photo)
+ if member.photo is not None:
+ user_info["avatar"] = mediaObjectOfURL(member.photo)
self.bridge.write({
- "_type": ROOM_INFO_UPDATED,
- "room": thread.uid,
- "data": room_info,
+ "_type": USER_INFO_UPDATED,
+ "user": self.bridge.getUserId(member),
+ "data": user_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,
- }
- })
+ def backlog_room(self, thread):
+ pass # TODO
- 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": getUserId(member),
- "data": user_info,
- })
-
- # 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()