Skip to content
This repository has been archived by the owner on Jul 9, 2022. It is now read-only.

Implementing addReaction function #427

Merged
merged 25 commits into from
Mar 30, 2017
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
c6957bf
Implementing addReaction function
gamelaster Mar 26, 2017
a34c12b
Patching tipos
gamelaster Mar 26, 2017
325f16f
Adding static doc_id
gamelaster Mar 26, 2017
6161ed8
Merge remote-tracking branch 'upstream/master' into feature-reactions
gamelaster Mar 27, 2017
99655f7
Renaming addReaction to setMessageReaction
gamelaster Mar 29, 2017
23c26e6
Adding reactions variable into message template
gamelaster Mar 29, 2017
1ec7ad1
Relative incrementing client_mutation_id
gamelaster Mar 29, 2017
915993f
Adding REMOVE_REACTION support
gamelaster Mar 29, 2017
4a8842a
Parsing reactions via Client Payload
gamelaster Mar 29, 2017
2211491
Only supported reaction emojis and conversion from text
gamelaster Mar 29, 2017
bdb9284
Adding threadId into message reaction template
gamelaster Mar 29, 2017
9c313ab
Removing unused argument on setMessageReaction
gamelaster Mar 29, 2017
8d459e5
Adding documentation for setMessageReaction
gamelaster Mar 29, 2017
7dd583d
Adding setMessageReaction on README.MD
gamelaster Mar 29, 2017
d476290
Removing old addReaction file and move clientMutationId to ctx
gamelaster Mar 29, 2017
6248686
Merge branch 'feature-reactions' of https://github.com/GAMELASTER/fac…
gamelaster Mar 29, 2017
b74b5e3
Adding reactions into delta and into documentation
gamelaster Mar 29, 2017
7f458e1
Reverting reaction implement into delta messages
gamelaster Mar 29, 2017
d8365fc
Adding forgotten break
gamelaster Mar 30, 2017
158e2ed
New reaction object + threadId fix
gamelaster Mar 30, 2017
a2b54ee
Fixed description of messageId
gamelaster Mar 30, 2017
c933951
Adding other forgotten breaks
gamelaster Mar 30, 2017
c55efec
Adding back compatilibity for threadKey.threadFbId
gamelaster Mar 30, 2017
4b2dc82
Adding Facebook emoji shortcuts
gamelaster Mar 30, 2017
a2eb5a5
Removing :angery: shortcut
gamelaster Mar 30, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
34 changes: 34 additions & 0 deletions DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
* [`api.sendMessage`](#sendMessage)
* [`api.sendTypingIndicator`](#sendTypingIndicator)
* [`api.setOptions`](#setOptions)
* [`api.setMessageReaction`](#setMessageReaction)
* [`api.setTitle`](#setTitle)

---------------------------------------
Expand Down Expand Up @@ -607,6 +608,7 @@ If `type` is `message`, the object will contain the following fields:
+ `messageID`: A string representing the message ID.
+ `attachments`: An array of attachments to the message.
+ `isGroup`: boolean, true if this thread is a group thread (more than 2 participants).
+ `reactions`: An array of reactions with key as userID and value as emoji

If `attachments` contains an object with type `"sticker"`, the object will contain the following fields: `url`, `stickerID`, `packID`, `frameCount`, `frameRate`, `framesPerRow`, `framesPerCol`, `spriteURI`, `spriteURI2x`, `height`, `width`, `caption`, `description`.

Expand Down Expand Up @@ -651,6 +653,14 @@ Difference between `"read_receipt"` and `"read"`:
- `"read_receipt"` event triggers when other people read the user's messages.
- `"read"` event triggers when the user read other people's messages.

If `type` is `"message_reaction"`, then the object will have following fields (enabled `listenEvents` required):
- `"reaction"`: Contains reaction emoji
- `"userId"`: The reaction senders ID
- `"senderId"`: ID of author the message, where has been reaction added
- `"messageId"`: The ID of message
- `"threadId"`: ID of thread where has been message sent
- `"offlineThreadingId"`: The offline message ID

<a name="presence"></a>
If enabled through [setOptions](#setOptions), `message` could also be a presence object, (`type` will be `"presence"`), which is the online status of the user's friends. That object given to the callback will have the following fields:
- `type`: The string `"presence"`.
Expand Down Expand Up @@ -925,6 +935,30 @@ login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, ap
});
```

---------------------------------------

<a name="setMessageReaction"></a>
### api.setMessageReaction(reaction, messageID[, callback])

Sets reaction on message

__Arguments__

* `reaction`: A string contains `emoji`, `emoji shortcut`, `emoji in unicode` or left `empty string` for delete reaction (look down for list of supported emojis)
* `messageID`: A string or number representing a thread. It happens to be someone's userId in the case of a one to one conversation.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A string or number representing a thread
so... it is threadID or messageID?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops! It's a messageID !

* `callback(err)` - A callback called when sending the reaction is done.

__Supported Emojis__

* 😍 - Unicode: `\uD83D\uDE0D`, Shortcut: `:heart_eyes:`
* 😆 - Unicode: `\uD83D\uDE06`, Shortcut: `:laughing:`
* 😮 - Unicode: `\uD83D\uDE2E`, Shortcut: `:open_mouth:`
* 😢 - Unicode: `\uD83D\uDE22`, Shortcut: `:cry:`
* 😠 - Unicode: `\uD83D\uDE20`, Shortcut: `:angry:`
* 👍 - Unicode: `\uD83D\uDC4D`, Shortcut: `:thumbsup:`
* 👎 - Unicode: `\uD83D\uDC4E`, Shortcut: `:thumbsdown:`
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you check if any other emojis are supported? ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, but @mangotec tried it and don't found any other

Copy link
Owner

@Schmavery Schmavery Mar 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok cool 😄 (too bad, would have been fun)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion, it would be better if reaction names shortcuts were unified with the names of the reactions for Facebook posts, i.e.:

  • :love:
  • :haha:
  • :wow:
  • :sad:
  • :angery:
  • :like:
  • :dislike:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ivkos hmm, probably you are right, I just used an "unified" emoji shortcuts. Let's left decide to @Schmavery

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't mind the unified ones, we could potentially support both. @ivkos do you have a source for these "canonical" facebook reaction names?

Copy link
Contributor

@ivkos ivkos Mar 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Schmavery They are not really canonical. It's what they're called in the Facebook UI and how most users know them. Reactions in Messenger don't have visible names in the UI, but most of my friends call them basically with the names of the corresponding reactions in Facebook.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but facebook's :love: is heart, not eyes with hearts... :/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okey, I added support for Facebook shortcuts 😊



---------------------------------------

<a name="setTitle"></a>
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ Result:
* [`api.sendMessage`](DOCS.md#sendMessage)
* [`api.sendTypingIndicator`](DOCS.md#sendTypingIndicator)
* [`api.setOptions`](DOCS.md#setOptions)
* [`api.setMessageReaction`](DOCS.md#setMessageReaction)
* [`api.setTitle`](DOCS.md#setTitle)

## Main Functionality
Expand Down
4 changes: 3 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ function buildAPI(globalOptions, html, jar) {
clientID: clientID,
globalOptions: globalOptions,
loggedIn: true,
access_token: 'NONE'
access_token: 'NONE',
clientMutationId: 0
};

var api = {
Expand Down Expand Up @@ -100,6 +101,7 @@ function buildAPI(globalOptions, html, jar) {
'searchForThread',
'sendMessage',
'sendTypingIndicator',
'setMessageReaction',
'setTitle',
];

Expand Down
17 changes: 17 additions & 0 deletions src/listen.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,23 @@ module.exports = function(defaultFuncs, api, ctx) {
})(0)
break;
}

if (v.delta.class == "ClientPayload") {
var clientPayload = utils.decodeClientPayload(v.delta.payload);
if (clientPayload && clientPayload.deltas) {
for (var i in clientPayload.deltas) {
var delta = clientPayload.deltas[i];
if (delta.deltaMessageReaction) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, just one more thing. Can you explicitly create and return a new object? This will help if facebook changes the format of the object they send us, I believe.
Why do you delete delta.threadKey and delta.action?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think threadKey and action is useless.
And I did that new object.

delta.deltaMessageReaction.type = "message_reaction";
delta.threadId = delta.threadKey.threadFbId;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i used this code:

var fs = require("fs");
var login = require("facebook-chat-api");

login({appState: JSON.parse(fs.readFileSync('appstate.json', 'utf8'))}, (err, api) => {
	if(err) return console.error(err);

	api.setOptions({listenEvents: true, logLevel: "silly"});

	var stopListening = api.listen((err, event) => {
		if(err) return console.error(err);

		if (event.type === "message") {
			console.log(JSON.stringify(event));
			//api.addReaction("👍", event.threadID, event.messageID);
			api.setMessageReaction("\uD83D\uDE0D", event.messageID, (err) => {
				console.error(err);
			})
		}
	});
});

and I got this error:

verb parseAndCheckLogin for (;;); {"t":"msg","seq":12,"u":100015305950325,"ms":[{"ofd_ts":1490827124159,"delta":{"payload":[123,34,100,101,108,116,97,115,34,58,91,123,34,100,101,108,116,97,77,101,115,115,97,103,101,82,101,97,99,116,105,111,110,34,58,123,34,116,104,114,101,97,100,75,101,121,34,58,123,34,111,116,104,101,114,85,115,101,114,70,98,73,100,34,58,49,48,48,48,48,49,51,51,49,51,56,53,53,55,48,125,44,34,109,101,115,115,97,103,101,73,100,34,58,34,109,105,100,46,36,99,65,65,65,65,66,102,56,100,69,74,100,104,84,88,51,100,89,70,98,72,68,105,87,101,117,88,101,80,34,44,34,97,99,116,105,111,110,34,58,48,44,34,117,115,101,114,73,100,34,58,49,48,48,48,49,53,51,48,53,57,53,48,51,50,53,44,34,114,101,97,99,116,105,111,110,34,58,34,92,117,48,48,102,48,92,117,48,48,57,102,92,117,48,48,57,56,92,117,48,48,56,100,34,44,34,115,101,110,100,101,114,73,100,34,58,49,48,48,48,48,49,51,51,49,51,56,53,53,55,48,44,34,111,102,102,108,105,110,101,84,104,114,101,97,100,105,110,103,73,100,34,58,34,54,50,53,50,57,56,50,49,54,50,51,54,54,55,53,54,55,53,49,34,125,125,93,125],"class":"ClientPayload"},"type":"delta","iseq":283814,"queue":100015305950325}]}
info listen Got answer in 282
ERR! listen TypeError: Cannot read property 'threadFbId' of undefined
ERR! listen     at parsePackets (/var/www/node/react/node_modules/facebook-chat-api/src/listen.js:163:55)
ERR! listen     at Array.forEach (native)
ERR! listen     at /var/www/node/react/node_modules/facebook-chat-api/src/listen.js:107:12
ERR! listen     at tryCatcher (/var/www/node/react/node_modules/facebook-chat-api/node_modules/bluebird/js/main/util.js:26:23)
ERR! listen     at Promise._settlePromiseFromHandler (/var/www/node/react/node_modules/facebook-chat-api/node_modules/bluebird/js/main/promise.js:510:31)
ERR! listen     at Promise._settlePromiseAt (/var/www/node/react/node_modules/facebook-chat-api/node_modules/bluebird/js/main/promise.js:584:18)
ERR! listen     at Promise._settlePromises (/var/www/node/react/node_modules/facebook-chat-api/node_modules/bluebird/js/main/promise.js:700:14)
ERR! listen     at Async._drainQueue (/var/www/node/react/node_modules/facebook-chat-api/node_modules/bluebird/js/main/async.js:123:16)
ERR! listen     at Async._drainQueues (/var/www/node/react/node_modules/facebook-chat-api/node_modules/bluebird/js/main/async.js:133:10)
ERR! listen     at Immediate.Async.drainQueues [as _onImmediate] (/var/www/node/react/node_modules/facebook-chat-api/node_modules/bluebird/js/main/async.js:15:14)
ERR! listen     at processImmediate [as _immediateCallback] (timers.js:383:17)
ERR! listen  [TypeError: Cannot read property 'threadFbId' of undefined]
[TypeError: Cannot read property 'threadFbId' of undefined]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strange, after decoding your clientPayload, I got this response:
image
It's different than mine.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, In my old parsed files, It's contains threadFbId, but today new parsed I have otherUserFbId too ! Maybe hotfix or something from Facebook side?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I follow 100% but if it seems like a value is sometimes in one place and sometimes in another, please support both 😄
Sometimes people have different versions and it would be great to have the api work for everyone.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

delete delta.threadKey;
delete delta.action;
globalCallback(null, delta.deltaMessageReaction);
}
}
return;
}
}

switch (v.delta.class) {
case 'ReadReceipt':
Expand Down
75 changes: 75 additions & 0 deletions src/setMessageReaction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"use strict";

var utils = require("../utils");
var log = require("npmlog");

module.exports = function(defaultFuncs, api, ctx) {
return function setMessageReaction(reaction, messageID, callback) {
if(!callback) {
callback = function() {};
}

switch (reaction) {
case "\uD83D\uDE0D": //:heart_eyes:
case "\uD83D\uDE06": //:laughing:
case "\uD83D\uDE2E": //:open_mouth:
case "\uD83D\uDE22": //:cry:
case "\uD83D\uDE20": //:angry:
case "\uD83D\uDC4D": //:thumbsup:
case "\uD83D\uDC4E": //:thumbsdown:
case "":
//valid
break;
case ":heart_eyes:":
reaction = "\uD83D\uDE0D";
case ":laughing:":
reaction = "\uD83D\uDE06";
case ":open_mouth:":
reaction = "\uD83D\uDE2E";
case ":cry:":
reaction = "\uD83D\uDE22";
case ":angry:":
reaction = "\uD83D\uDE20";
case ":thumbsup:":
reaction = "\uD83D\uDC4D";
case ":thumbsdown:":
reaction = "\uD83D\uDC4E";
default:
return callback({error: "reaction is not valid emoji"});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you forgot to add breaks to cases, now it always calls callback with error.
and maybe "Reaction is not a valid emoji." instead of "reaction is not valid emoji"?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch, thanks @ravkr

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, thanks @ravkr !

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you've added one break, but it needs a break after every reaction = ...;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yea, sorry, when I arrive home I will fix that 🤣

break;
}

var variables = {
data: {
client_mutation_id: ctx.clientMutationId++,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will give NaN if ctx.clientMutationId is not initialized to a number.
You can fix that here: https://github.com/Schmavery/facebook-chat-api/blob/master/index.js#L58

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh ok, sorry I must have missed that.

actor_id: ctx.userID,
action: reaction == "" ? "REMOVE_REACTION" : "ADD_REACTION",
message_id: messageID,
reaction: reaction
}
};

var qs = {
doc_id: "1491398900900362",
variables: JSON.stringify(variables),
dpr: 1
};

defaultFuncs
.postFormData("https://www.messenger.com/webgraphql/mutation/", ctx.jar, {}, qs)
.then(utils.parseAndCheckLogin(ctx.jar, defaultFuncs))
.then(function(resData) {
if (!resData) {
throw {error: "addReaction returned empty object."};
}
if(resData.error) {
throw resData;
}
callback(null);
})
.catch(function(err) {
log.error("addReaction", err);
return callback(err);
});
};
};
14 changes: 12 additions & 2 deletions utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,8 @@ function formatDeltaMessage(m){
messageID: md.messageId,
attachments: (m.delta.attachments || []).map(v => _formatAttachment(v)),
timestamp: md.timestamp,
isGroup: !!md.threadKey.threadFbId
isGroup: !!md.threadKey.threadFbId,
reactions: originalMessage.reactions ? originalMessage.reactions : []
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I going crazy or is originalMessage not defined here? Can you make sure to test this change? deltaMessage can have slightly different behaviour than the other one (unless you're not getting delta messages?).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, when I thinking about this, the reactions in deltaMessage is not required because all reactions sync going through ClientPayload

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I don't understand. Can you explain more? Does delta message not have info about reactions?
Mhh... because it sends the delta message right away, so no one could have reacted to it yet?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I know, Delta message doesn't have info about reactions. But, the information about reaction is still in /pull/ request, but marked as ClientPayload. So first you get DeltaMessage and then ClientPayload contains Message Reaction

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I think that can work.

}
}

Expand All @@ -397,7 +398,8 @@ function formatMessage(m) {
timestampAbsolute: originalMessage.timestamp_absolute,
timestampRelative: originalMessage.timestamp_relative,
timestampDatetime: originalMessage.timestamp_datetime,
tags: originalMessage.tags
tags: originalMessage.tags,
reactions: originalMessage.reactions ? originalMessage.reactions : []
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add this to the docs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I searched, the message format is not documented in DOCS

Copy link
Owner

@Schmavery Schmavery Mar 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be here:

image

I just realized this actually corresponds to formatDeltaMessage, which I now notice you didn't add reactions to. Is it possible to add it there?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added 😊

};

if(m.type === "pages_messaging") obj.pageID = m.realtime_viewer_fbid.toString();
Expand Down Expand Up @@ -718,6 +720,13 @@ function formatPresence(presence, userID) {
};
}

function decodeClientPayload(payload) {
/*
Special function which Client using to "encode" clients JSON payload
*/
return JSON.parse(String.fromCharCode.apply(null, payload));
}

function getAppState(jar){
return jar
.getCookies("https://www.facebook.com")
Expand Down Expand Up @@ -756,5 +765,6 @@ module.exports = {
generatePresence: generatePresence,
generateAccessiblityCookie: generateAccessiblityCookie,
formatDate: formatDate,
decodeClientPayload: decodeClientPayload,
getAppState: getAppState,
};