How to get ChatBot user Latitude and Longitude using BotFramework - geolocation

I am building a chatbot using Microsoft bot framework. I am stuck on how to get latitude and longitude values from "activity".
Is there a way to get latitude and longitude info from "activity"?
There is a similar question exists already here but that does not give an answer to this question.
I have tried Microsoft..Bot.Builder.Location and that does not give lat and long of the user but helps in getting info from the user and validate it.
Any suggestion would be appreciated.

You can use this free REST API to get the Latitude and Longitude of the place, this REST API works on Wi-Fi Traingulation.
REST API for getting Latitude and Longitude of a place
Make a model class corresponding to the output of the REST API used for deserialization:
public class PlaceGeography
{
public string ip { get; set; }
public string country_code { get; set; }
public string country_name { get; set; }
public string region_code { get; set; }
public string region_name { get; set; }
public string city { get; set; }
public string zip_code { get; set; }
public string time_zone { get; set; }
public float latitude { get; set; }
public float longitude { get; set; }
public int metro_code { get; set; }
}
Now, simply make a GET request from your C# code in Bot Framework and get result like this:
public async static Task<HttpResponseMessage> GetCoOrdinates()
{
string responseJSON = string.Empty;
PlaceGeography obj = new PlaceGeography();
using (HttpClient client = new HttpClient())
{
string URI = $"http://freegeoip.net/json/";
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using (HttpResponseMessage msg = await client.GetAsync(URI))
{
if (msg.IsSuccessStatusCode) //if HTTP 200 then
{
responseJSON = await msg.Content.ReadAsStringAsync();
JsonConvert.PopulateObject(responseJSON, obj); // Deserialize model class object and get latitude and longitude
return msg;
}
else
return msg;
}
}
}

For Messenger on mobile, the user can specifically send their location using the built-in functionality. Once they do that, you can receive the values from the incoming message:
var location = activity.Entities?.FirstOrDefault(e => e.Type == "Place");
if (location != null)
{
var latitude = location.Properties["geo"]?["latitude"]?.ToString();
var longitude = location.Properties["geo"]?["longitude"]?.ToString();
}

Related

Slack API Slash Command sent to ASP.NET Core MVC Web App Issue

I am having an issue with my web api in ASP.NET Core MVC receiving the POST request from the Slack API when I attempt to execute a slash command. I do have the app hosted on a dedicated server (with a valid SSL Cert) and it is reachable by the Slack API.
Here is the method I am using to receive the POST request from the Slack API:
[HttpPost("slash/dispatch")]
[Consumes("application/x-www-form-urlencoded")]
public async Task<ActionResult<string>> ReceiveSlashDispatchEvent([FromForm] string data)
{
try
{
// string data;
// using (var sr = new StreamReader(Request.Body))
// {
// data = await sr.ReadToEndAsync();
// }
await _slackDispatchBotService.PostMessage(new SlackChatSendRequest
{
Channel = "XXXXXXX",
Message = data
});
}
catch (Exception e)
{
return Ok($"Error: {e.Message}");
}
return Ok();
}
Now if I don't declare anything in the method signature, it actually does enter it correctly. I have been successful in retrieving the request body using that commented section and directly reading in the Stream.
Here is a class definition that I have used in the method body previously (and it would fail each time):
public class SlackSlashDispatchEvent
{
public string Token { get; set; }
[JsonPropertyName("team_id")]
public string TeamId { get; set; }
[JsonPropertyName("team_domain")]
public string TeamDomain { get; set; }
[JsonPropertyName("enterprise_id")]
public string EnterpriseId { get; set; }
[JsonPropertyName("enterprise_name")]
public string EnterpriseName { get; set; }
[JsonPropertyName("channel_id")]
public string ChannelId { get; set; }
[JsonPropertyName("channel_name")]
public string ChannelName { get; set; }
[JsonPropertyName("user_id")]
public string UserId { get; set; }
[JsonPropertyName("user_name")]
public string UserName { get; set; }
public string Command { get; set; }
public string Text { get; set; }
[JsonPropertyName("response_url")]
public string ResponseUrl { get; set; }
[JsonPropertyName("trigger_id")]
public string TriggerId { get; set; }
}
I have also accessed the Request.QueryString field, which is always empty. Does anyone have a suggestion of where I should investigate further?
So far it seems to be that the framework I'm using is failing to successfully transform the request body data to whatever type I define in the method signature. Unfortunately, I'm having a hard time determining what the next troubleshooting steps are.
EDIT
While I have a strong feeling that there is a more graceful way to do this, I have implemented a solution. I do use the stream reader to grab the request body.
string data;
using (var sr = new StreamReader(Request.Body))
{
data = await sr.ReadToEndAsync();
}
I then wrote this method to parse the URLEncoded query parameters (that for some reason get sent in the request body from the Slack API) to convert it to my desired class:
public static SlackSlashDispatchEvent ParseQueryString(string queryString)
{
var dict = new Dictionary<string, string>();
var items = queryString.Split('&');
foreach (var item in items)
{
var keyValue = item.Split('=');
var key = HttpUtility.UrlDecode(keyValue[0]);
var value = HttpUtility.UrlDecode(keyValue[1]);
dict.Add(key, value);
}
var stringifyDict = JsonSerializer.Serialize(dict);
return JsonSerializer.Deserialize<SlackSlashDispatchEvent>(stringifyDict)!;
}
Serializing and then deserializing the Dictionary feels inefficient, but it's the best I could come up with for now.

