Updating an activity in BotFramework v4 on Teams platform - javascript

I have a bot developed using the Bot Framework v4 using NodeJS and deployed on multiple channels in Teams. Is there a way we can update a message sent by the bot? I tried implementing the updateActivity() function in the BotFrameworkAdapter. However, it does not update the activity.
I have this card sent from the bot to a Teams channel. When someone clicks on the button, is there a way I can update the card or the message (disabling the button)?

The key to this is making sure that when you use updateActivity(), you use the right activity ID that is created by the Teams Channel. You also need to make sure that the updated activity gets all of the Teams data set to it.
In onTurn, capture outgoing activities so that you can easily save all of the necessary Teams Channel data:
public onTurn = async (turnContext: TurnContext) => {
turnContext.onSendActivities(async (ctx, activities, nextSend) => {
activities.forEach(async (activity) => {
if (activity.channelData.saveMe) {
this.savedActivity = activity;
}
});
return await nextSend();
});
Note: There might be another way to do this. I just found this to be the easiest, since you need to save all of the channelData, conversation info, and activity.id, at a minimum
How you store that activity to be used later is up to you. If you store it in the constructor, it will either be re-instantiated on every message (C# SDK) or any user has the ability to change it (JS SDK). You might consider writing custom storage.
Activities keep all channelData. By specifying a saveMe flag, we ensure we save the right activity
Instantiate some key variables:
const teamsChannel = '19:8d60061c3d10xxxxxxxxxxxxxxxx#thread.skype';
const serviceUrl = 'https://smba.trafficmanager.net/amer/';
Note: the easiest way to get these variables is to send a message from Teams to the bot while putting a breakpoint on the incoming activity
serviceUrl likely varies by geo region
Send the first activity and store the ID:
// This ensures that your bot can send to Teams
turnContext.activity.conversation.id = teamsChannel;
turnContext.activity.serviceUrl = serviceUrl;
MicrosoftAppCredentials.trustServiceUrl(serviceUrl);
// Add the saveMe flag
yourActivity.channelData = { saveMe: true };
const response = await turnContext.sendActivity(yourActivity);
this.activityToUpdateId = response.id;
How you store that ID to be used later is up to you. If you store it in the constructor, it will either be re-instantiated on every message (C# SDK) or any user has the ability to change it (JS SDK). You might consider writing custom storage.
Update your saved activity:
// New data
const card2 = CardFactory.adaptiveCard(adaptiveCard2);
// Set the saved activity.id and new activity data (an adaptiveCard, in this example)
this.savedActivity.id = this.activityToUpdateId;
this.savedActivity.attachments = [card2];
Send the update:
await turnContext.updateActivity(this.savedActivity);
Note: you can update the activity with anything. I swapped out entirely different Adaptive Cards
Before:
After:

I've tried this using the middleware but keep getting: "The bot is not part of the conversation roster". Question: My bot is updating a message that a user wrote, so do I need special permissions?
let ActivityID = context.activity.conversation.id.split("=")[1];
let updatedActivity: Partial<Activity> = {
"id": ActivityID,
"channelId": context.activity.channelId,
"channelData": context.activity.channelData,
"conversation":
{
"name": "",
"id": context.activity.conversation.id,
"isGroup": context.activity.conversation.isGroup,
"conversationType": context.activity.conversation.conversationType,
"tenantId": context.activity.conversation.tenantId
},
"type": "message",
"text": "",
"summary": "",
"attachments": [ attachment ]
} await context.updateActivity(updatedActivity);

Related

Pushing and pulling JSON data with a filter from API

I am working on a REACT JS project in an attempt to create a small Todo List app.
I have my data in a JSON file, currently hosted on jsonbin.io, in a format that looks like this...
{
"moduleAccess": {
"tasks": [
{
"email": "campbell#yahoo.com",
"id": 0,
"task_name": "Call mom",
"due_date": 44875,
"completed": true
},
{
"email": "palsner593#gmail.com",
"id": 1,
"task_name": "Buy eggs",
"due_date": 44880,
"completed": false
},
{
"email": "rob#gmail.com",
"id": 2,
"task_name": "Go to dog park",
"due_date": 44879,
"completed": false
}
]
}
}
Currently, I fetch the data using jsonbin.io's API. The data is brought into a variable called Tasks. If a user updates a specific to-do item, deletes a to-do item, or creates a new one, all those changes are put back into the Tasks variable. I can then send push those tasks to the server.
What I explained above works fine; however, the caveat is that I would like to allow multiple users to log in and then pull only the Todo items that pertain to their respective email.
Say, campbell#yahoo.com is logged in to my app. In this case, in my fetch pull request, I can specify that I would only like records with campbell#yahoo.com
async function loadData() {
const newPath = '$..tasks[?(#.email==' + campbell#yahoo.com + ')]';
console.log(newPath);
const url = 'https://api.jsonbin.io/v3/b/*binid*?meta=false'
const response = await
fetch(url, {
method: "GET",
headers: {
"X-Master-Key": key,
"X-JSON-Path": newPath
}
});
const data = await response.json();
setTasks([...data]); //or whatever
console.log(tasks);
}
This concept works as well. However, when pushing my task data back to a server after a user has made changes, I encounter an issue. The API I am using does not seem to allow parameters for specifying the JSON path upon PUSH. JSON-PATH is only allowed for a pull request. So when I push data to the server, it seems as if all JSON data will be overwritten, regardless of the user.
Does anybody have an alternative way to push/pull user-specific data? I am sorry if the detail I have provided is unnecessary. Not sure what the easiest way to approach this problem is for a react app.
Any help is appreciated. Thanks!
I did a little research in jsonbin.io API and came up with a solution that might work.
So I'm not really sure that this will work, but still.
When creating a new bin, you can add it to some collection using X-Collection-Id. So you might be able to make next flow:
When user register, create a separate bin for tasks for this user
Add user with bin id to some users collection where you will have all your users
When user auth, get his bin id using filters that you used in your code and store it for future use somewhere in your app.
After this you will be able to fetch users tasks by that bin id and modify it, cause now it will be a separate bin for each user and you can simply override all of its content.
Hope this works.

XMPP - How do I delete all messages between two jids, but only for one user?

Problem:
I want to delete all the messages (and thread) from one side of an equation between two users, A and B. I have no idea if this is even possible and if so, how.
I have the:
jid of each user
an XMPP library in JS (custom) that allows me to send IQ or any other type of stanza.
For example, this is how I get my friends (roster) list:
async getFriends() {
const requestId = this.sendStanza(
'iq',
{ type: 'get' },
(stanza) => stanza.c('query', { xmlns: 'jabber:iq:roster' }),
)
const result = await this.once('*', requestId);
const requests = result.children[0].children.map(child => child.attrs.jid);
return requests;
}
Hopefully this is enough for someone to advise me. Thanks.
If you have full access to the client logic, you can implement your own logic, for instance, you can send an IQ stanza with a specific name space (xmlns) along with some elements/attributes, when the receiving side receives that IQ, it will do whatever logic you want (delete messages, thread, etc..)
Check this out:
https://xmpp.org/extensions/xep-0424.html
it is an extension to delete (retract) single message.

Unable to Post to a Feed Group in GetStream

I'm currently using Stream to build Feeds in our React Native app.
I ran into a problem enabling users to post to a new board feed group I made.
Currently iOS only
Problem: I have created a "board" feed group and want to allow users to post to that feed group to save posts. I have enabled permissions for this as per: https://github.com/GetStream/react-activity-feed/issues/23 via support. Yet I am still unable to post to the "board" feed group but I'm still receiving a permissions error.
I'm using the SinglePost component from the official React Native GetStream library and have a save button on the activity component.
The save button opens a modal to allow a user to select a board and after a user selects a board the _saveBoard method is called. You can see the full method here.
_saveToBoard = item => {
let boardId = item.id;
console.log('boardId', boardId, this.props);
// // Add activity to selectedBoard
var selectedBoard = this.props.client.feed('board', boardId);
let activity_data = {
actor: this.props.client.currentUser.id,
verb: 'save',
object: `BoardPost:${generateId()}`
};
console.log('activity data', activity_data);
selectedBoard
.addActivity(activity_data)
.then(res => console.log('added activity', res))
.catch(err => console.log('error adding activity to board', err));
console.log('selectedBoard', selectedBoard);
};
Not sure where I went wrong and hoping its something I overlooked.
The default permission policies don't allow a user to add an activity with actor set to anything else than current user's reference. This restriction is only enforced client-side to make sure that users are not able to impersonate other users.
Changing actor from this.props.client.currentUser.id into this.props.client.currentUser.id will make sure that the activity is added with a user reference equal to current user.

Telegraf session for unique user?

I'm building a wallet bot and I was wondering how can I initiate a session for an unique user. For example, in this session I would need an object containing the unique user identifier, the public key and secret key so they can access this after initiating the bot.
I was thinking in something like this:
var myWallet = (ctx) =>{
return{
user: ctx.from.id,
publicKey: wallet.public,
secretKey: wallet.secret
}
}
bot.command('/myWallet', (ctx)=>{
ctx.reply(myWallet.user);
ctx.reply(myWallet.publicKey);
ctx.reply(myWallet.secretKey);
})
But when I type /myWallet on my bot nothing happens, any idea what am I doing wrong?
Might be a bit late but for sessions you can use Telegrafs inbuild session management. Here an example:
const session = require('telegraf/session')
const bot = new Telegraf(process.env.BOT_TOKEN)
bot.use(session())
bot.on('text', (ctx) => {
ctx.session.counter = ctx.session.counter || 0
ctx.session.counter++
return ctx.reply(`Message counter:${ctx.session.counter}`)
})
Basically it just works like above example. You intitiate a session (bot.use(session());) then when a user writes you use the context of the returned message (ctx) in which all user data is stored (username, id, message, etc) and calling the session from that (ctx.session). In there you store your regular variable data. Now normal sesions are active until the bot shuts down. When you want persistent sessions just import an 3rd-party session manager as written in the docs.
So to sum that up:
const session = require('telegraf/session') // import session addon
ctx.session.walletData = 'some data' // store data in session
console.log(ctx.session.walletData) // show data

Getting id of the current user from Cloud Functions and Firebase

I am using google cloud functions to register push notifications through firebase. In my app, i have a notifications reference that changes for a current user whenever they get a new follower or like, etc. As of right now I am able to send the notification to the phone whenever that whole reference child changes
For example, if any single post is liked, then it will send a notification. What I need to do is observe the current user to only send the notification that single person.
Here is my JavaScript file
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendPushNotification = functions.database.ref('/notification/{id}').onWrite(event => {
const payload = {
notification: {
title: 'New message arrived',
body: 'come check it',
badge: '1',
sound: 'default',
}
};
return admin.database().ref('fcmToken').once('value').then(allToken => {
if (allToken.val()) {
const token = Object.keys(allToken.val());
return admin.messaging().sendToDevice(token, payload).then(response => {
});
}
});
});
I would like to replace this line:
functions.database.ref('/notification/{id}').onWrite(event => {
With this:
functions.database.ref('/notification/{id}').(The current user ID).onWrite(event => {
How do I get the current users id?
You seem very new to JavaScript (calling it JSON is sort-of a give-away for that). Cloud Functions for Firebase is not the best way to learn JavaScript. I recommend first reading the Firebase documentation for Web developers and/or taking the Firebase codelab for Web developer. They cover many basic JavaScript, Web and Firebase interactions. After those you'll be much better equipped to write code for Cloud Functions too.
Now back to your question: there is no concept of a "current user" in Cloud Functions. Your JavaScript code runs on a server, and all users can trigger the code by writing to the database.
You can figure out what user triggered the function, but that too isn't what you want here. The user who triggered the notification is not the one who needs to receive the message. What you want instead is to read the user who is the target of the notification.
One way to do this is to read it from the database path that triggered the function. If you keep the notifications per user in the database like this:
user_notifications
$uid
notification1: ...
notification2: ...
You can trigger the Cloud Function like this:
exports.sendPushNotification = functions.database.ref('/user_notification/{uid}/{id}').onWrite(event => {
And then in the code of that function, get the UID of the user with:
var uid = event.params.uid;
For Swift 3.0 - 4.0
You can do this:
import Firebase
import FirebaseAuth
class YourClass {
let user = Auth.auth().currentUser
let userID = user.uid
// user userID anywhere
}

Categories