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.
Related
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.
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 have web app project and an angular 2 project.
I would like use SignalR to send message from the server.
Then I found this article about implementing it.
But I don't know how to send message to the current user.
Code for send message C#:
public class EventHub : Hub
{
public async Task Subscribe(string channel)
{
await Groups.Add(Context.ConnectionId, channel);
var #event = new ChannelEvent
{
Name = $"{Context.ConnectionId} subscribed",
Data = new
{
Context.ConnectionId,
ChannelName = channel
}
};
await Publish(#event);
}
public async Task Unsubscribe(string channel)
{
await Groups.Remove(Context.ConnectionId, channel);
var #event = new ChannelEvent
{
Name = $"{Context.ConnectionId} unsubscribed",
Data = new
{
Context.ConnectionId,
ChannelName = channel
}
};
await Publish(#event);
}
public Task Publish(ChannelEvent channelEvent)
{
Clients.Caller.OnEvent(Constants.AdminChannel, channelEvent);
return Task.FromResult(0);
}
public override Task OnConnected()
{
var #event = new ChannelEvent
{
Name = $"{Context.ConnectionId} connected",
Data = new
{
Context.ConnectionId,
}
};
Publish(#event);
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
var #event = new ChannelEvent
{
Name = $"{Context.ConnectionId} disconnected",
Data = new
{
Context.ConnectionId,
}
};
Publish(#event);
return base.OnDisconnected(stopCalled);
}
}
public static class Constants
{
public const string AdminChannel = "admin";
public const string TaskChannel = "tasks";
}
public class ChannelEvent
{
public string Name { get; set; }
public string ChannelName { get; set; }
public DateTimeOffset Timestamp { get; set; }
public object Data
{
get { return _data; }
set
{
_data = value;
Json = JsonConvert.SerializeObject(_data);
}
}
private object _data;
public string Json { get; private set; }
public ChannelEvent()
{
Timestamp = DateTimeOffset.Now;
}
}
Then in my controller I create IhubContent
private readonly IHubContext _context = GlobalHost.ConnectionManager.GetHubContext<EventHub>();
and invoke my publish event:
private void PublishEvent(string eventName, StatusModel status)
{
_context.Clients.Group(Constants.TaskChannel).OnEvent(Constants.TaskChannel, new ChannelEvent
{
ChannelName = Constants.TaskChannel,
Name = eventName,
Data = status
});
}
But this message sent to all users. Help me to fix this issue and implement code to the send message to the current user.
The IHubContext object your are using has multiple methods, one of which is Clients, of type IHubConnectionContext.
In there you have Groups, Group, Clients & Client methods which abstract what you want to target.
In your case using:
_context.Clients.Client(connectionId).send(message);
should be working fine (you still need to propagate the connectionId though).
N.B.: send is a JS method that should be implemented on client-side.
I’m trying to get Linq to twitter to work on a wp8 App, i’ve looked throughout the available documentation and was not able to figure this out, when trying the demos (which the author says works for wp8) i get errors.
Im using the ApplicationOnlyAuthorizer, my intention is just to be able to read the public tweets (not be able to log in and send tweets).
MainPage.cs
public MainPage()
{
this.InitializeComponent();
UserTweetsWidget = new UserTweetsViewModel("xxxxxxxx", 20);
this.DataContext = this;
}
TweetModel.cs
public class TweetModel
{
public string ScreenName { get; set; }
public string UserName { get; set; }
public string Image { get; set; }
public string Text { get; set; }
public string PublicationDate { get; set; }
}
UserTweetsViewModel.cs
public class UserTweetsViewModel
{
public string Label { get; set; }
public ObservableCollection<TweetModel> Tweets { get; set; }
private const string consumerKey = “xxxxxxx”;
private const string consumerSecret = “xxxxxx”;
private const string twitterAccessToken = “xxxxxxxxxxxxxxxxx”;
private const string twitterAccessTokenSecret = “xxxxxxxxxx”;
public UserTweetsViewModel(string userName, int count)
{
String _n= userName;
int _c= count;
InitializeAsync(_n, _c);
}
private async Task InitializeAsync(string userName, int count)
{
this.Label = string.Format("Tweets by #{0}", userName);
Tweets = await GetTwitterUserTimeLine(userName, count);
}
private async Task<ObservableCollection<TweetModel>> GetTwitterUserTimeLine(string userName, int count)
{
ObservableCollection<TweetModel> result = new ObservableCollection<TweetModel>();
var auth = new ApplicationOnlyAuthorizer
{
CredentialStore = new InMemoryCredentialStore
{
ConsumerKey = consumerKey,
ConsumerSecret = consumerSecret,
OAuthToken= twitterAccessToken,
OAuthTokenSecret= twitterAccessTokenSecret
}
};
await auth.AuthorizeAsync();
TwitterContext twitterCtx = new TwitterContext(auth);
var tweets = twitterCtx.Status.Where(tweet => tweet.ScreenName == userName && tweet.Type == StatusType.Home).Take(count).ToList();
foreach (var item in tweets)
{
TweetModel tweet = new TweetModel()
{
Text = item.Text,
ScreenName = item.User.Name,
UserName = "#" + item.ScreenName,
PublicationDate = Convert.ToString(item.CreatedAt),
Image = item.User.ProfileImageUrl
};
result.Add(tweet);
}
return result;
}
}
Thanks,
Bob
The problem, i think, is you aren't awaiting the AuthorizeAsync method.
First, you need to convert your GetTwitterUserTimeLine into an async method:
private async Task<ObservableCollection<TweetModel>> GetTwitterUserTimeLine(string userName, int count)
Then, you can call auth.AuthorizeAsync, awaiting the call:
await auth.AuthorizeAsync();
This way, the method waits until the authorization is completed, before continue execution.
UPDATE:
To call this method from your viewmodel, You can create a new method in the ViewModel, called InitializeAsync:
private async Task InitializeAsync()
In that method, call the GetTwitterUserTimeLine this way:
Tweets = await GetTwitterUserTimeLine(username, count);
Finally, you need to call the InitializeAsync Method from your viewmodel constructor, and it's going to work.
Hope this helps.