DCR: Context should offer a method to "go proactive"
Issue
There many are instances where developers will want to do a call to sendActivities after the turn has ended - that are not traditional "proactive" types.
For example, developers will use setTimeout to set a test to see if a user has finished a task or needs more time. Currently, this requires that they capture a ConversationReference object from a message, then use it to create a new context to send the "Do you need more time?" message.
This is a really common pattern in Javascript, and it seems like it should work. However since we revoke the proxy on the context at the official end of the turn, it currently throws confusing errors for developers.
Proposed change
I propose that we add a method to the context object that would allow the same context object to be used. This method would refresh the context object in the same way that would happen were the developer to go through the process of capturing the reference and creating a new turn context -- but would leave the original activity intact, etc.
This would enable an example as above:
await turnContext.sendActivity('Let me know within 30 seconds');
setTimeout(() => {
await turnContext().goProactive();
await turnContext.sendActivity('TIME UP');
}, 30000);
Component Impact
This would impact the TurnContext object in core.
Customer Impact
This would be an additive feature, so no breaking. This would make many common dev scenarios much less complex.
Tracking Status
Dotnet SDK
- [ ] PR
- [ ] Merged
Javascript SDK
- [ ] PR
- [ ] Merged
Java SDK
- [ ] PR
- [ ] Merged
Python SDK
- [ ] PR
- [ ] Merged
Emulator
- [ ] PR
- [ ] Merged
Samples
- [ ] PR
- [ ] Merged
Docs
- [ ] PR
- [ ] Merged
Tools
- [ ] PR
- [ ] Merged
[dcr]
@benbrown In your code, after calling goProactive(), it should return a new TurnContext that is capable of proactive messaging, i.e.
const proactiveContext = await turnContext().goProactive();
await proactiveContext.sendActivity('TIME UP');
It ain't too difficult to write that goProactive helper function today. We should include it, and/or make the object model in JavaScript more like real world JavaScript.
export default function goProactive(context, callback) {
const { activity, adapter } = context;
const reference = TurnContext.getConversationReference(activity);
adapter.continueConversation(reference, callback);
}
callbackis an async function that return a Promise when proactiveContextcan be freed up. If Promise pattern is preferred, this async function can be replaced withrespondWithpattern in JavaScript.
On the other hand, it will be nice to have warning messages when the onMessage/etc handlers took more than 2 seconds to complete. So it can give the developers early warnings during development phase.
Question: Does the problem mean that current botframework can't create a bot which does sendActivity from background threads?
Recently I'm making such a bot which saves ConversationReferences on a DB so that the background thread can do sendActivity later on. As long as I tested, its background thread can successfully send messages so far. But it's tested only in short sessions (at most several tens of minutes). So I'm worried whether this is possible some days after the user closes the chat view with the bot.
No, you can use the conversation reference to create a new context.
This DCR would just provide a utility method to do it in async scenarios where the original context object is still in scope, but may have expired.
Closing this issue as the repository is scheduled for deprecation soon.