MS Bot Builder: how to set session data to proactive message? - c#

I first send a proactive message to the user via sms channel inside OAuthCallback method
var connector = new ConnectorClient();
Message message = new Message();
message.From = new ChannelAccount { Id = Constants.botId, Address = "+12312311", ChannelId = "sms", IsBot = true };
message.To = new ChannelAccount { Id = newUserId, Address = "+18768763", ChannelId = "sms", IsBot = false };
message.Text = $"How are you doing? ";
message.Language = "en";
connector.Messages.SendMessage(message);
IBotData myDataBag = new JObjectBotData(message);
myDataBag.UserData.SetValue("Username", "Bob");
myDataBag.PerUserInConversationData.SetValue("Newuser", "yes");
Then in my main Dialog.cs I try to access it
public static readonly IDialog<string> dialog = Chain
.PostToChain()
.Switch(new Case<Message, IDialog<string>>((msg) =>
{
var regex = new Regex("hello$", RegexOptions.IgnoreCase);
return regex.IsMatch(msg.Text);
},
(ctx, msg) =>
{
// Clearing user related data upon logout
string isnewuser = ctx.PerUserInConversationData.TryGetValue("Newuser");
string username = ctx.UserData.TryGetValue("Username");
return Chain.Return($"Welcome {username}");
}))
.Unwrap()
.PostToUser();
I receive the message on my phone. However, I am not able to get back the username and newuser session data saved inside OAuthCallback.
I suspect that this is happening because the proactive message does not have conversationId set. And the conversationId must differ somehow.
so how can I get it to set session data to my proactive message in the future conversation?

In proactive's scenarios, the conversation Id for channels change when the user answers your message, it's like a new session, we do this type of features using the channel data, but this solution is only for small data, you also have the option of creating a persistent session using the same table storage that the bot framework is using to save the dialog context, in this solution you can create another table to store your data serialized, and the final one is a persistent session using a distributed cache like Redis, but this type of services are expensive, so you have to analyze which type of solution is the right one for your solution, but as a start, you should try with the Channel Data property and if it works, you can analyze another approach
I hope I have been helpful

Not sure if this is still relevant after four years, but I think I figured this out in Access UserProfile from NotifyBot. Check it out.

Related

How to send a 'conversationUpdate' to Microsoft Teams from bot manually?

I have a bot written with the help of bot framework v4. The bot is integrated with Microsoft Teams. I want to send a welcome message to the user when the user installed the bot and joins the 1:1 conversation. In Teams the conversationUpdate is fired exactly once (this is when the suer joins the 1:1 conversation) and then never again for that user. My idea was to write a function that is triggered by a chat message to send the updateConversation activity manually to debug the welcome message.
I failed so far and got a
BadArgument: Unknown activity type exception.
I have tried using the Microsoft.Bot.Builder.Teams nuget using the ConnectorClient to send the conversationUpdate activity to the conversation.
Also I set up a console application and tried using the v3/directline/conversations/{conversationId}/activities and got a Forbidden error.
private async Task SendConversationUpdateToTeamsAsync(ITurnContext turnContext, CancellationToken cToken = default)
{
var connectorClient = turnContext.TurnState.Get<IConnectorClient>();
var conversationUpdateMessage = new Activity
{
Type = ActivityTypes.ConversationUpdate,
Id = turnContext.Activity.Id,
ServiceUrl = turnContext.Activity.ServiceUrl,
From = turnContext.Activity.From,
Recipient = turnContext.Activity.Recipient,
Conversation = turnContext.Activity.Conversation,
ChannelData = turnContext.Activity.ChannelData,
ChannelId = turnContext.Activity.ChannelId,
Timestamp = turnContext.Activity.Timestamp,
MembersAdded = new List<ChannelAccount>
{
turnContext.Activity.From,
turnContext.Activity.Recipient
},
};
var result = await connectorClient.Conversations.SendToConversationAsync(conversationUpdateMessage, cToken);
}
I expect that sending a conversationUpdate manually to debug the behavior in Teams works. Creating new users in the office portal and installing the bot for them to debug the conversationUpdate behavior is no option for me, because it is to time consuming. If there is another workaround to trigger the conversationUpdate in Teams please let me know.
I'm not sure of a way to force a ConversationUpdate to be sent in the way you're attempting to. Instead, I'd just throw something like this in OnMessageAsync():
if (turnContext.Activity.Text == "fakeConversationUpdate")
{
var fakeTurnContext = new TurnContext(turnContext.Adapter, MessageFactory.Text(string.Empty));
fakeTurnContext.Activity.AsConversationUpdateActivity();
fakeTurnContext.Activity.Type = ActivityTypes.ConversationUpdate;
fakeTurnContext.Activity.MembersAdded = new List<ChannelAccount>()
{
new ChannelAccount()
{
Id = "fakeUserId",
Name = "fakeUserName"
}
};
await OnConversationUpdateActivityAsync(new DelegatingTurnContext<IConversationUpdateActivity>(fakeTurnContext), cancellationToken);
}
Then to debug, you just write "fakeConversationUpdate" (which you can change/customize) to the bot in chat and it will send your fakeTurnContext (which you can change/customize) through OnConversationUpdateActivityAsync()

