I am relatively new to building with Azure's LUIS for chatbot development. I am stuck on testing my bot with a deployed LUIS and QnA Maker. I essentially developed questions on LUIS but how do I get the response based on the questions directed from LUIS? Thanks!
To Invoke QNA maker you could have a look on following code:
Invoke QNA Maker API:
//Take User Input And Validate param
if (string.IsNullOrEmpty(objQnAMakerQuestion.question))
{
validationMessage = new OkObjectResult("Question is required!");
return (IActionResult)validationMessage;
}
// Call QnA API
var jsonContent = JsonConvert.SerializeObject(objQnAMakerQuestion);
var endpointKey = "YourSubscriptionKey";
var qnaMakerURI = "https://YourSource.azurewebsites.net/qnamaker/knowledgebases/YourSubscription/generateAnswer";
using (var client = new HttpClient())
using (var request = new HttpRequestMessage())
{
request.Method = HttpMethod.Post;
request.RequestUri = new Uri(qnaMakerURI);
request.Content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
request.Headers.Add("Authorization", "EndpointKey" + endpointKey);
var response = await client.SendAsync(request);
//Check status code and retrive response
if (response.IsSuccessStatusCode)
{
QnAMakerModelClass objQnAResponse = JsonConvert.DeserializeObject<QnAMakerModelClass>(await response.Content.ReadAsStringAsync());
// var responseBody = await response.Content.ReadAsStringAsync();
foreach (var item in objQnAResponse.answers)
{
QnAMakerAnswer objAnswer = new QnAMakerAnswer();
objAnswer.answer = item.answer;
return new OkObjectResult(objAnswer);
}
}
else
{
var result_string = await response.Content.ReadAsStringAsync();
return new OkObjectResult(result_string);
}
Class Used To Invoke QNA Maker:
public class QnAMakerQuestion
{
public string question { get; set; }
}
public class QnAMakerAnswer
{
public string answer { get; set; }
}
public class Metadata
{
public string name { get; set; }
public string value { get; set; }
}
public class Context
{
public bool isContextOnly { get; set; }
public List<object> prompts { get; set; }
}
public class Answer
{
public List<string> questions { get; set; }
public string answer { get; set; }
public double score { get; set; }
public int id { get; set; }
public string source { get; set; }
public List<Metadata> metadata { get; set; }
public Context context { get; set; }
}
public class QnAMakerModelClass
{
public List<Answer> answers { get; set; }
public object debugInfo { get; set; }
}
For more details you could refer this official document
Hope it will help.
If you have a chatbot that is calling QnAMaker, a slightly easier method would be to use the QnAMaker class. Here's how you would do this in C#:
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
var httpClient = _httpClientFactory.CreateClient();
var qnaMaker = new QnAMaker(new QnAMakerEndpoint
{
KnowledgeBaseId = _configuration["QnAKnowledgebaseId"],
EndpointKey = _configuration["QnAEndpointKey"],
Host = _configuration["QnAEndpointHostName"]
},
null,
httpClient);
_logger.LogInformation("Calling QnA Maker");
var options = new QnAMakerOptions { Top = 1 };
// The actual call to the QnA Maker service.
var response = await qnaMaker.GetAnswersAsync(turnContext, options);
if (response != null && response.Length > 0)
{
await turnContext.SendActivityAsync(MessageFactory.Text(response[0].Answer), cancellationToken);
}
else
{
await turnContext.SendActivityAsync(MessageFactory.Text("No QnA Maker answers were found."), cancellationToken);
}
}
There is a good example on how to integrate QnA into a bot here, along with the official documentation here.
Related
So, I have been struggling with this for a while now, but I am getting a bit further each day. But now I am really stuck.
What I am trying to do:
I have an automation account with a runbook (PowerShell)
I want to create a schedule
Then I want to connect that schedule to the runbook so the runbook is executed at a specific date and time.
I want to achieve this through C# with the Management API of Azure. I found a few webpages that should give me all the information I needed, but it doesn't work.
The first one is this: https://learn.microsoft.com/en-us/rest/api/automation/schedule/create-or-update?tabs=HTTP
I get this to work and I can see the schedule after I ran the code.
The second one is this: https://learn.microsoft.com/en-us/rest/api/automation/job-schedule/create?tabs=HTTP
And here I get stuck. I get a 404 error when I execute the request and I have no idea what I am doing wrong.
The code below is far from perfect, but it's a POC and not the actual application. I want to make it work before I refactor.
This class has all the logic for the schedule:
public class ScheduleLogic
{
const string subscriptionId = "######";
const string resourceGroupName = "######";
const string automationAccountName = "######";
const string tenantId = "######";
const string clientId = "######";
const string clientSecret = "######";
const string resourceId = "https://management.azure.com/";
public string accessToken { get; set; }
// THIS ONE DOESN'T WORK!
public async Task ConnectScheduleToRunbook(RunbookSchedule runbookSchedule)
{
StringContent body = new(JsonSerializer.Serialize(runbookSchedule));
body.Headers.ContentType = new MediaTypeHeaderValue("application/json");
string url = $"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Automation/automationAccounts/{automationAccountName}/jobSchedules/{runbookSchedule.Properties.Schedule.JobScheduleId}?api-version=2019-06-01";
await ExecuteRequest(RequestType.Put, url, body);
}
public async Task CreateSchedule(Schedule schedule)
{
if (string.IsNullOrEmpty(schedule.JobScheduleId))
throw new Exception("Job schedule ID is empty");
StringContent body = new(JsonSerializer.Serialize(schedule));
body.Headers.ContentType = new MediaTypeHeaderValue("application/json");
string url = $"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Automation/automationAccounts/{automationAccountName}/schedules/{schedule.JobScheduleId}?api-version=2019-06-01";
await ExecuteRequest(RequestType.Put, url, body);
}
public async Task DeleteSchedule(string scheduleName)
{
string url = $"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Automation/automationAccounts/{automationAccountName}/schedules/{scheduleName}?api-version=2019-06-01";
await ExecuteRequest(RequestType.Delete, url, null);
}
private async Task ExecuteRequest(RequestType requestType, string url, StringContent? body)
{
HttpResponseMessage response;
using (HttpClient client = new())
{
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {await GetAccessToken()}");
switch (requestType)
{
case RequestType.Put:
if (body == null)
throw new Exception("Body cannot be empty");
response = await client.PutAsync(url, body);
break;
case RequestType.Delete:
response = await client.DeleteAsync(url);
break;
default:
throw new Exception("Unknown request type");
}
if (!response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
throw new Exception(content);
}
}
}
private async Task<string> GetAccessToken()
{
if (!string.IsNullOrEmpty(accessToken))
return accessToken;
AuthenticationContext authContext = new("https://login.microsoftonline.com/" + tenantId);
ClientCredential clientCreds = new(clientId, clientSecret);
AuthenticationResult authResult = await authContext.AcquireTokenAsync(resourceId, clientCreds);
accessToken = authResult.AccessToken;
return accessToken;
}
}
Models:
public class RunbookSchedule
{
public RunbookScheduleProperties Properties { get; set; }
}
public class RunbookScheduleProperties
{
public Schedule Schedule { get; set; }
public Runbook Runbook { get; set; }
public object Parameters { get; set; }
}
public class Runbook
{
public string Name { get; set; }
}
public class Schedule
{
public string Name { get; set; }
[JsonIgnore]
public string JobScheduleId { get; set; }
[JsonPropertyName("properties")]
public ScheduleProperties ScheduleProperties { get; set; }
}
public class ScheduleProperties
{
public string Description { get; set; }
public DateTime StartTime { get; set; }
public DateTime ExpiryTime { get; set; }
public string Frequency { get; set; }
public object AdvancedSchedule => new { };
}
And the unit tests I use to test the logic:
public class ScheduleTests
{
private readonly string jobId = "d52004cc-b7ec-4b9b-99c1-3922492f6e1b-1";
private readonly string runbookName = "RunFunctionApp";
private readonly ScheduleLogic scheduleService;
public ScheduleTests()
{
scheduleService = new();
}
[Fact]
public async Task Should_CreateASchedule()
{
Schedule newScheduleSettings = new()
{
Name = jobId,
JobScheduleId = jobId,
ScheduleProperties = new()
{
Description = "Here is another example",
StartTime = new DateTime(2024, 03, 27, 9, 0, 0, 0),
ExpiryTime = new DateTime(2024, 03, 27, 10, 0, 0, 0),
Frequency = "OneTime"
}
};
await scheduleService.CreateSchedule(newScheduleSettings);
}
[Fact]
public async Task Should_DeleteSchedule()
{
await scheduleService.DeleteSchedule(jobId);
}
[Fact]
public async Task Should_ConnectScheduleAndRunbook()
{
await scheduleService.ConnectScheduleToRunbook(new RunbookSchedule
{
Properties = new()
{
Schedule = new() { Name = jobId, JobScheduleId = jobId },
Runbook = new() { Name = runbookName },
Parameters = new { id = 12 }
}
});
}
}
Somehow I think I am doing something stupid and the fix/answer is really simple.
I tried to mess around with the URL because I think there is a problem with that, not sure why... Just a feeling.
Google did show me some results, but those were for schedules of other services. Runbooks and automation don't seem to be very popular.
ChatGPT comes with older, obsolete solutions. Visual Studio gives a lot of green and even red lines when I try those suggestions.
I am new to Blazor and I am encountering the following issue when trying to post for data with an authentication token : at the time of the API call, an exception is lifted with the message "The JSON value could not be converted to System.Int32. Path: $ | LineNumber: 0 | BytePositionInLine: 1."
Here's the code in my blazor page's code-behind :
public partial class ContactCreate : AuthenticatedPageBase
{
[Inject]
public IContactDataService ContactDataService { get; set; }
[Inject]
public ICountryDataService CountryDataService { get; set; }
public Contact.Post Model { get; set; } = new Contact.Post();
protected string CountryIdString { get; set; } = string.Empty;
protected string TokenString { get; set; } = string.Empty;
public string ErrorMessage { get; set; } = string.Empty;
protected List<Country.ListItem> Countries { get; set; } = new List<Country.ListItem>();
protected async override Task OnInitializedAsync()
{
await base.OnInitializedAsync();
Countries = (await CountryDataService.GetCountryListAsync(Token.Token)).ToList();
TokenString = Token.Token;
}
protected async Task HandleValidSubmit()
{
try
{
Model.CountryId = int.Parse(CountryIdString);
var response = await ContactDataService.PostContactAsync(TokenString, Model);
NavManager.NavigateTo("/contacts");
}
catch(Exception ex)
{
ErrorMessage = ex.Message;
}
}
protected void HandleInvalidSubmit()
{
ErrorMessage = "Le formulaire n'est pas valide. Veuillez réessayer.";
}
}
and here's the relevant code in the data service :
public async Task<int> PostContactAsync(string token, Contact.Post model)
{
var response = await PostAuthenticatedAsync<int>(token, Url, model);
return response;
}
public async Task<T> PostAuthenticatedAsync<T>(string token, string url, object model)
{
var jsonBody = model.ToJson();
var request = new HttpRequestMessage()
{
RequestUri = new Uri(HttpClient.BaseAddress.ToString() + url),
Method = HttpMethod.Post,
Content = jsonBody
};
request.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
var response = await HttpClient.SendAsync(request);
return await response.FromJson<T>(Options);
}
...and the extension method that serializes the object into json :
public static StringContent ToJson(this object o)
{
return new StringContent(JsonSerializer.Serialize(o), Encoding.UTF8, "application/json");
}
Here's the object model that I'm passing through :
public class Contact
{
public class Post
{
[MaxLength(50)]
public string FirstName { get; set; }
[MaxLength(50)]
public string LastName { get; set; }
[MaxLength(50)]
public string CompanyName { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
[MaxLength(20)]
public string PostCode { get; set; }
[MaxLength(60)]
public string Locality { get; set; }
public int CountryId { get; set; }
}
}
And, finally, here's the API method that I'm trying to reach :
[HttpPost]
public async Task<ActionResult> PostContact(Contact.Post model)
{
try
{
var createdId = await _contactRepository.CreateAsync(model);
return Ok(new { Id = createdId });
}
catch (Exception ex)
{
return BadRequest(new { ex.Message });
}
}
Any idea what is happening or what actual exception lies behind this cryptic error message ?
P.S. : I know that there is a question with that exact exception message but it concerns .NET Core while I'm targeting .NET Standard 2.1. I've read it and it visibly doesn't apply to this case.
You are not returning an int (the Id). You're returning an anonymous object with an int property named Id.
Try
return Ok(createdId);
When I get a json response from HttpClient () and try to deselize, my Xamarin application freezes (UI works, but the code after in class ExecuteGetRequest line 15 does not work). What can it be because of?
No errors.
I call the method of obtaining a list of anime user
ShikimoriMain shikimoriMain = new ShikimoriMain();
var UserInformation = await shikimoriMain.GetUserInformation(Convert.ToInt64(UserID));
var UserAnimeList = await shikimoriMain.GetUserAnimeList(Convert.ToInt64(UserID), 1, 5);
string animeName = UserAnimeList.Anime[0].Anime.Name;
ShikimoriMain.GetUserAnimeList
public async Task<ShikimoriUserAnimeList> GetUserAnimeList(long id, int page, int limit)
{
string[] args = new string[] { ShikimoriCategories.UserID + "/" + id + ShikimoriCategories.UserAnimeList + $"?limit={limit}&page={page}" };
return await ExecuteGetRequest<ShikimoriUserAnimeList>(args);
}
ExecuteGetRequest
public async Task<T> ExecuteGetRequest<T>(string[] args) where T : class
{
T returnedObject;
using (var client = new HttpClient())
{
// client.BaseAddress = new Uri($"{httpApiv1}/{args}");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, httpApiv1 + String.Join("/", args));
request.Headers.TryAddWithoutValidation("User-Agent", "Search Anime");
HttpResponseMessage responseMessage = await client.SendAsync(request);
string json = await responseMessage.Content.ReadAsStringAsync(); // successfully get json
returnedObject = JsonConvert.DeserializeObject<T>(json); // after that the code is not executed
return returnedObject;
}
}
ShikimoriUserAnimeList
public class ShikimoriUserAnimeList
{
[JsonProperty()]
public List<GetAnime> Anime { get; set; }
}
public class GetAnime
{
[JsonProperty("id")]
public int ID { get; set; }
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("anime")]
public Anime Anime { get; set; }
}
public class Anime
{
[JsonProperty("id")]
public int ID { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("russian")]
public string NameRU { get; set; }
[JsonProperty("image")]
public AnimeImage AnimeImage { get; set; }
[JsonProperty("kind")]
public string King { get; set; }
[JsonProperty("score")]
public string Score { get; set; }
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("episodes")]
public int Episodes { get; set; }
}
public class AnimeImage
{
[JsonProperty("original")]
public string Original { get; set; }
[JsonProperty("preview")]
public string Preview { get; set; }
[JsonProperty("x96")]
public string ImageX96 { get; set; }
[JsonProperty("x48")]
public string ImageX48 { get; set; }
}
For the sake of completion:
An error was being thrown but was not visible in the device log. Wrapping the JsonConvert.DeserializeObject<T>(json) in a try catch block helped finding the Exceptionbeing thrown.
try
{
returnedObject = JsonConvert.DeserializeObject<T>(json); // after that the code is not executed
return returnedObject;
}
catch (Exception ex)
{
... (debug and fix the error that occurred)
}
I had same problem, I've realized that using HttpClient async will cause deadlock in winforms or xamarin (however it works well with Asp) and I changed these lines
HttpResponseMessage responseMessage = await client.SendAsync(request);
string json = await responseMessage.Content.ReadAsStringAsync(); // successfully get json
Like these (Make them work synchronous):
HttpResponseMessage responseMessage = client.SendAsync(request).Result;
string json = responseMessage.Content.ReadAsStringAsync().Result; // successfully get json
And change your method as default synchronous
Take a look at Here
IMPORTANT - Microsoft is retiring AppCenter Push pretty soon, you can still follow my answer below to implement it. Or you can follow my new post at How to implement Push Notification in Xamarin with Firebase and Apple Push Notification with C# backend on using Firebase & Apple Push Notification. Thank you.
I have been reading https://learn.microsoft.com/en-us/appcenter/push/rest-api and looking over the Internet for example of how to implement this easily but found nothing useful.
I read and implemented this
https://www.andrewhoefling.com/Blog/Post/push-notifications-with-app-center-api-integration. His solution offers very good start, but incomplete.
So I enhanced Andrew Hoefling's solution from above to a full working version and thought it's good to share with fellows Xamarin members in the answer below.
public class AppCenterPush
{
User receiver = new User();
public AppCenterPush(Dictionary<Guid, string> dicInstallIdPlatform)
{
//Simply get all the Install IDs for the receipient with the platform name as the value
foreach(Guid key in dicInstallIdPlatform.Keys)
{
switch(dicInstallIdPlatform[key])
{
case "Android":
receiver.AndroidDevices.Add(key.ToString());
break;
case "iOS":
receiver.IOSDevices.Add(key.ToString());
break;
}
}
}
public class Constants
{
public const string Url = "https://api.appcenter.ms/v0.1/apps";
public const string ApiKeyName = "X-API-Token";
//Push required to use this. Go to https://learn.microsoft.com/en-us/appcenter/api-docs/index for instruction
public const string FullAccessToken = "{FULL ACCESS TOKEN}";
public const string DeviceTarget = "devices_target";
public class Apis { public const string Notification = "push/notifications"; }
//You can find your AppName and Organization/User name at your AppCenter URL as such https://appcenter.ms/users/{owner-name}/apps/{app-name}
public const string AppNameAndroid = "{APPNAME_ANDROID}";
public const string AppNameIOS = "{APPNAME_IOS}";
public const string Organization = "{ORG_OR_USER}";
}
[JsonObject]
public class Push
{
[JsonProperty("notification_target")]
public Target Target { get; set; }
[JsonProperty("notification_content")]
public Content Content { get; set; }
}
[JsonObject]
public class Content
{
public Content()
{
Name = "default"; //By default cannot be empty, must have at least 3 characters
}
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("body")]
public string Body { get; set; }
[JsonProperty("custom_data")]
public IDictionary<string, string> CustomData { get; set; }
}
[JsonObject]
public class Target
{
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("devices")]
public IEnumerable Devices { get; set; }
}
public class User
{
public User()
{
IOSDevices = new List<string>();
AndroidDevices = new List<string>();
}
public List<string> IOSDevices { get; set; }
public List<string> AndroidDevices { get; set; }
}
public async Task<bool> Notify(string title, string message, Dictionary<string, string> customData = default(Dictionary<string, string>))
{
try
{
//title, message length cannot exceed 100 char
if (title.Length > 100)
title = title.Substring(0, 95) + "...";
if (message.Length > 100)
message = message.Substring(0, 95) + "...";
if (!receiver.IOSDevices.Any() && !receiver.AndroidDevices.Any())
return false; //No devices to send
//To make sure in Android, title and message is retain when click from notification. Else it's lost when app is in background
if (customData == null)
customData = new Dictionary<string, string>();
if (!customData.ContainsKey("Title"))
customData.Add("Title", title);
if (!customData.ContainsKey("Message"))
customData.Add("Message", message);
//custom data cannot exceed 100 char
foreach (string key in customData.Keys)
{
if(customData[key].Length > 100)
{
customData[key] = customData[key].Substring(0, 95) + "...";
}
}
var push = new Push
{
Content = new Content
{
Title = title,
Body = message,
CustomData = customData
},
Target = new Target
{
Type = Constants.DeviceTarget
}
};
HttpClient httpClient = new HttpClient();
//Set the content header to json and inject the token
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Add(Constants.ApiKeyName, Constants.FullAccessToken);
//Needed to solve SSL/TLS issue when
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
if (receiver.IOSDevices.Any())
{
push.Target.Devices = receiver.IOSDevices;
string content = JsonConvert.SerializeObject(push);
HttpContent httpContent = new StringContent(content, Encoding.UTF8, "application/json");
string URL = $"{Constants.Url}/{Constants.Organization}/{Constants.AppNameiOS}/{Constants.Apis.Notification}";
var result = await httpClient.PostAsync(URL, httpContent);
}
if (receiver.AndroidDevices.Any())
{
push.Target.Devices = receiver.AndroidDevices;
string content = JsonConvert.SerializeObject(push);
HttpContent httpContent = new StringContent(content, Encoding.UTF8, "application/json");
string URL = $"{Constants.Url}/{Constants.Organization}/{Constants.AppNameAndroid}/{Constants.Apis.Notification}";
var result = await httpClient.PostAsync(URL, httpContent);
}
return true;
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
return false;
}
}
}
To use this, simply do the following from your program
var receiptInstallID = new Dictionary<string, string>
{
{ "XXX-XXX-XXX-XXX", "Android" },
{ "YYY-YYY-YYY-YYY", "iOS" }
};
AppCenterPush appCenterPush = new AppCenterPush(receiptInstallID);
await appCenterPush.Notify("{YOUR_TITLE}", "{YOUR_MESSAGE}", null);
Can't add comment yet, but theres a typo in the above answer
string URL = $"{Constants.Url}/{Constants.Organization}/Constants.AppNameiOS}/{Constants.Apis.Notification}";
Should be
string URL = $"{Constants.Url}/{Constants.Organization}/{Constants.AppNameIOS}/{Constants.Apis.Notification}";
Missing { and IOS constant is capitalized.
Also, in your example to call it, Should be constructed as < guid, string >
var receiptInstallID = new Dictionary<Guid, string>
also needed as just FYI:
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
If you want to send notification for target user (user_ids_target),
public class AppCenterPush
{
User receiver = new User();
public AppCenterPush(Dictionary<string, string> dicInstallIdPlatform)
{
//Simply get all the Install IDs for the receipient with the platform name as the value
foreach (string key in dicInstallIdPlatform.Keys)
{
switch (dicInstallIdPlatform[key])
{
case "Android":
receiver.AndroidDevices.Add(key.ToString());
break;
case "iOS":
receiver.IOSDevices.Add(key.ToString());
break;
}
}
}
public class Constants
{
public const string Url = "https://api.appcenter.ms/v0.1/apps";
public const string ApiKeyName = "X-API-Token";
//Push required to use this. Go to https://learn.microsoft.com/en-us/appcenter/api-docs/index for instruction
public const string FullAccessToken = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
public const string DeviceTarget = "devices_target";
public const string UserTarget = "user_ids_target";
public class Apis { public const string Notification = "push/notifications"; }
//You can find your AppName and Organization/User name at your AppCenter URL as such https://appcenter.ms/users/{owner-name}/apps/{app-name}
public const string AppNameAndroid = "XXXXXX";
public const string AppNameIOS = "XXXXXX";
public const string Organization = "XXXXXXX";
}
[JsonObject]
public class Push
{
[JsonProperty("notification_target")]
public Target Target { get; set; }
[JsonProperty("notification_content")]
public Content Content { get; set; }
}
[JsonObject]
public class Content
{
public Content()
{
Name = "default"; //By default cannot be empty, must have at least 3 characters
}
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("body")]
public string Body { get; set; }
[JsonProperty("custom_data")]
public IDictionary<string, string> CustomData { get; set; }
}
[JsonObject]
public class Target
{
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("user_ids")]
public IEnumerable Users { get; set; }
}
public class User
{
public User()
{
IOSDevices = new List<string>();
AndroidDevices = new List<string>();
}
public List<string> IOSDevices { get; set; }
public List<string> AndroidDevices { get; set; }
}
public async Task<bool> Notify(string title, string message, Dictionary<string, string> customData = default(Dictionary<string, string>))
{
try
{
//title, message length cannot exceed 100 char
if (title.Length > 100)
title = title.Substring(0, 95) + "...";
if (message.Length > 100)
message = message.Substring(0, 95) + "...";
if (!receiver.IOSDevices.Any() && !receiver.AndroidDevices.Any())
return false; //No devices to send
//To make sure in Android, title and message is retain when click from notification. Else it's lost when app is in background
if (customData == null)
customData = new Dictionary<string, string>();
if (!customData.ContainsKey("Title"))
customData.Add("Title", title);
if (!customData.ContainsKey("Message"))
customData.Add("Message", message);
//custom data cannot exceed 100 char
foreach (string key in customData.Keys)
{
if (customData[key].Length > 100)
{
customData[key] = customData[key].Substring(0, 95) + "...";
}
}
var push = new Push
{
Content = new Content
{
Title = title,
Body = message,
CustomData = customData
},
Target = new Target
{
Type = Constants.UserTarget
}
};
HttpClient httpClient = new HttpClient();
//Set the content header to json and inject the token
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Add(Constants.ApiKeyName, Constants.FullAccessToken);
//Needed to solve SSL/TLS issue when
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
if (receiver.IOSDevices.Any())
{
push.Target.Users = receiver.IOSDevices;
string content = JsonConvert.SerializeObject(push);
HttpContent httpContent = new StringContent(content, Encoding.UTF8, "application/json");
string URL = $"{Constants.Url}/{Constants.Organization}/{Constants.AppNameIOS}/{Constants.Apis.Notification}";
var result = await httpClient.PostAsync(URL, httpContent);
}
if (receiver.AndroidDevices.Any())
{
push.Target.Users = receiver.AndroidDevices;
string content = JsonConvert.SerializeObject(push);
HttpContent httpContent = new StringContent(content, Encoding.UTF8, "application/json");
string URL = $"{Constants.Url}/{Constants.Organization}/{Constants.AppNameAndroid}/{Constants.Apis.Notification}";
var result = await httpClient.PostAsync(URL, httpContent);
}
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return false;
}
}
}
After create a function for call it;
public async void PushNotification()
{
var receiptInstallID = new Dictionary<string, string>
{
{"17593989838", "Android" }
};
var customData = new Dictionary<string, string>
{
{"taskId", "1234" }
};
AppCenterPush appCenterPush = new AppCenterPush(receiptInstallID);
await appCenterPush.Notify("Hello", "How are you?", customData);
}
I found some new APIs in Windows 10 Mobile device portal that allows to run applications on user phone .
you can launch this like to see the result : http://{PhoneIP}/api/app/packagemanager/packages
and there's another API to launch applications :
api/taskmanager/app
Starts a modern app
HTTP verb: POST
Parameters
appid : PRAID of app to start, hex64 encoded
package : Full name of the app package, hex64 encoded
I have this code to run applications but doesn't work any idea ?
public class PackageInfoToRun
{
public string appid { get; set; }
public string package { get; set; }
public string PackageFamilyName { get; set; }
}
public class PhoneInstalledPackages
{
public Installedpackage[] InstalledPackages { get; set; }
}
public class Installedpackage
{
public string Name { get; set; }
public string PackageFamilyName { get; set; }
public string PackageFullName { get; set; }
public int PackageOrigin { get; set; }
public string PackageRelativeId { get; set; }
public bool IsXAP { get; set; }
}
private static string Encode(string strn)
{
var toEncodeAsBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(strn);
string appName64 = System.Convert.ToBase64String(toEncodeAsBytes);
appName64 = appName64.Replace(" ", "20%");
return appName64;
}
public async Task<PhoneInstalledPackages> GetInstalledApps()
{
// /api/app/packagemanager/packages
HttpWebRequest webrequest = (HttpWebRequest)WebRequest.Create("http://127.0.0.1/api/app/packagemanager/packages");
string res = "";
webrequest.Method = "GET";
try
{
using (var webresponse = await webrequest.GetResponseAsync())
using (StreamReader loResponseStream = new StreamReader(webresponse.GetResponseStream()))
{
res = loResponseStream.ReadToEnd();
}
var des = JsonConvert.DeserializeObject<PhoneInstalledPackages>(res);
return des;
}
catch (Exception ex)
{
return null;
}
}
public async Task<bool> RunAppAsync(string appid, string packagename)
{
HttpResponseMessage http = new HttpResponseMessage();
string str;
try
{
var package = new PackageInfoToRun()
{
appid = Encode(appid),
package = Encode(packagename)
};
using (HttpClient client = new HttpClient())
{
var serial = JsonConvert.SerializeObject(package);
http = await client.PostAsync("http://127.0.0.1/api/taskmanager/app", new StringContent(serial, Encoding.UTF8, "application/json"));
using (HttpResponseMessage response = http)
{
str = await response.Content.ReadAsStringAsync();
string retur = str;
if (retur.Contains("true"))
{
return true;
}
return false;
}
}
}
catch (Exception ex)
{
return false;
}
}
what's going wrong in my codes ? thanks :)
I used your code with small changes and it works for me.
now RunAppAsync looks like this. Don't forget to specify a PORT.
HttpClient client = new HttpClient();
var serial = JsonConvert.SerializeObject(package);
var result = await client.PostAsync(String.Format("http://127.0.0.1:*PORT*/api/taskmanager/app?appid={0}&package={1}", package.appid, package.package), new StringContent(serial));
Where the package is
var package = new PackageInfoToRun()
{
appid = Encode(app.PackageRelativeId),
package = Encode(app.PackageFullName),
};
And the app variable is an instance of InstalledPackage class.