Get user join / leave events retroactively from Channels - node.js

I'm trying to do some analytics on average response time from some of our users on Twilio Chat.
I'm iterating through my channels, and I'm able to pull the info about messages, so I can compare times a message went un-responded to. However, I can't determine which users were in the channel at that time.
Is there anything on the channel that would give me historic member data? Who was in the channel? The channel.messages().list() method is only giving me the text of the messages sent to the channel and who it was by, but the user who may have been in a channel to respond changes throughout a channel's life time.
This is on the backend using the node.js SDK. note: This isn't a complete implementation for what I'm trying to do, but taking it in steps to get access to the information I'd need to do this. Once I have these messages and know which users are supposed to be in a channel at a given time, I can do the analytics to see how long it took for the users I am looking for to respond.
var fs = require('fs');
const Twilio = require('twilio');
const client = new Twilio(env.TWILIO_ACCOUNT_SID, env.TWILIO_AUTH);
const service = client.chat.services(env.TWILIO_IPM_SERVICE_SID);
async function getChatMessages() {
const fileName = 'fileName.csv';
const getLine = message => {
return `${message.channelSid},${message.sid},${message.dateCreated},${message.from},${message.type},${message.body}\n`;
}
const writeToFile = message => { fs.appendFileSync(fileName, getLine(message)); };
const headerLine = `channelSid,messageSid,dateCreated,author,type,body`;
fs.writeFileSync(fileName, headerLine);
await service.channels.each(
async (channel, done) => {
i++;
let channelSid = channel.sid;
if( channel.messagesCount == 0 ) return;
try {
await channel.messages().list({limit:1000, order:"asc"}).then(
messages => {
messages.forEach( writeToFile );
}
);
} catch(e) {
console.log(`There was an error getting messages for ${channelSid}: ${e.toString()}`);
}
if( i >= max ) done();
}
);
}
I'm beginning to be resigned to the fact that maybe this would only have been possible to track had I set up the proper event listeners / webhooks to begin with. If that's the case, I'd like to know so I can get that set up. But, if there's any endpoint I can reach out to and see who was joining / leaving a channel, that would be ideal for now.

The answer is that unfortunately you can not get this data retroactively. Twilio offers a webhooks API for chat which you can use to track this data yourself as it happens, but if you don't capture the events, you do not get access to them again.

Related

Better approach to store FCM tokens array in a collection/doc and update it on token refresh

I am building an application where people can host events. It allows for users to follow the events and all the event followers can chat in a group. I am storing the FCM token for push notifications in a user collection in Firestore. But when I send the notification my current logic is not quite optimal and it takes several minutes for the notification to send to each user as I am first getting all user's tokens for each user's document and then combing those tokens in a list and send push notification to each token using a loop in my cloud function. I think it takes time for sending the messages and the group chat does not seem to be real-time because of that computation.
What I thought is to store the FCM tokens of each user inside every event he follows. So that when there is a chat message the list of tokens is fetched from the specific event and they are sent a multicast notification. But here again, I am not sure if it is a good approach because I will need to keep track of refreshed tokens and update the tokens in each document where it exists.
Let's say a user has joined 50 events and on app launch, his FCM token got refreshed now I will have to run a cloud function that will see if the token is renewed it should loop through all of those 50 events and update his token.
I want to know what can be the best approach for this use case.
Below is the code that I am using:
exports.sendNotification = functions.firestore
.document("event/{eventid}/{chat}/{chatid}")
.onCreate((snap, context) => {
processData(snap, context);
return null;
});
async function processData(snap, context) {
const doc = snap.data();
const eventID = context.params.eventid;
const senderID = doc.sender;
var senderName = doc.senderName;
await admin
.firestore()
.collection("event")
.doc(eventID)
.get()
.then(value => {
var joinedBy = value.data()["joinedBy"];
joinedBy.forEach(item => {
getTokens(uid, senderID, doc, eventName, senderName);
});
});
}
async function getTokens(uid, senderID, doc, eventName, senderName) {
await admin
.firestore()
.collection("people")
.doc(uid)
.get()
.then(value => {
var token = value.data()["pushToken"];
if (uid != senderID) {
if (token) {
const payload = {
notification: {
title: `${eventName} : ${senderName}`,
body: doc.text,
badge: "1",
sound: "default"
},
};
sendMessage(token, payload);
} else {
console.log("Can not find pushToken target user");
}
} else {
console.log("uid and sender id is same so notification not sent");
}
});
}
One thing to consider in your current approach is whether you really first need to read all tokens for the message and only then starting calling FCM. Might things get faster if you call FCM right away on each set of tokens you read, and thus perform some of the reads and FCM calls in parallel?
Another consideration is that you say that FCM's built-in topics work really fast for you. Internally FCM's topic subsystem associates a bulk of device IDs with the topic, and then reads the tokens for the topic as needed.
Is this something you can mimic for your app? For example, can you store the tokens in larger groups inside fewer documents, thus reducing the number of read operations you need to do.