C# Exchange web service - send and immediately retrieve sent message

The system I'm working has a requirement where the MimeContent of a just-sent email is stored in a local database. My understanding of how Exchange works is that it will create the MimeContent on the server and I cannot access it unless I query the service for that just-sent message.
So, the steps I take are:
-- Send the email and get it's Id
message.SendAndSaveCopy();
return message.Id.UniqueId;
-- Use the new id to get the just-sent EmailMessage
ExchangeService exchangeService = ExchangeService;
var properties = new List<PropertyDefinitionBase>
{
ItemSchema.MimeContent
};
EmailMessage message = EmailMessage.Bind(exchangeService, new ItemId(messageId), new PropertySet(BasePropertySet.IdOnly, properties));
When this code runs without interruption, it works. The Id returned is still valid (message is in the outbox folder perhaps) and I get the result. However, if I slow it down for even a second, the Id is no longer valid (I guess it's now moved into the sent folder).
I cannot leave it like this as there is no guarantee I will get back to the server in time to retrieve the message with that Id.
If there is a solution that involves me not having to query the service for the message again, that'd be swell. However, if not, is there a way I can use the Id and ChangeKey to get the just-sent email?
Eventually answered my own question. I did this by adding a custom property onto each email that I control. I then use that same value to search for the email. I had some issues with the email moving between folders inbetween the queries but it's sorted now.
Define this somewhere, I have it within my EmailProvider class:
private readonly PropertyDefinitionBase _TempEmailId = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.PublicStrings, "TempEmailId", MapiPropertyType.String);
Then add it just before you send the email:
string messageId = null;
if (getTempId)
{
messageId = Guid.NewGuid().ToString();
message.SetExtendedProperty((ExtendedPropertyDefinition) _TempEmailId, messageId);
}
message.SendAndSaveCopy();
return messageId;
Finally, use the messageId to get the MimeContent (or any other properties you desire):
/// <summary>
/// Get the mime content for an email just sent
/// </summary>
/// <param name="messageId"></param>
/// <returns></returns>
public byte[] GetJustSentMimeContent(string messageId)
{
ExchangeService exchangeService = ExchangeService;
// Use the following search filter to get mail with TempEmailId set to what we just got
var searchCriteria = new SearchFilter.IsEqualTo(_TempEmailId, messageId);
var itemView = new ItemView(1) {PropertySet = new PropertySet(BasePropertySet.IdOnly)};
byte[] GetMimeContent(WellKnownFolderName folder)
{
var items = exchangeService.FindItems(folder, searchCriteria, itemView);
if (items.TotalCount > 0)
{
// if it's still in there, try and load the properties for it
exchangeService.LoadPropertiesForItems(items,
new PropertySet(BasePropertySet.IdOnly,
ItemSchema.MimeContent));
var emailMessage = (EmailMessage) items.First();
// if the content has been loaded, return it
if (emailMessage.MimeContent != null)
return emailMessage.MimeContent.Content;
}
return null;
}
// check the drafts folder to see if it's still in there
var mimeContent = GetMimeContent(WellKnownFolderName.Drafts);
if (mimeContent != null)
return mimeContent;
// if at this point, either:
// - email was moved to SentItems before the first search was done
// - or email was found in Drafts but then moved to SentItems but before the MimeContent could be loaded. Need to restart the process and find the email in SentItems instead
// should be in here (sentItems) now, try and load the properties for it
return GetMimeContent(WellKnownFolderName.SentItems);
}

Validating spam, temporary emails and emails with invalid domains