Third party service integration

I'm working on a reply from email feature in my app, and I'm using mandrillapp.com. How it works now :
1. User replies from their email to something#something.com
2. Mandril receives the mail and sends the POST request to preconfigured endpoint of my service
3. I process that post request (create internal app message)
With regards to step 3, I have basically code that should create internal app message wrapped in the begin/rescue. So any potential errors would get reported to me, which they have in the past.
However now I've encountered a user who claims that the email has been sent (step 1), I contacted the mandrill support they say they have processed it and send to my endpoint (step 2).
So that only leaves me to see what happened in the step 3, within that specific time range. My app is on heroku, and my logs stored on AWS I could go back in past to that specific time and I could see a successful POST request from mandrill side.
What can I do to catch this case moving forward? And fix it of course, because I have no idea what is going on now.
I was thinking create a model named Mandrill hooks, which would have params column which would be a serialized hash and it would save all the params which came to my endpoint from mandrill.
Then I could check counts of webhooks received by my account and the ones sent by mandrill, and hopefully I spot 1 with different count, and since I will have the params data in my db, I'd be able to reproduce the case.
Or is there more wise approach, as this looks naive even to me?
I think there may be a wiser approach that enables you to have more control over everything and this sounds extremely similar to what I recently accomplished and I answered a similar question here. Does this help at all or is there some more specific help that you would like?
** Edit: Linked post/code below
I am running my Mandrill Webhook handling as an API project in VS and this is how I am have gotten it up and running:
[HttpPost]
public string Post()
{
/* Every Mandrill webhook uses the same general data format, regardless of the event type.
* The webhook request is a standard POST request with a single parameter (currently) - 'mandrill_events'. */
string validJson = HttpContext.Current.Request.Form["mandrill_events"].Replace("mandrill_events=", ""); //"mandrill_events=" is not valid JSON. If you take that out you should be able to parse it. //http://stackoverflow.com/questions/24521326/deserializing-mandrillapp-webhook-response
List<MandrillEvent> mandrillEventList = JsonConvert.DeserializeObject<List<MandrillEvent>>(validJson);
foreach (MandrillEvent mandrillEvent in mandrillEventList)
{
if (mandrillEvent.msg.email != null)
{
DataLayer.ReportingData.EmailSave(mandrillEvent); //Saves MandrillEvent email to database and sets a messageId for datalayer
}
}
foreach (MandrillEvent mandrillEvent in mandrillEventList)
{
DataLayer.ReportingData.MandrillEventSave(mandrillEvent); //Saves MandrillEvent object to database
}
return "DONE";
}
I then took the documented (and undocumented) JSON parameters of "mandrill_event" and used json2csharp.com to generate the C# properties. I created a class called "MandrillEvent.cs" and put these within:
public class SmtpEvent
{
public int ts { get; set; }
public DateTime SmtpTs { get; set; }
public string type { get; set; }
public string diag { get; set; }
public string source_ip { get; set; }
public string destination_ip { get; set; }
public int size { get; set; }
public int smtpId { get; set; } //added for datalayer
}
public class Msg
{
public int ts { get; set; }
public DateTime MsgTs { get; set; }
public string _id { get; set; }
public string state { get; set; }
public string subject { get; set; }
public string email { get; set; }
public List<object> tags { get; set; }
public List<object> opens { get; set; } //an array of containing an item for each time the message was opened. Each open includes the following keys: "ts", "ip", "location", "ua"
public List<object> clicks { get; set; } //an array containing an item for each click recorded for the message. Each item contains the following: "ts", "url"
public List<SmtpEvent> smtp_events { get; set; }
public List<object> resends { get; set; } //not currently documented on http://help.mandrill.com/entries/58303976-Message-Event-Webhook-format
public string _version { get; set; }
public string diag { get; set; } //for bounced and soft-bounced messages, provides the specific SMTP response code and bounce description, if any, received from the remote server
public int bgtools_code { get; set; } //Is it this? for bounced and soft-bounced messages, a short description of the bounce reason such as bad_mailbox or invalid_domain. (not currently documented but in JSON response)
public string sender { get; set; }
public object template { get; set; }
public string bounce_description { get; set; }
public Msg()
{
tags = new List<object>();
opens = new List<object>();
clicks = new List<object>();
smtp_events = new List<SmtpEvent>();
smtp_events.Add(new SmtpEvent());
resends = new List<object>();
}
}
public class MandrillEvent
{
public string #event { get; set; }
public string _id { get; set; }
public Msg msg { get; set; }
public int ts { get; set; }
public DateTime MandrillEventTs { get; set; }
public int messageId { get; set; } //added for datalayer
public List<string> SingleMandrillEventData { get; set; } //added for Reporting
public MandrillEvent()
{
SingleMandrillEventData = new List<string>();
msg = new Msg();
}
}
You now have your "mandrill_events" JSON object as a functioning C# object!! Did this help or do you need some more clarification/help?

