How is Activity.Text encoded Microsoft Teams? - c#

I am writing a custom Teams bot. I got some questions about Activity.Text field:
How is it encoded? I see some <at> tags when the bot is #mentioned, but I also see some '. Can I use HttpUtility.HtmlDecodeto decode it?
Is there any document about the details of the Activity.Text field? The schema says it is
Text of the message that is sent from user to bot or bot to user. See the channel's documentation for limits imposed upon the contents of this property.
But it does not talk about the details about the field.

Nothing special about Activity.Text in Teams except for the tags. In general, depending on the Activity.TextFormat, the message you send might contain markdown or XML, but in general the message you receive from a user will be plain text.
Anything additional, like if the user sends bold text to your bot, can be extracted from the attachments object in the incoming payload, e.g. :
"attachments": [
{
"contentType": "text/html",
"content": "<div><span itemscope=\"\" itemtype=\"http://schema.skype.com/Mention\" itemid=\"0\">Teams TestBot</span> |echo| <strong><strong>Hi</strong></strong>​​​​​​​</div>"
}
]

Related

How to get SendGrid bulk-email statuses in C#

I need to send an emails (bulk sending), and I have two cases:
The same email sent into multiple recipients
Different emails sent into different recipients
How, in both cases, I can control statuses?
First case will return single x-message-id, but how it can be translated into separate status for each recipients? (delivered, opened and so on?)
Second case, I believe, need to be done by sending one-by one, is that correct? Or there is also method to send bulk emails in that case?
I'm using C# and official .NET library.
To send the same email sent to multiple recipients:
You can use the MailHelper.CreateSingleEmailToMultipleRecipients and you'll need to specify the text and HTML content in code. (Sample)
You can also use the MailHelper.CreateSingleTemplateEmailToMultipleRecipients method which will use a SendGrid Dynamic Email Template. In that case the email template is already stored in SendGrid and you simply provide the ID of the template.
There's a showAllRecipients named parameter which defaults to false. When false recipients cannot see other recipients, when true recipients can see other recipients.
I'll talk about retrieving the status's of the individual emails later.
To send different email to different people:
You can use MailHelper.CreateMultipleEmailsToMultipleRecipients which will use a single text and HTML body template, but you can add substitution tags and pass in substitution data so the email will be personalized for every recipient. (Sample)
You can construct a single MailMessage but use multiple Personalization objects to override any of the MailMessage properties for specific recipients. (Sample)
You can also construct multiple MailMessage objects and submit them individually to the API, although the above options are made for this scenario.
To send any of these emails, your API key must have the mail.send permission.
There are additional ways to send bulk email with SendGrid, which I documented here in much more detail.
As you noted, when you send email in bulk, only a single message ID is returned via the x-message-id header.
When you later try to retrieve the details using that message ID, you will not find any messages.
That is because the message ID in the x-message-id header is used as a base ID and the real message IDs will append another ID.
For example, x-message-id will return "W86EgYT6SQKk0lRflfLRsA", but when you retrieve one of the individual messages, it will looks something like this: "W86EgYT6SQKk0lRflfLRsA.filterdrecv-5645d9c87f-78xgx-1-62841247-82.1".
You can retrieve these messages via the E-Mail Activity API like this:
var queryParams = JsonSerializer.Serialize(new
{
query = $"msg_id LIKE '{messageId}%'",
limit = 10
});
var response = await client.RequestAsync(
method: SendGridClient.Method.GET,
urlPath: "messages",
queryParams: queryParams
);
Console.WriteLine(await response.Body.ReadAsStringAsync());
Take note of the query property which is has the $"msg_id LIKE '{messageId}%'" value.
This query will filter to messages that begin with the message ID returned from the bulk email (messageId), and as a result retrieve all the individual messages.
You cannot query these messages immediately after submitting the bulk email to SendGrid's queue as it takes some time for them to become available through the API.
In my code I queried these messages every 30 seconds until the count of the recipients in the bulk email matched the messages returned.
The resulting JSON data looks like this:
{
"messages": [
{
"from_email": "some#email.address",
"msg_id": "5QSczogTRHqFtiIkLxMtWA.filterdrecv-5645d9c87f-6r2ch-1-62847C63-2D.0",
"subject": "Sending with Twilio SendGrid is Fun",
"to_email": "some#email.address",
"status": "delivered",
"opens_count": 0,
"clicks_count": 0,
"last_event_time": "2022-05-18T05: 01: 05Z"
},
{
"from_email": "some#email.address",
"msg_id": "5QSczogTRHqFtiIkLxMtWA.filterdrecv-5645d9c87f-6r2ch-1-62847C63-2D.1",
"subject": "Sending with Twilio SendGrid is Fun",
"to_email": "some#email.address",
"status": "delivered",
"opens_count": 0,
"clicks_count": 0,
"last_event_time": "2022-05-18T05: 01: 05Z"
},
...
]
}
As you can see, this includes the status property.
Note: You must purchase additional email activity history to gain access to the Email Activity Feed API.
Note: To retrieve message via the Email Activity Feed API, your API key must have the email_activity.read permission.
This would be one way to retrieve the status of your email messages.
Another way to keep track of the status of email messages would be to create a public ASP.NET endpoint configure the URL as the SendGrid Event Webhook.
SendGrid will send an HTTP request to your ASP.NET endpoint for every event which you can use to update the status of email messages.
PS: You may already be doing this, but whether you're sending a single email to multiple recipients or multiple emails to multiple recipients, Twilio recommends setting the SendAt property on the SendGridMessage when sending bulk emails.
Quoting from the SendGrid docs:
This technique allows for a more efficient way to distribute large
email requests and can improve overall mail delivery time performance.
This functionality:
Improves efficiency of processing and distributing large volumes of email.
Reduces email pre-processing time.
Enables you to time email arrival to increase open rates.
Is available for free to all SendGrid customers.
I hope that answered all your questions, if not let me know. I can't wait to see what you build!