Is there a way to obtain Discord message ID upon posting msg to channel from node server?

Using Discord.js in an Express/Node.js app, I'm trying to build a bot that grabs external data periodically and updates Discord with an embed msg containing some elements of that data. I'm trying to add a feature that will check if that data was deleted from the external source(no longer existing upon the next grab), then delete the specific msg in Discord that contains that data that was sent.
Some of the msgs posted in Discord may have duplicate data items, so I want to delete by specific msg ID, but it seems that msg ID is assigned when posted to Discord.
Is there a way to programmatically grab or return this msg ID when sending from Discord.js, rather than manually copy/pasting the msg ID from the Discord GUI? In other words, I need my bot to know which message to delete if it sees that msg's source data is no longer being grabbed.
// FOR-LOOP TO POST DATA TO DISCORD
// see if those IDs are found in persistent array
for (var i = 0; i < newIDs.length; i++) {
if (currentIDs.indexOf(newIDs[i]) == -1) {
currentIDs.push(newIDs[i]); // add to persistent array
TD.getTicket(33, newIDs[i]) // get ticket object
.then(ticket => { postDiscord(ticket); }) // post to DISCORD!
}
}
// if an ID in the persistent array is not in temp array,
// delete from persistent array & existing DISCORD msg.
// message.delete() - need message ID to get msg object...
// var msg = channel.fetchMessage(messageID) ?
Let me refer you to:
https://discord.js.org/#/docs/main/stable/class/Message
Assuming you are using async/await, you would have something like:
async () => {
let message = await channel.send(some message);
//now you can grab the ID from it like
console.log(message.id)
}
If you are going to use .then for promises, it is the same idea:
channel.send(some message)
.then(message => {
console.log(message.id)
});
ID is a property of messages, and you will only get the ID after you receive a response from the Discord API. This means you have to handle them asynchronously.

How to set up email notifications about new messages in Twilio Programmable chat?

What is a correct way to synchronize Twilio chat consumption horizon and send an email notification with a list of new messages from our own server?
I can use kind of pre-hooks / post-hooks for new messages, but I don't want to keep every message and its reading status in my database.
Is there a smarter way to set up notifications on my server?
Twilio developer evangelist here.
You wouldn't need to store all of this. You can just use the REST API to get all of this information.
What you need to do is store your user's Channel Member Sid. You can then make a call to the Member resource and get their last_consumed_message_index.
With the index, which is an integer representing index of the last message the member has read within the channel, you can then call on the Message resource to list all the messages since that index. In Node, that would be so
service
.channels(CHANNEL_SID)
.members(MEMBER_SID)
.fetch()
.then(member => {
const lastConsumedMessageIndex = member.lastConsumedMessageIndex;
return service.channels(CHANNEL_SID).messages.list({
pageSize: lastConsumedMessageIndex,
limit: lastConsumedMessageIndex
});
})
.then(messages => {
console.log(messages);
// do something with unread messages
})
.catch(error => {
console.error(error);
});
Let me know if that helps at all.

