I have a pub/sub scenario, where I create and save to DB something in one service, publish it to a SNS topic, subscribe with SQS listener, and handle the message and save it to DB in another service. So far so good.
In one of the scenarios I create a user and subscribe it to a site. Then I send the new user to its topic, the user-site relation to another topic, and the subscribed service updates its own DB tables.
private void publishNewUserNotifications(UserEntity userEntity, List<SiteEntity> sitesToAssociateWithUser) {
iPublisherService.publishNewUserNotification(userEntity);
if (sitesToAssociateWithUser != null || !sitesToAssociateWithUser.isEmpty()) {
List<String> sitesIds = sitesToAssociateWithUser.stream().map(SiteEntity::getSiteId).collect(Collectors.toList());
iPublisherService.publishSitesToUserAssignment(userEntity.getId(), new ArrayList<>(), sitesIds);
}
}
The problem is that sometimes I have a thread race and handle the user-site relation before I created the user in the second service, get an empty result from DB when loading the User object, and fail to handle the user-site relation.
#Override
#Transactional
public void handle(UsersSitesListNotification message) {
UsersSitesNotification assigned = message.getAssigned();
List<UserEntity> userEntities = iUserRepository.findAllByUserIdIn(CollectionUtils.union(assigned.getUserIds()));
List<SiteEntity> siteEntities = iSiteRepository.findAllByIdIn(CollectionUtils.union(assigned.getSiteIds()));
List<UserSiteAssignmentEntity> assignedEntities = fromUsersSitesNotificationToUserSiteAssignmentEntities(assigned, userEntities, siteEntities);
Iterable<UserSiteAssignmentEntity> saved = iUserSiteAssignmentRepository.saveAll(assignedEntities);
}
Because of that, I consider using SQS in a synchronic way. The problem is that in order to use SQS I need to import the "spring-cloud-aws-messaging" package, and the SQS configuration inside it uses the Async client.
Is there a way to use SQS in a synchronic way? What should I change? How should I override the Async configuration that I need in the package/get some other package?
Any idea will help, tnx.
I am exploring the Microsoft Bot Builder SDK to create a chat bot that integrates with MS Teams. Most of the provided samples do not have any authentication mechanisms and the samples that reference OAuth seem to do so for allowing the bot to access a resource using the on-behalf-of flow. Is correct way to think of the security model is that the bot should be considered public and any non-public information accessed is done from the context of the calling user?
The Bot Framework has three kinds of authentication/authorization to consider:
Bot auth - Microsoft app ID and password
Client auth - Direct Line secret/token, or various mechanisms for other channels
User auth - OAuth cards/prompts/tokens
Unfortunately there's some inconsistency in the documentation about which is which, but I've just raised an issue about that here: https://github.com/MicrosoftDocs/bot-docs/issues/1745
In any case, there's no need to think of all bots as "public." The Bot Builder SDK authenticates both incoming messages and outgoing messages using its app ID and password. This means any unauthorized messages sent to the bot's endpoint will be rejected, and no other bot can impersonate yours.
In general you should have the user sign in if you want the bot to access secure information on the user's behalf. But since you mentioned wanting to restrict bot access to specific tenants, I can briefly explain how to do that. You can find middleware here that does it in C#, and here's a modified version of the code that I think improves on it by using a hash set instead of a dictionary:
public class TeamsTenantFilteringMiddleware : IMiddleware
{
private readonly HashSet<string> tenantMap;
public TeamsTenantFilteringMiddleware(IEnumerable<string> allowedTenantIds)
{
if (allowedTenantIds == null)
{
throw new ArgumentNullException(nameof(allowedTenantIds));
}
this.tenantMap = new HashSet<string>(allowedTenantIds);
}
public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken = default(CancellationToken))
{
if (!turnContext.Activity.ChannelId.Equals(Channels.Msteams, StringComparison.OrdinalIgnoreCase))
{
await next(cancellationToken).ConfigureAwait(false);
return;
}
TeamsChannelData teamsChannelData = turnContext.Activity.GetChannelData<TeamsChannelData>();
string tenantId = teamsChannelData?.Tenant?.Id;
if (string.IsNullOrEmpty(tenantId))
{
throw new UnauthorizedAccessException("Tenant Id is missing.");
}
if (!this.tenantMap.Contains(tenantId))
{
throw new UnauthorizedAccessException("Tenant Id '" + tenantId + "' is not allowed access.");
}
await next(cancellationToken).ConfigureAwait(false);
}
}
I have created a Teams bot in .NET Core from following the sample found here: https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/57.teams-conversation-bot
This is working and is running locally with ngrok. I have a controller with a route of api/messages:
[Route("api/messages")]
[ApiController]
public class BotController : ControllerBase
{
private readonly IBotFrameworkHttpAdapter Adapter;
private readonly IBot Bot;
public BotController(IBotFrameworkHttpAdapter adapter, IBot bot)
{
Adapter = adapter;
Bot = bot;
}
[HttpPost]
public async Task PostAsync()
{
// Delegate the processing of the HTTP POST to the adapter.
// The adapter will invoke the bot.
await Adapter.ProcessAsync(Request, Response, Bot);
}
}
I now want to call a POST to api/messages from my Angular client using TypeScript to send a proactive message to a specific Teams user.
I did figure out how to set the ConversationParameters in TeamsConversationBot.cs to a specific Teams user by doing the following:
var conversationParameters = new ConversationParameters
{
IsGroup = false,
Bot = turnContext.Activity.Recipient,
Members = new[] { new ChannelAccount("[insert unique Teams user guid here]") },
TenantId = turnContext.Activity.Conversation.TenantId,
};
but what I'm struggling with is how to build a JSON request that sends the Teams user guid (and maybe a couple other details) to my api/messages route from TypeScript.
How do I go about doing this? What parameters/body do I need to send? I haven't been able to find samples online that show how to do this.
Update below for added clarification
I am building a web chat app using Angular for our customers. What I'm trying to do is send a proactive message to our internal employees, who are using Microsoft Teams, when a customer performs some action via the chat app (initiates a conversation, sends a message, etc.).
I've built a Teams bot using .NET Core using this sample: https://kutt.it/ZCftjJ. Modifiying that sample, I can hardcode my Teams user ID and the proactive message is showing up successfully in Teams:
var proactiveMessage = MessageFactory.Text($"This is a proactive message.");
var conversationParameters = new ConversationParameters
{
IsGroup = false,
Bot = turnContext.Activity.Recipient,
Members = new[] { new ChannelAccount("insert Teams ID here") },
TenantId = turnContext.Activity.Conversation.TenantId,
};
await ((BotFrameworkAdapter)turnContext.Adapter).CreateConversationAsync(teamsChannelId, serviceUrl, credentials, conversationParameters,
async (t1, c1) =>
{
conversationReference = t1.Activity.GetConversationReference();
await ((BotFrameworkAdapter)turnContext.Adapter).ContinueConversationAsync(_appId, conversationReference,
async (t2, c2) =>
{
await t2.SendActivityAsync(proactiveMessage, c2);
},
cancellationToken);
},
cancellationToken);
What I'm struggling with is:
How to configure my Angular app to notify my bot of a new proactive message I want to send.
How to configure the bot to accept some custom parameters (Teams user ID, message).
It sounds like you've got some progress with pro-active messaging already. Is it working 100%? If not, I've covered the topic a few times here on stack overflow - here's an example that might help: Programmatically sending a message to a bot in Microsoft Teams
However, with regards -trigging- the pro-active message, the truth is you can do it from anywhere/in any way. For instance, I have Azure Functions that run on their own schedules, and pro-active send messages as if they're from the bot, even though the code isn't running inside the bot at all. You haven't fully described where the Angular app fits into the picture (like who's using it for what), but as an example in your scenario, you could create another endpoint inside your bot controller, and do the work inside there directly (e.g. add something like below:)
[HttpPost]
public async Task ProActiveMessage([FromQuery]string conversationId)
{
//retrieve conversation details by id from storage (e.g. database)
//send pro-active message
//respond with something back to the Angular client
}
hope that helps,
Hilton's answer is still good, but the part about proactively messaging them without prior interaction requires too long of a response. So, responding to your latest comments:
Yes, the bot needs to be installed for whatever team the user resides in that you want to proactively message. It won't have permissions to do so, otherwise.
You don't need to override OnMembersAddedAsync; just query the roster (see below).
You don't need a conversation ID to do this. I'd make your API, instead, accept their Teams ID. You can get this by querying the Teams Roster, which you'll need to do in advance and store in a hash table or something...maybe a database if your team size is sufficiently large.
As far as required information, you need enough to build the ConversationParameters:
var conversationParameters = new ConversationParameters
{
IsGroup = false,
Bot = turnContext.Activity.Recipient,
Members = new ChannelAccount[] { teamMember },
TenantId = turnContext.Activity.Conversation.TenantId,
};
...which you then use to CreateConversationAsync:
await ((BotFrameworkAdapter)turnContext.Adapter).CreateConversationAsync(
teamsChannelId,
serviceUrl,
credentials,
conversationParameters,
async (t1, c1) =>
{
conversationReference = t1.Activity.GetConversationReference();
await ((BotFrameworkAdapter)turnContext.Adapter).ContinueConversationAsync(
_appId,
conversationReference,
async (t2, c2) =>
{
await t2.SendActivityAsync(proactiveMessage, c2);
},
cancellationToken);
},
cancellationToken);
Yes, you can modify that sample. It returns a Bad Request because only a particular schema is allowed on /api/messages. You'll need to add your own endpoint. Here's an example of NotifyController, which one of our other samples uses. You can see that it accepts GET requests. You'd just need to modify that our build your own that accepts POST requests.
All of this being said, all of this seems like it may be a bigger task than you're ready for. Nothing wrong with that; that's how we learn. Instead of jumping straight into this, I'd start with:
Get the Proactive Sample working and dig through the code until you really understand how the API part works.
Get the Teams Sample working, then try to make it message individual users.
Then build your bot that messages users without prior interaction.
If you run into trouble feel free to browse my answers. I've answered similar questions to this, a lot. Be aware, however, that we've switched from the Teams Middleware that I mention in some of my answers to something more integrated into the SDK. Our Teams Samples (samples 50-60) show how to do just about everything.
I am try to save data to the MemoryStorage in Microsoft Bot Frame Work (in .NET environment).
I am using this method for do it:
public static class StateManager
{
private static MemoryStorage _myStorage;
static StateManager()
{
_myStorage = new MemoryStorage();
}
public async static void Save(UserDetails userDetails)
{
var changes = new Dictionary<string, object>();
{
changes.Add("ud", userDetails);
}
await _myStorage.WriteAsync(changes, new CancellationToken());
}
}
until now it's always work fine. but suddenly i am getting this error:
System.Exception: Etag conflict. Original: 4 Current: 5
any idea how to solve this error? thanks!
edit - with solve
I got that the problem was that i push data to the memory twice in a row (without get the data between the tow pushes). it's mean that after i push data one time, i have to get the data from the storage before i push the data again.
My question now it's why? i cannot save data twice without get the data between the tow pushes?
Without more code, I wasn't able to replicate your issue. However, it sounds like you have a concurrency problem.
Your Save() method returns a void. You should instead use:
public async static Task Save(UserDetails userDetails)
Then, when saving, call with:
await StateManager.Save(userDetails).
However, you can save yourself the trouble of these kinds of things and use BotBuilder's built-in state storage. References:
Save User and Conversation Data
Core Bot Sample - This is an example of good user profile storage
I am using directline V3 for testing out a bot inside MS Teams.
This is a bot showing some messages inside MS Teams.
Is there a way to read all the messages which are already posted in the bot without knowing their respective Conversation IDs. How to read all the conversations from the bot show in the attached screenshot.
On bot side, if we want to save and retrieve all the conversation history, in C# we can implement the IActivityLogger interface, and log the data in Task LogAsync(IActivity activity) for example:
public class ActivityLogger : IActivityLogger
{
public Task LogAsync(IActivity activity)
{
IMessageActivity msg = activity.AsMessageActivity();
//log here
return null;
}
}
So if you save data in Azure SQL Database, you can refer to Saving Bot Activities in Azure SQL Database, and here are some official examples.
Then in node.js, you can intercept and log messages using middleware:
bot.use({
botbuilder: function (session, next) {
myMiddleware.logIncomingMessage(session, next);
},
send: function (event, next) {
myMiddleware.logOutgoingMessage(event, next);
}
})