Outlook Mail API returns spellcheck class names as part of html response when using List Messages

I am currently using the outlook mail api to retreive messages from a specific shared folder (List Messages Request), when i get a response from the query i want to read the body content in this case my header prefers html.
What i'm trying to achieve is string replacement from the html response.
The problem is inside my shared emails i have something like this:
Hello [UserName], further text in mail message, Regards [CompanyName].
and the response i get from the api looks like this:
<p class=\"MsoNormal\">Hello [<span class=\"SpellE\">UserName</span>],</p><p class=\"MsoNormal\"> </p><p class=\"MsoNormal\">further text in mail message, Regards [CompanyName].</p>
the response shows a spelling error has been returned with one of my string placement texts and not the other, this is not ideal because i dont want to rely on me writing some code to check if:
[<span class=\"SpellE\">UserName</span>]
exists or not, mainly because this could be subject to change at any given time and that would be a breaking change to the system.
Is there any way i can disable spell checking being returned in the html?
Try disabling spell checking in Microsoft Outlook,
File -> Options -> Mail -> Spell
either through the application or programatically by altering the configuration in the windows registry.
Look at
HKCU\Software\Microsoft\Office\11.0\Outlook\Options\Spelling
HKCU\Software\Microsoft\Shared Tools\Proofing Tools\1.0\Office\OutlookSpellingOptions
HKCU\Software\Microsoft\Spelling

How do you terminate a conversation based on user input evaluation through a webhook (api v2)

Let's say my bot has an ongoing conversation with the user and my webhook asks "Are you done?" and the user responds "Yes".
How do I close the conversation through the webhook?
I suspect it is through followupEventInput but I could not get anything to work.
I am not using a nodeJS library. Coding in C#.
Here is what I tried: I respond with an output context and an event: the event is associated with "End conversation" under the Google Assistant at the bottom of the intent window.
{
"fulfillmentText": "OK. The note was recorded.",
"fulfillmentMessages": [
{
"text": {
"text": [
"OK. The note was recorded."
]
}
}
],
"outputContexts": [
{
"lifespanCount": 2,
"name": "projects/xxx/agent/sessions/1512399774430/contexts/note-end"
}
],
"followupEventInput": {
"name": "end-event",
"languageCode": "en"
}
}
With the above code, the user never hears "OK. The note was recorded.", the session ends abruptly with Google Home saying "The app is not responding anymore" (or something similar). Works but not acceptable.
If I don't put the followupEvent, the user hears "OK. The note was recorded." but the conversation doesn't end... then I need to rely on the user to close it using another intent, which is what I want to avoid as it would be redundant.
You have two options to end a Google Assistant conversation with Dialogflow:
Response defined in Dialogflow
Go to the intent defined in Dialogflow and scroll down and click on Google Assistant and check the box labeled End conversation
NOTE: this will only work with responses defined in Dialogflow console, not responses created by fulfillment.
Response defined in webhook
Respond to Dialogflow's webhook request with a response of the form:
{
"fulfillmentMessages": [
{
{
"platform": "ACTIONS_ON_GOOGLE",
"paylaod":{
{
"expectUserResponse": false,
...
}
}
}
}
],
...
}
Using either of these methods your app will say whatever is specified in the console (for #1) or webhook (for #2) and end the conversation with the user. Fill in values in the response section of the Dialogflow intent (for #1) or the payload object (for #2) to send a response to the user as you end the conversation.
Sources
Dialogflow Webhook Response v2 docs:https://dialogflow.com/docs/reference/api-v2/rest/v2beta1/WebhookResponse
Action on Google webhook response docs: https://developers.google.com/actions/reference/rest/Shared.Types/AppResponse#FIELDS.expect_user_response
OK. After further investigation, it seems that:
the text of the fulfillment is never sent to the user but it needs to be set to something (" " works), otherwise the user will be told that the app is not responding and that s/he has to try again later.
The context and the event are correct and necessary. The only way (I found) to get the user to hear "OK. The note was recorded." is to include this message as a text response of the intent associated with the event. When set this way, the app closes in an acceptable manner.

Link formatting not working

On the Slack website, I can format a message like this:
{
"text": "<http://google.com|link to google>"
}
And in the message it will appear like this:
link to google
But I am trying to write a bot and those links aren't working. Using my websocket connection I can send a message like this:
var send = new MessageToSlack()
{
type = "message",
channel = msg.channel,
text = $"http://google.com"
};
ws.SendAsync(JsonConvert.SerializeObject(send), b => { });
And Slack will correctly interpret http://google.com as a link and display it like:
http://google.com
But if I try to send a message with the link in angle brackets with the pipe between the link and the link text (which works on Slack's site) like this:
var send = new MessageToSlack()
{
type = "message",
channel = msg.channel,
text = $"<http://google.com|to google>"
};
ws.SendAsync(JsonConvert.SerializeObject(send), b => { });
I end up with:
<http://google.com|to google>
So how do I get this working from my bot? Why is it not able to parse by links correctly? What am I missing?
As far as I can see from the docs, this should work. Here in the section on formatting messages it says:
The RTM API only supports posting simple messages formatted using our default message formatting mode.
And links to here which does mention links with the pipe character, so I think it should work.
(note MessageToSlack is just an ordinary .NET class with type, channel and text properties that gets serialized by JSON.Net and appears to give the correct JSON. ws is my websocket connection from the WebSocketSharp nuget package)
JSON:
{
"id": 1,
"type": "message",
"channel": "C6QRKT0EA",
"text": "<http://google.com|to google>"
}
Edit: So it seems if I switch from replying with the web socket connection and instead post to https://slack.com/api/chat.postMessage, it will work correctly, but it's a bit more fiddly to use and the documentation led me to believe that the links should work without needing to jump through that particular hoop. Am I just misreading the docs? Or are docs just not very clear on this point?
Try to enable markdown support by adding "mrkdwn": true to your json
{
"type": "message",
"channel": "C6QRKT0EA",
"text": "<http://google.com|to google>",
"mrkdwn": true
}
Read Message Formatting section. Hope it will help.

Example of creating a draft with an attachment in C#?

I've written a test application in C# that creates a draft message using the new Gmail API. It works fine when the message has no attachment.
I'm moving from the IMAP API and have used the MailBee.NET components with that API. The MailBee.NET components includes a class that produces an RFC 2822 message, so I've re-used this and have Base64-encoded the message and have assigned to the "Raw" property as described here:
https://developers.google.com/gmail/api/guides/drafts
MailMessage msg = new MailMessage();
msg.Subject = "test!";
msg.BodyPlainText = "Test content";
msg.Attachments.Add(#"D:\Trace.log", "Trace.log", Guid.NewGuid().ToString(), null, null, NewAttachmentOptions.Inline, MailTransferEncoding.Base64);
Message m = new Message();
m.Raw = Convert.ToBase64String(msg.GetMessageRawData());
Draft d = new Draft();
d.Message = m;
d = gs.Users.Drafts.Create(d, "me").Execute();
It works fine when no attachment is added, but fails with a 500 response when one is added:
{
"error": {
"errors": [
{
"domain": "global",
"reason": "backendError",
"message": "Backend Error"
}
],
"code": 500,
"message": "Backend Error"
}
}
Could somebody please provide an example of how to do this using the .NET API? The example on the API page is very barebones and doesn't really give much in the way of useful information and the documentation isn't great. It would probably be best to use the Message / MessagePart / MessagePartBody classes included with the .NET Client, however I can't find any clear guidance or examples on their use so don't know where to begin.
Questions:
1) Can anybody provide some example code of creating a draft message with an attachment using the classes within the .NET Client?
2) Is it possible to attach more than one file? The documentation refers to a single file throughout and the Multipart guidance refers to exactly two parts: metadata and attachment.
Providing a sample "raw" field that you're uploading would definitely be helpful to debug (either base64 encoded or just directly).
However this sounds related to:
GMail API : unable to add an attachment in a draft
also about this:
m.Raw = Convert.ToBase64String(msg.GetMessageRawData());
you want to make sure you're using "web safe" (aka "url safe") base64 encoding alphabet from https://www.rfc-editor.org/rfc/rfc4648#section-5
as it says in the docs at the URL you mentioned:
"""
Your application can create drafts using the drafts.create method. The general process is to:
Create a MIME message that complies with RFC 2822.
Convert the message to a URL-safe base64-encoded string.
Create a draft, setting the value of the drafts.message.raw field to the encoded string.
"""
Google APIs use the
Much like for the poster of the other question GmailGuy referred to, this has magically started working overnight. So it must've been a Gmail-side problem after all.
Regarding:
m.Raw = Convert.ToBase64String(msg.GetMessageRawData());
Thanks for the heads-up on this; I had actually encoded it previously but while trying 20 different things to get things working I removed it and forgot to add it back in!
Also, to confirm: yes, you're able to add more than one attachment when you use the raw message approach.

Categories

Resources