How to signup/login Parse User from authenticated Twitter oauth_token in .NET?

I've used the OAuth1Authenticator class from the Xamarin.Auth component library to allow users to login via Twitter. It authenticates correctly and gets me a response which has the following: oauth_token, oauth_token_secret, user_id, screen_name, oauth_consumer_key, oauth_consumer_secret.
Here is my code
OAuth1Authenticator twitterAuthenticator = new OAuth1Authenticator(Constants.Twitter.CONSUMER_KEY, Constants.Twitter.CONSUMER_SECRET, new Uri(Constants.Twitter.REQUEST_TOKEN_URL), new Uri(Constants.Twitter.AUTHORIZE_URL), new Uri(Constants.Twitter.ACCESS_TOKEN_URL), new Uri(Constants.Twitter.CALLBACK_URL));
twitterAuthenticator.Completed += async (sender, e) =>
{
if (!e.IsAuthenticated)
{
return;
}
string oauth_token = e.Account.Properties["oauth_token"].ToString();
The question is how do I then use that response to signup/signin a Parse User ? i.e. I want a ParseUser created on the parse database via the Twitter token and the session should be taken care of, same way it works for sign-via-Facebook using ParseFacebookUtils class
Problem is Parse doesn't support Login via Twitter in Xamarin, however, I believe parse does support any type of 3rd party authentication in an alternative way as shown below but I don't know how to do it.
Here are the most relative links
https://parse.com/tutorials/adding-third-party-authentication-to-your-web-app but the problem in this link is that it's made as a webpage button, don't know how to use that on a mobile, and it's for GitHub don't know how to use it for Twitter instead (Twitter is only OAuth1)
http://blog.parse.com/2013/12/03/bring-your-own-login/ This is exactly what I need but it needs a session Token, doesn't work with the oauth_tokens that twitter responds back to me, hence don't know how to use the method mentioned in the link
https://github.com/auth0/rules/blob/master/parse.md This looks like the solution, however I don't know how to use it, it does show the twitter icon so it should work with twtiter but how do I get that to work in .NET Update: I've found this xamarin component http://components.xamarin.com/view/Auth0Client which gets me closer to use the method mentioned in the first link in this paragraph, yet I'm still lost and don't know how to link autho0 to parse
All in all, I'm lost in this maze and really wish anyone could help me out.
I don't have a Twitter account so I can't test this but it looks like your POST DTO would be this:
public class TwitterAuthRequest
{
public AuthData authData { get; set; }
}
public class Twitter
{
public string id { get; set; }
public string screen_name { get; set; }
public string consumer_key { get; set; }
public string consumer_secret { get; set; }
public string auth_token { get; set; }
public string auth_token_secret { get; set; }
}
public class AuthData
{
public Twitter twitter { get; set; }
}
With a response DTO like this:
public class TwitterAuthResponse
{
public string username { get; set; }
public string createdAt { get; set; }
public string updatedAt { get; set; }
public string objectId { get; set; }
public string sessionToken { get; set; }
public AuthData authData { get; set; }
}
public class Twitter
{
public string id { get; set; }
public string screen_name { get; set; }
public string consumer_key { get; set; }
public string consumer_secret { get; set; }
public string auth_token { get; set; }
public string auth_token_secret { get; set; }
}
public class AuthData
{
public Twitter twitter { get; set; }
}
Don't forget to put in the headers:
("X-Parse-Application-Id", ApplicationId)
("X-Parse-REST-API-Key", ApplicationKey)
Source: https://parse.com/docs/rest#users
EDIT:
I have a created a draft for how you would use the DTO's:
public static class TwitterLoginProvider
{
public static Task<ServiceResponse<TwitterAuthResponse>> Login(
Twitter twitterInfo,
string applicationId,
string apiKey,
IRestClient restClient)
{
var request = new TwitterAuthRequest ()
{
authData = new AuthData ()
{
twitter = twitterInfo
}
};
restClient.AddHeader ("X-Parse-Application-Id", applicationId);
restClient.AddHeader ("X-Parse-REST-API-Key", apiKey);
return restClient.PostAsync<TwitterAuthResponse>("https://api.parse.com/1/users", request, Format.Json);
}
}
When you get the response from Xamarin.Auth, use that info to create a Twitter object and pass it to the IRestClient. As a response from the service you will get a response with the session information.
IRestClient interface: https://github.com/sami1971/SimplyMobile/blob/master/Core/SimplyMobile.Web/IRestClient.cs
Sample implementation: https://github.com/sami1971/SimplyMobile/blob/master/Core/SimplyMobile.Web/RestClient.cs
Alternatively you could use RestSharp: http://restsharp.org/

MVC 4 Web API NullException Error (Noobie)

I'm working on my first MVC 4 app, following the MVC First Web API Tutorial on Asp.net. I've left the names the same, but changed the model and controller code. Here's my model:
public class Product
{
public string SID { get; set; }
public string name { get; set; }
public string givenName { get; set; }
public string sn { get; set; }
public string mail { get; set; }
public string telephoneNumber { get; set; }
public string mobile { get; set; }
public string otherMobile { get; set; }
public string title { get; set; }
public string Manager { get; set; }
public DateTime whenChanged { get; set; }
}
public class ProductModel
{
public ProductModel()
{
ProductList = new List<Product>();
}
public IList<Product> ProductList { get; set; }
}
And here's my APIcontroller:
public class ProductsController : ApiController
{
ProductModel products = new ProductModel();
public IEnumerable<Product> GetAD()
{
DirectoryEntry domainRoot = new DirectoryEntry(LDAP_server);
DirectorySearcher searcher = new DirectorySearcher(searchStr);
SearchResultCollection results = searcher.FindAll();
foreach (SearchResult srchResult in results)
{
DirectoryEntry dirEntry = srchResult.GetDirectoryEntry();
if (dirEntry.Properties["givenName"].Value != null && dirEntry.Properties["sn"].Value != null && !dirEntry.Parent.Name.Contains("Terminated"))
{
products.ProductList.Add(new Product()
{
SID = dirEntry.Properties["sid"].Value.ToString(),
name = dirEntry.Properties["name"].Value.ToString(),
givenName = dirEntry.Properties["givenName"].Value.ToString(),
sn = dirEntry.Properties["sn"].Value.ToString(),
mail = dirEntry.Properties["mail"].Value.ToString(),
telephoneNumber = dirEntry.Properties["telephoneNumber"].Value.ToString(),
mobile = dirEntry.Properties["mobile"].Value.ToString(),
otherMobile = dirEntry.Properties["otherMobile"].Value.ToString(),
title = dirEntry.Properties["title"].Value.ToString(),
Manager = dirEntry.Properties["Manager"].Value.ToString(),
whenChanged = Convert.ToDateTime(dirEntry.Properties["whenChanged"].Value.ToString()),
});
}
}
return products.ProductList;
}
}
I'm getting the NullException on 'products.ProductList.Add(new Product()', am I missing something simple? Please forgive my coding, as I'm just trying to get this up and running, thanks.
the problem mostly likely is dealing with dirEntry, not Web API itself. rather than introduce LDAP into this, just create a bunch of dummy products to return.
FYI... there is also a memory leak issue with the use of LDAP objects. They need to be properly disposed of, both along the happy path and if an exception is thrown.
I'm an idiot. 'sid' is not the correct property name from AD, it is 'objectSid', thus always returning a null. I knew it was something simple.

How to get Json Data from IPinfodb.com with MVC WebService/Method

I want to get User's Location based on IP. When user enters website
I used to do it with XMLHTTPREquest in classic asp
how to do it with .net MVC.
I am new to .net
A combination of WebClient and JavaScriptSerializer classes could help you. As always start by defining a class that will represent your model:
public class LocationResult
{
public string Ip { get; set; }
public string Status { get; set; }
public string CountryCode { get; set; }
public string CountryName { get; set; }
public string RegionCode { get; set; }
public string RegionName { get; set; }
public string City { get; set; }
public string ZipPostalCode { get; set; }
public float Latitude { get; set; }
public float Longitude { get; set; }
}
and then call the service and deserialize the JSON result back to your model:
public LocationResult GetLocationInfo(string ip)
{
using (var client = new WebClient())
{
// query the online service provider and fetch the JSON
var json = client.DownloadString(
"http://ipinfodb.com/ip_query.php?ip=" + ip +
"&output=json&timezone=false"
);
// use the JavaScriptSerializer to deserialize the JSON
// result back to a LocationResult
var serializer = new JavaScriptSerializer();
return serializer.Deserialize<LocationResult>(json);
}
}

Resources