the below code is getting error while deleting the message based on the channel id
public async Task DeleteSentNotification(
string conversationId,
string recipientId,
string serviceUrl,
string tenantId,
string name)
{
// Set the service URL in the trusted list to ensure the SDK includes the token in the request.
MicrosoftAppCredentials.TrustServiceUrl(serviceUrl);
var conversationReference = new ConversationReference
{
ServiceUrl = serviceUrl,
Conversation = new ConversationAccount
{
TenantId = tenantId,
Id = conversationId,
name = name,(AdaptiveCard Json)
},
};
await this.botAdapter.ContinueConversationAsync(
botAppId: this.microsoftAppId,
reference: conversationReference,
callback: async (turnContext, cancellationToken) =>
{
try
{
// Delete message.
await turnContext.DeleteActivityAsync(conversationReference);
}
catch (ErrorResponseException e)
{
var errorMessage = $"{e.GetType()}: {e.Message}";
}
},
cancellationToken: CancellationToken.None);
// return response;
}
DeleteActivityAsync has two overloads, but they're both basically aimed towards deleting an "Activity" (i.e. a specific message or thread). If you want to use the overload that takes in a conversationReference, then that means your conversationReference needs to have the ActivityId set (see here for more on the property). Alternatively, you can use the overload that takes in an activityId (it looks like this: public System.Threading.Tasks.Task DeleteActivityAsync (string activityId, System.Threading.CancellationToken cancellationToken = default);), and then you need to pass in the activityId.
Essentially, as stated at the beginning of my answer, you're telling it to delete an "Activity", but you're not telling it which Activity. See here for the docs: https://learn.microsoft.com/en-us/dotnet/api/microsoft.bot.builder.turncontext.deleteactivityasync?view=botbuilder-dotnet-stable
Related
I am building an operation that calls api with the cancellationToken parameter. cancel () from event A, when the party B is calling, the api will be caught, but I want this exception to be arrested on the api side. ..... help, it took me 4 days but it didn't work out
api server
public async Task<Actionresult> getText(CancellationToken token){ string s= "";try{for(int i=0;i<10;i++)
{
a += " number "+i;
await Task.Delay(1000,token);
}
}catch(Excaption ex){
return badrequest("canceled")
}
}
client xamaarin app
CancellationTokenSource cts = new CancellationTokenSource();
private async commandEventA()
{
cts.Cancel();
}
private async commandEventB()
{
var token =cts.Token();
string txtkq ="";
var client = new RestClient("urlwebb");
RestRequest request = new RestRequest("");
try{
var a = await
client.ExcuteTaskAsync(request,token);
}catch(TaskCancellationException ex){}
}
I'm using TranscriptLoggerMiddleware to log transcript to Azure blobs.
Now, I want to add additional information to the activity, for example, account ID.
Ideally I want the account ID to be the top level folder when creating the blobs so one can easily locate all conversations for a given account.
The logger is only passed the activity without any context. So I'm looking at the Entities activity property which I can potentially use for storing my account ID.
Is this a valid approach?
Any other ideas on how to implement this?
Answering my own question.
This worked for me:
public class SetEntityMiddleware : IMiddleware
{
private readonly BotState _userState;
private readonly IStatePropertyAccessor<UserProfileState> _userProfileState;
public SetEntityMiddleware(UserState userState)
{
_userState = userState;
_userProfileState = userState.CreateProperty<UserProfileState>(nameof(UserProfileState));
}
public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken = default)
{
var userProfile = await _userProfileState.GetAsync(turnContext, () => new UserProfileState(), cancellationToken);
this.SetEntity(turnContext.Activity, userProfile);
turnContext.OnSendActivities(async (ctx, activities, nextSend) =>
{
var userProfile = await _userProfileState.GetAsync(ctx, () => new UserProfileState(), cancellationToken);
foreach (var activity in activities)
{
this.SetEntity(activity, userProfile);
}
return await nextSend().ConfigureAwait(false);
});
await next(cancellationToken).ConfigureAwait(false);
}
private void SetEntity(Activity activity, UserProfileState userProfile)
{
if (activity.Type == ActivityTypes.Message &&
!string.IsNullOrEmpty(userProfile.AccountNumber))
{
var entity = new Entity();
entity.Type = "userProfile";
entity.Properties["accountNumber"] = userProfile.AccountNumber;
if (activity.Entities == null)
{
activity.Entities = new List<Entity>();
}
activity.Entities.Add(entity);
}
}
}
I am working on a xamarin mobile application, upon making an async call to the exposed api, i do not get any error, however when i execute the .Result on the task the call never proceeds and it stuck forever.
Click here to see stringResourceResponse details
The same .Result call from a separate project (windows service) in the same solution works.
Any idea if .NET standard is causing limitation in executing async tasks, any advice would be helpful, thanks
Code added below:
//This is code from app.xaml.cs
var stringResourceApi = new StringResourceApiTask();
Task.Run(() =>
{
a = controller.CallStringResourceApi(stringResourceApi);
}).Wait();
public class MobileController
{
public string CallStringResourceApi(StringResourceApiTask stringResourceApiTask)
{
return stringResourceApiTask.Start(StringResourceUrl);
}
}
public override string Start(string URL)
{
var stringResourceResponse = SendRequest(url, "", HttpMethod.Get);
var result = stringResourceResponse.Result;
return result;
}
protected async Task < string > SendRequest(string url, string uri, HttpMethod method, int attempt = 1, int maxAttempts = 5)
{
return await SendRequest(
url, uri, Key, Secret, method, string.Empty, attempt, maxAttempts)
.ConfigureAwait(false);
}
protected async Task<string> SendRequest(string url, string uri, string key, string secret, HttpMethod method,
string requestBody = "", int attempt = 1, int maxAttempts = 5)
{
if (attempt > maxAttempts)
{
return null;
}
var client = InitialiseHttpClient(key, secret);
var request = new HttpRequestMessage
{
RequestUri = string.IsNullOrEmpty(url) ? new Uri(uri) : new Uri(url),
Method = method,
};
if (!string.IsNullOrWhiteSpace(requestBody))
{
request.Content = new StringContent(requestBody, Encoding.UTF8, "application/json");
}
SetOutputText($"Attempting to communicate with {uri}...{Environment.NewLine}");
using (var response = await client.SendAsync(request).ConfigureAwait(false))
{
using (var content = response.Content)
{
try
{
response.EnsureSuccessStatusCode();
}
catch (HttpRequestException ex)
{
if (attempt > maxAttempts)
{
SetOutputText(errorMessage);
}
return await SendRequest(url, uri, key, secret, method, requestBody, attempt + 1).ConfigureAwait(false);
}
var responseBody = await content.ReadAsStringAsync().ConfigureAwait(false);
var isSuccessResponseButEmptyBody = response.IsSuccessStatusCode &&
(string.IsNullOrEmpty(responseBody) ||
string.IsNullOrWhiteSpace(responseBody));
if (!isSuccessResponseButEmptyBody)
{
return responseBody;
}
if (attempt > maxAttempts)
{
SetOutputText(errorMessage);
}
return await SendRequest(url, uri, key, secret, method, requestBody, attempt + 1).ConfigureAwait(false);
}
}
}
when i execute the .Result on the task the call never proceeds and it stuck forever.
Yes. This is a common deadlock situation. When code running on the UI thread blocks on asynchronous code, a deadlock usually occurs.
The same .Result call from a separate project (windows service) in the same solution works.
It works because the Win32 service code does not run on a UI thread.
The proper solution is to remove the blocking code; use await instead. This in turn will cause the calling methods to become async (e.g., StringResourceApiTask.Start), and they should also be awaited, etc. The usage of async and await should "grow" through your code; this is natural.
Alternatively, you can block in a thread pool thread, e.g., Task.Run(() => a = controller.CallStringResourceApi(stringResourceApi)).GetAwaiter().GetResult();. This is a bit of a hack (consuming an unnecessary thread), but it's a quick way to remove the deadlock. Note that this hack is not appropriate for ASP.NET apps; it's acceptable here since this is a UI app.
I currently try to send a private notification to a user.
What I currently have is a connected bot and connector in my MS Teams environment.
In my c# backend I have the bot which inherit from the ActivityHandler, the tenant id and the user id of ms teams.
The case is now:
Someone is creating an object (for example a Task) in my backend by making an API Post call and I want to notify the user in ms teams.
My idea was now to instantiate the Bot in my API controller (including the ITurnContext). Then with the bot find the right ms teams environment with the tenant id and user id, create a new chat/conversation and send the message. But I guess this is not the right way or I do something wrong. Because I think there is no way to initialise the ITurnContext from my code or?
This is my code and my idea was to use the CreatePrivateConversation method in my API controller.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Teams;
using Microsoft.Bot.Connector;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Bot.Schema;
using Microsoft.Bot.Schema.Teams;
namespace IntegrationsService.Bots
{
public class ProactiveBot : ActivityHandler
{
double _secondsToReply = 3;
ICredentialProvider _credentialProvider;
public ProactiveBot(ICredentialProvider credentialProvider)
{
_credentialProvider = credentialProvider;
}
public async Task<ConversationResourceResponse> CreatePrivateConversation(string message, ITurnContext turnContext)
{
ConnectorClient _client = new ConnectorClient(
new Uri(turnContext.Activity.ServiceUrl),
await GetMicrosoftAppCredentialsAsync(turnContext),
new HttpClient());
var channelData = turnContext.Activity.GetChannelData<TeamsChannelData>();
var conversationParameter = new ConversationParameters
{
Bot = turnContext.Activity.Recipient,
Members = new[] { new ChannelAccount("userid") },
// IsGroup = true,
ChannelData = channelData,
TenantId = channelData.Tenant.Id,
Activity = MessageFactory.Text(message)
};
var response = await _client.Conversations.CreateConversationAsync(conversationParameter);
return response;
}
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
try
{
await turnContext.SendActivityAsync(MessageFactory.Text($"I'll reply to you in {_secondsToReply} seconds."));
QueueReplyAndSendItProactively(turnContext).Wait();
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
throw e;
}
}
public async Task QueueReplyAndSendItProactively(ITurnContext turnContext)
{
string conversationMessage = "I created my own conversation.";
string replyMessage = "I proactively replied to this conversation.";
var task = Task.Run(async () =>
{
await Task.Delay(TimeSpan.FromSeconds(_secondsToReply));
// Let the Bot Proactively create a a conversation.
var response = await CreateConversation(conversationMessage, turnContext);
// Reply to the conversation which the bot created.
await ProactivelyReplyToConversation(response.Id, replyMessage, turnContext);
return Task.CompletedTask;
});
await task;
}
public async Task<ConversationResourceResponse> CreateConversation(string message, ITurnContext turnContext)
{
ConnectorClient _client = new ConnectorClient(new Uri(turnContext.Activity.ServiceUrl), await GetMicrosoftAppCredentialsAsync(turnContext), new HttpClient());
var channelData = turnContext.Activity.GetChannelData<TeamsChannelData>();
var conversationParameter = new ConversationParameters
{
Bot = turnContext.Activity.Recipient,
IsGroup = true,
ChannelData = channelData,
TenantId = channelData.Tenant.Id,
Activity = MessageFactory.Text(message)
};
var response = await _client.Conversations.CreateConversationAsync(conversationParameter);
return response;
}
public async Task ProactivelyReplyToConversation(string conversationId, string message, ITurnContext turnContext)
{
ConnectorClient _client = new ConnectorClient(new Uri(turnContext.Activity.ServiceUrl), await GetMicrosoftAppCredentialsAsync(turnContext), new HttpClient());
var reply = MessageFactory.Text(message);
reply.Conversation = new ConversationAccount(isGroup: true, id: conversationId);
await _client.Conversations.SendToConversationAsync(reply);
}
private async Task<MicrosoftAppCredentials> GetMicrosoftAppCredentialsAsync(ITurnContext turnContext)
{
ClaimsIdentity claimsIdentity = turnContext.TurnState.Get<ClaimsIdentity>("BotIdentity");
Claim botAppIdClaim = claimsIdentity.Claims?.SingleOrDefault(claim => claim.Type == AuthenticationConstants.AudienceClaim)
??
claimsIdentity.Claims?.SingleOrDefault(claim => claim.Type == AuthenticationConstants.AppIdClaim);
string appPassword = await _credentialProvider.GetAppPasswordAsync(botAppIdClaim.Value).ConfigureAwait(false);
return new MicrosoftAppCredentials(botAppIdClaim.Value, appPassword);
}
protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
foreach (var member in membersAdded)
{
if (member.Id != turnContext.Activity.Recipient.Id)
{
await turnContext.SendActivityAsync(MessageFactory.Text($"Hello and Welcome!"), cancellationToken);
}
}
}
}
}
Thanks for the help.
There is no way to initialize the ITurnContext from code.
So If you want to send a private proactive message to user, You need to follow below steps
"OnConversationUpdateActivityAsync" method will be called when user install bot. You can get required parameters like ConversationId, ServiceUrl from turnContext.Activity.From object. you can store these details in database when each user installs the bot.
OnConversationUpdateActivityAsync(
ITurnContext turnContext,
CancellationToken cancellationToken)
When user makes an POST API call, you need to pass user id or AadID based on which you can query database and get user details which are needed to send proactive message. Now you can call "CreatePrivateConversation" method and send the proactive message
My facebook bot has 2200 subscriber , so I am using the below code to send them an image daily . However , this code is failing for most of the users with the error as captured from bot framework
public static class MessagesSender
{
public static void SendSpecificMessage(string botName, string serviceURL, string botId, string messageCode, string messageText, string imageURL, Customer customer , Guid logId)
{
var connector = new ConnectorClient(new Uri(serviceURL));
Thread thread = new Thread(() => SendMessage(botName, serviceURL, botId, messageCode, messageText, imageURL, customer, connector , logId));
thread.Start();
}
private static void SendMessage(string botName, string serviceURL, string botId, string messageCode, string messageText, string imageURL, Customer customer, ConnectorClient connector , Guid logId)
{
try
{
Task.Run(async () =>
{
IMessageActivity message = Activity.CreateMessageActivity();
//defining accounts
var userAccount = new ChannelAccount(name: customer.name, id: customer.fromidstate);
var botAccount = new ChannelAccount(name: botName, id: botId);
//creating conversation
var conversationId = await connector.Conversations.CreateDirectConversationAsync(botAccount, userAccount);
message.From = botAccount;
message.Recipient = userAccount;
message.Text = "Daily Image";
message.Conversation = new ConversationAccount(id: conversationId.Id.Replace("#", string.Empty));
if (!string.IsNullOrEmpty(imageURL))
{
message.Attachments = new List<Attachment>();
message.Attachments.Add(new Attachment()
{
ContentUrl = imageURL,
ContentType = "image"
});
}
await connector.Conversations.SendToConversationAsync((Activity)message);
}).Wait();
}
catch ( Exception ex)
{
throw ex;
}
}
}
{"error":{"message":"(#100) Failed to fetch the file from the url","type":"OAuthException","code":100,"error_subcode":2018008,"fbtrace_id":"G8ZFKZLCmNp"}}
I am not sure what to do because when I am sending one message it's working fine but not all the 2200 messages isnt delivered , If i send it for 10 persons it's fine. and also the main problem is that the text gets delivered but not the image
The error is being returned by the Facebook API. It looks like you are hitting a throttling limit. The solution is to slow down the rate at which you send images.
You can check the error return code ("613") if you want to confirm
Facebook error codes