Twilio Functions - SMS masking

Hello I am quite new to Twilio, but I have tried to look up how to answer this question. I would like to use Twilio Functions to solve my problem. I was wondering if it is possible for two people to send SMS messages to each other without revealing either of their numbers.
I was hoping to do this with only one new number per pair.
I imagined it would be through a conditional statement, where person X sends a message to the twilio number and person Y receives it, and vice versa. I assume this cannot be done with the twiML bins because of this conditional statement.
Thanks for your attention.
Twilio developer evangelist here.
You could absolutely do this with Twilio Functions. Here's a simple example of using a number to mask SMS messages between two callers.
class NumberMapping {
constructor() {
this.mapping = {};
}
addMaskedPair(numberA, numberB, twilioNumber) {
if (!this.mapping[twilioNumber]) {
this.mapping[twilioNumber] = {};
}
this.mapping[twilioNumber][numberA] = numberB;
this.mapping[twilioNumber][numberB] = numberA;
}
findNumber(from, to) {
const numberPairs = this.mapping[to];
if (!numberPairs) { return undefined; }
return numberPairs[from];
}
}
const numberMapping = new NumberMapping();
numberMapping.addMaskedPair('+1234567890', '+1098765432', '+1203948576');
exports.handler = function(context, event, callback) {
const to = numberMapping.findNumber(event.From, event.To);
if (typeof to !== 'undefined') {
const response = new Twilio.twiml.MessagingResponse();
response.message({ from: event.To, to: to }, event.Body);
callback(null, response);
} else {
callback(new Error(`Number mapping couldn't be found for sender ${event.From} and Twilio number ${event.To}.`));
}
};
The idea is that you create a NumberMapping object that maps between the two external numbers and your Twilio number. You add your mappings using:
numberMapping.addMaskedPair(firstNumber, secondNumber, twilioNumber);
and then when you need to retrieve the other number in a pair you can call
numberMapping.findNumber(number, twilioNumber);
The rest is just the function to return TwiML.
Note, you will only need as many Twilio numbers as there are relationships of the number that has the maximum set of relationships.
Let me know if that helps at all.
You need to purchase a number from twilio, then use node JS code to send and receive sms with it. You can also send voice messages too. The thing with twilio is that when you receive messages, twilio saves it to its website so you have to go to website and check it explicitly with your account.
You can create account and receive messages with this link
Here is some tutorial on how to send messages, you have to choose node.JS option.

Messaging a user a bot does not know

I am using the Slack RTM node client and having a bit of an issue with DM's. Say a user joins the channel who has never DM'ed the bot before, the user types a command in the channel that the bot usually will respond to and by default the bot responds in a private message to the user. However, the bot cannot do this because the dataStore does not contain any DM data for this user. Code sample below...
rtm.on(RTM_EVENTS.MESSAGE, function (message) {
user = rtm.getUserById(message.user);
console.log(user); // It gets the user object fine
dm = rtm.getDMByName(user.name);
console.log(dm); // This is always undefined unless the user has DM'ed the bot previously
});
Is there a way around this? I can't seem to find anything in the docs or code to suggest there might be.
You can use the im.open method of the web API. Here's roughly how you'd do it with #slack/client (untested, apologies in advance!):
var webClient = new WebClient(token);
...
rtm.on(RTM_EVENTS.MESSAGE, function (message) {
var dm = rtm.getDMById(message.user);
if (dm) {
console.log(`Already open IM: ${dm}`);
// send a message or whatever you want to do here
} else {
webClient.im.open(message.user, function (err, result) {
var dm = result.channel.id;
console.log(`Newly opened IM: ${dm}`);
// send a message or whatever you want to do here
});
}
});

Resources