recently I have noticed a lot of users being registered to my site and they all use disposable emails, temporary email addresses or fake emails to register on the site...
What I have done to partially prevent that is I added mailgun email verification which tells me whether an email is valid or not , disposable one or not...
This filtered out like 95% of the fake emails and users, however there is still a small portion of them which go "unnoticed". What I have figured out to do are following things via code:
public static bool IsValidEmail(string email)
{
RestClient client = new RestClient();
client.BaseUrl = new Uri("https://api.mailgun.net/v3");
client.Authenticator = new HttpBasicAuthenticator("api", "");
RestRequest request = new RestRequest();
request.Resource = "/address/validate";
request.AddParameter("address", email);
var res = new JavaScriptSerializer().Deserialize<RootObject>(client.Execute(request).Content);
if (res.is_disposable_address || res.is_valid==false)
return false;
return true;
}
This is one step via mailgun that I described previously... And second method to prevent spam/fake users is that I have set up manually a table in my DB with blacklisted domains to which registration won't be allowed... And the logic for that is:
public static bool IsValid(string email)
{
using (var ctx = new myEntities())
{
var blacklistedProviders = ctx.BlacklistedDomains.ToList();
foreach (var item in blacklistedProviders)
{
if (email.ToLower().Contains(item.DomainName.ToLower()))
return false;
}
}
return true;
}
However I don't think this will prevent them from doing more malicious stuff on the website...
I have also added google recaptcha to harden the process of registration for spammers, that does the job well too....
So my questions here are:
Are there any other ways that I can validate the email to see whether it is disposable or invalid email
What other way could I implement to prevent users from creating fake accounts?
Can someone help me out with this?

Sendgrid email not sent + no "recent activity" + no exceptions

I am configuring Sendgird for the first time and I want to send emails using c#.
This is the code I am using, and I know for a fact that it is working because I can use it with another sendgird account that was configured by the IT department:
public static async void SendEmailsToUser(string[] mailAddresses, string subject, string fullHtmlEmail)
{
var sendgridMessage = new SendGrid.SendGridMessage();
foreach (var emailAddress in mailAddresses)
sendgridMessage.AddTo(emailAddress);
sendgridMessage.From = new MailAddress("blah#blah.com", "blah");
sendgridMessage.Subject = subject;
sendgridMessage.Html = fullHtmlEmail;
var transportWeb = new SendGrid.Web("sendgrid_key");
await transportWeb.DeliverAsync(sendgridMessage);
}
The code generates no exceptions, but I don't receive the test email and the sendgrid webpage shows no trace of recent activity.
The only idea that comes to mind is that maybe I have not generated the API key correctly? (Settings -> API keys -> Generate new -> full permissions)

How can I get the picture of a Facebook event using server side code?

I'm writing a web app that pulls events data from Facebook, and I can get a lot of the information using an app token, but not the picture, which requires a client token, as documented here: https://developers.facebook.com/docs/graph-api/reference/v2.2/event/picture
I want the code that grabs the event data to run automatically on a server at regular intervals, without requiring a user to log in to their Facebook account.
Is there a way I can get a client token without user intervention? If not, is there another way I can get the event picture?
This is the code I am using to get the event data, using C# and JSON.Net (This gets a list of events created by the specified user - ResortStudios):
var fb = new FacebookClient();
dynamic result = fb.Get( "oauth/access_token", new
{
client_id = "XXXXXXXXXXX",
client_secret = "XXXXXXXXXXXXXXXXXXXXXXXXX",
grant_type = "client_credentials"
} );
var apptoken = result.access_token;
fb = new FacebookClient(apptoken);
result = fb.Get("ResortStudios/events");
JObject events = JObject.Parse(result.ToString());
JArray aEvents = (JArray)events["data"];
string s = aEvents.ToString();
List<fbEvent> lEvents = JsonConvert.DeserializeObject<List<fbEvent>>(s);
I've not tried this but something occurred to me that might work for you. Have you considered something like storing it a non-persistent data store like session state? Then, using the Facebook SDK for .NET, you create an ActionResult for UserInfo, like below. (I know this isn't directly applicable but I hoped it might get you thinking.)
//http://facebooksdk.net/docs/web/ajax-requests/
public ActionResult UserInfo()
{
var accessToken = Session["AccessToken"].ToString();
var client = new FacebookClient(accessToken);
dynamic result = client.Get("me", new { fields = "name,id" });
return Json(new
{
id = result.id,
name = result.name,
});
}

Categories

Resources