Mercury was to be an Emacs interface to Facebook Messenger, SMS (via Pushbullet), and Signal. Unfortunately, I lost momentum on the project, although I still think it would be extremely cool.
Communication between the Emacs Lisp frontend and the Python backend is done by message passing on stdio. Each message is a single line containing a JSON object.
General message protocol:
- map:
type
: string, message type identifierid
: string, identifier to match server responses to their original messagesdata
: map, contents dependent on message typeerror
: string, error message (or null if request successful)- notes:
- only used for response messages
- notes:
Request-response messages:
addAccount
:- message:
service
: string, must bemessenger
name
: string, human-readable name for the account
- response:
id
: string, account ID
- message:
removeAccount
:- message:
id
: string, account ID
- message:
getAccounts
:- response:
- map:
- keys: strings, account IDs
- values: maps
service
: string, alwaysmessenger
name
: string, human-readable name for the accountloginRequired
: booleanloginFields
: list- values: maps
field
: string, internal identifier used in responsename
: string, display nameprivate
: boolean, whether this field should be treated as a password
- values: maps
- map:
- response:
login
:- message:
aid
: string, account IDfields
: map- keys: strings, as in
field
key undergetAccounts
response - values: strings, as provided by the user
- keys: strings, as in
- message:
logout
:- message:
aid
: string, account ID
- message:
getConversations
:- message:
aid
: string, account IDlimit
: integer, how many conversations to retrieve (or null for default)offset
: integer, how many conversations to skip (or null or omitted for default, skipping no conversations)
- response:
conversations
: list, sorted bytimestamp
descending- values: maps
id
: string, conversation IDname
: string, service-provided display name for conversationtimestamp
: integer, most recent message timestamp (may be more recent than any message we have if we haven't fetched the most recent message yet)participants
: list- values: maps
id
: string, user IDname
: string, display nameyou
: boolean, whether you are this user
- values: maps
- values: maps
- message:
getMessages
:- message:
aid
: string, account IDcid
: string, conversation IDlimit
: integer, how many messages to retrieve (or null or omitted for default)offset
: integer, how many messages to skip (or null or omitted for default, skipping no messages)
- response:
messages
: list, sorted bytimestamp
- values: maps
id
: string, message IDtype
: string, eithertext
orimage
orfile
orunsupported
content
: string, message contents fortext
or URL forimage
andfile
or description forunsupported
timestamp
: integer, time at which message was sentsender
: mapid
: string, user IDname
: string, nameyou
: boolean, whether you are this user
- values: maps
participants
: list- values: maps
id
: string, user IDname
: string, display nameyou
: boolean, whether you are this userlastSeenMessage
: string, message ID, or null if the message has not been fetched yet
- values: maps
- message:
sendMessage
:- message:
aid
: string, account IDcid
: string, conversation IDtype
: string, eithertext
orimage
orfile
content
: string, message contents fortext
or filename forimage
andfile
- response: empty
- message:
Notification messages:
resyncNeeded
: emptyreceivedMessage
:id
: string, message IDtype
: string, eithertext
orimage
orfile
orunsupported
content
: string, message contents fortext
or URL forimage
andfile
or description forunsupported
timestamp
: integer, time at which message was sentsender
: mapid
: string, user IDname
: string, nameyou
: boolean, whether you are this user
receivedReadReceipt
:id
: string, message IDconversation
: mapid
: string, conversation IDname
: string, display name of conversation
user
: mapid
: string, user IDname
: string, display name of user
Any API method may throw LoginRequiredError
upon authentication
failure or invalid session, or ServiceError
upon bad data or
unexpected error from upstream API. Other errors should only be thrown
in case of programmer error and will result in the use of lethal force
by Mercury on the service client.
get_session()
:- returns: string, as used for
restore_session
(or null if no active session)
- returns: string, as used for
restore_session(session)
:session
: string, as returned byget_session
- returns: nothing
- notes:
- should validate session and throw
LoginRequiredError
if it's not valid
- should validate session and throw
get_login_fields()
:- returns: list
- values: maps
field
: string, internal identifier used in responsename
: string, display nameprivate
: boolean, whether this field should be treated as a password
- values: maps
- returns: list
login(fields)
:fields
: map- keys: strings, as returned by
get_login_fields
underfield
- values: strings, values filled in by the user
- keys: strings, as returned by
- preconditions:
logout()
has been called
logout()
:- returns: nothing
- notes:
- should silently do nothing if already logged out or there is no existing session
get_you()
:- returns: string, user ID of you
get_users(uids)
:uids
: list- values: strings, user IDs
- returns: map
- keys: strings, user IDs
- values: maps
name
: string, display name of user
get_conversations(before)
:before
: timestamp at which to start retrieving conversations (or null to retrieve most recently updated conversations)- returns: map
conversations
: list, sorted bytimestamp
descending- length is unspecified but should only be zero if there are no matching conversations
- values: maps
id
: string, conversation IDname
: string, conversation display nametimestamp
: integer, most recent message timestamp (may be more recent than any message we have if we haven't fetched the most recent message yet)participants
: map- keys: strings, user IDs
- values: maps
lastSeenMessage
: string, message ID (or null or omitted if there's no update to report)
messages
: list, sorted bytimestamp
descending, or omitted- values: maps
id
: string, message IDtype
: string, eithertext
orimage
orfile
orunsupported
content
: string, message contents fortext
or URL forimage
andfile
or description forunsupported
timestamp
: integer, time at which message was sent
- values: maps
users
: map or omitted- keys: strings, user IDs
- values: maps
name
: string, display name of user (or null or omitted if this information is not available)
get_messages(before)
:before
: timestamp at which to start retrieving messages (or null to retrieve most recent messages)- returns: map
messages
: list, sorted bytimestamp
descending- length is unspecified but should only be zero if there are no matching messages
- values: maps
id
: string, message IDtype
: string, eithertext
orimage
orfile
orunsupported
content
: string, message contents fortext
or URL forimage
andfile
or description forunsupported
timestamp
: integer, time at which message was sentsender
: string, user ID
participants
: map or omitted- keys: strings, user IDs
- notes:
- does not need to include all participants, only ones for which data should be updated
- notes:
- values: maps
lastSeenMessage
: string, message ID (or null or omitted if there's no update to report)
- keys: strings, user IDs
users
: map or omitted- keys: strings, user IDs
- values: maps
name
: string, display name of user (or null or omitted if this information is not available)
send_message(cid, type, content)
:cid
: string, conversation IDtype
: string, eithertext
orimage
orfile
content
: string, message contents fortext
or filename forimage
andfile
- returns: nothing
All timestamps are milliseconds since the epoch.
$MERCURY_SESSION_FILE
or$XDG_CONFIG_HOME/mercury/sessions.json
: mapsessions
: map- keys: strings, account IDs
- values: JSON objects
$MERCURY_DATA_FILE
or$XDG_CONFIG_HOME/mercury/data.json
: mapversion
: integer, schema versionaccounts
: map- keys: strings, account IDs
- values: maps
name
: string, account display nameusers
: map- keys: strings, user IDs
- values: maps
name
: user display name
conversations
: list, sorted bytimestamp
descending- values: maps
id
: string, conversation IDname
: string, conversation display nametimestamp
: integer, most recent message timestamp (may be more recent than any message we have if we haven't fetched the most recent message yet)participants
: map- keys: strings, user IDs
- values: maps
lastSeenMessage
: string, message ID (or null if we haven't fetched the message yet)
messages
: list, sorted bytimestamp
descending- values: maps
id
: string, message IDtype
: string, eithertext
orimage
orfile
orunsupported
content
: string, message contents fortext
or URL forimage
andfile
or description forunsupported
timestamp
: integer, time at which message was sentsender
: string, user ID
- values: maps
- values: maps