How to retrieve a PNS Handle from the Platform Notification Service? - c#

I have an Xamarin.iOS application that is connecting an Azure backend service and I want my service to send notifications to the client applications.
The Microsoft documentation explains how to set up the Notification Hub for different scenario. I think I am getting most of it, however I am not sure I understood the very first part, which is for the iOS application to Retrieve PNS Handle from the Platform Notification Service, as shown in the following picture:
It looks like this is some task that must be performed by the client application alone and then communicate this to the backend service for the registration.
I have a feeling that it happens at the 10th step of this section, when iOS calls the application back on the method RegisteredForRemoteNotifications. In that callback, the application is given a deviceToken:
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
Hub = new SBNotificationHub(Constants.ListenConnectionString, Constants.NotificationHubName);
Hub.UnregisterAllAsync (deviceToken, (error) => {
if (error != null)
{
System.Diagnostics.Debug.WriteLine("Error calling Unregister: {0}", error.ToString());
return;
}
NSSet tags = null; // create tags if you want
Hub.RegisterNativeAsync(deviceToken, tags, (errorCallback) => {
if (errorCallback != null)
System.Diagnostics.Debug.WriteLine("RegisterNativeAsync error: " + errorCallback.ToString());
});
});
}
Question
Is that deviceToken the PNS Handle that I need to send to the backend service to start the registration process? If not, how am I supposed to contact the PNS to get the Handle?

The information is in the documentation but not in an obvious form for a C# developer.
In Objective-C, the deviceToken is provided by the iOS application, as mentioned by #LucasZ, after it got registered against the PNS.
However I can't just send this deviceToken right away as it will not be accepted by the AppleRegistrationDescription class used in my Service.
It took me a while to get more familiar with Objective-C to figure out that this token was actually transformed before being sent to Azure:
NSSet* tagsSet = tags?tags:[[NSSet alloc] init];
NSString *deviceTokenString = [[token description]
stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#"<>"]];
deviceTokenString = [[deviceTokenString stringByReplacingOccurrencesOfString:#" " withString:#""] uppercaseString];
I have implemented something similar in C#:
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
string pnsHandle = deviceToken.Description
.Replace("<", string.Empty)
.Replace(">", string.Empty)
.Replace(" ", string.Empty)
.ToUpper();
Hub = new SBNotificationHub(Constants.ListenConnectionString, Constants.NotificationHubName);
Hub.UnregisterAllAsync (pnsHandle, (error) =>
{
if (error != null)
{
System.Diagnostics.Debug.WriteLine("Error calling Unregister: {0}", error.ToString());
return;
}
// In my use case, the tags are assigned by the server based on privileges.
NSSet tags = null;
Hub.RegisterNativeAsync(pnsHandle, tags, (errorCallback) =>
{
if (errorCallback != null)
System.Diagnostics.Debug.WriteLine("RegisterNativeAsync error: " + errorCallback.ToString());
});
});
}
To answer my question, yes, the deviceToken is the PNS Handle but it must be formatted.

The method RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken) is to tell the delegate that the app successfully registered with the Push Notification service.
The parameter ‘deviceToken’ is a globally unique token that identifies this device to the Push Notification service.
NSSet tags = null; // create tags if you want
Hub.RegisterNativeAsync(deviceToken, tags, (errorCallback) =>
{
if (errorCallback != null)
System.Diagnostics.Debug.WriteLine("RegisterNativeAsync error: " + errorCallback.ToString());
});
Because you are using Azure, the Hub has send the token to the generate remote notifications in above method. So if you only want to push some thing to all users, you don't need to do something else. If you want to push to specific users, you can register a tag and use it as parameter.

Related

Calling SignalR from API at another project - No error nor notification

I have a WebSite integrated with SignalR. It functions well, and it has a button which sends popup notification to all clients who are online. It works well when I click on the button.
My API is in another project but in the same Solution. I want to send the above notification by calling from the API side. Basically, a mobile app will send a request to API and then API will send a notification to all online web clients.
Below code runs and not gives the notification nor any error.
Is this fundamentally correct? Appreciate your help
API code (at WebAPI project)
[HttpGet]
public IEnumerable<string> WatchMe(int record_id)
{
GMapChatHub sendmsg = new GMapChatHub();
sendmsg.sendHelpMessage(record_id.ToString());
return "Done";
}
C# code (at Web project)
namespace GMapChat
{
public class GMapChatHub : Hub
{
public void sendHelpMessage(string token)
{
var context = GlobalHost.ConnectionManager.GetHubContext<GMapChatHub>();
context.Clients.All.helpMessageReceived(token, "Test help message");
}
}
}
Home.aspx file (at Web project)
var chat = $.connection.gMapChatHub;
$(document).ready(function () {
chat.client.helpMessageReceived = function (token,msg) {
console.log("helpMessageReceived: " + msg);
$('#helpMessageBody').html(msg)
$('#helpModal').modal('toggle');
};
}
You can not call that hub directly. Firs you need to install the .net client for SignalR from nuget. Then you need to initialize it like this :
[HttpGet]
public IEnumerable<string> WatchMe(int record_id)
{
using (var hubConnection = new HubConnection("your local host address"))
{
IHubProxy proxy= hubConnection.CreateHubProxy("GMapChatHub");
await hubConnection.Start();
proxy.Invoke("sendHelpMessage",record_id.ToString()); // invoke server method
}
// return sth. IEnumerable<string>
}
And opening a new connection per request may not be good idea you may make it per session (if you use) or static or time fashioned.

Express (NodeJS) failure to handle Post from Xamarin Android App

I've got a few devices that interact with an Express app on a local server. For the purposes of a prototype demo I'm trying to make this as simple as possible. Among many other things, my local server interacts with images, and image data in the /uploads folder thusly:
(Express/NodeJS code):
app.route('/uploads')
//Send File to Android
.get( function (req, res) {
var images = fs.readdirSync('./uploads');
console.log(images[images.length-1]);
var imgpath = path.resolve('./uploads/' + images[images.length-1]);
res.sendFile(imgpath);
//res.end('Image List Sent')
})
//Receive image chip data from Android
.post( function (req, res) {
Console.log("insideit");
Console.log(req.body);
res.end('got something?')
});
This server code is receiving requests from C# Android code. The GET command works perfectly, so I will omit that Xamarin/C# code. The POST command from the android app is thus (In C#/Xamarin):
var rxcui = "198840";
string _url = string.Format(#"http://10.1.10.194:3000/uploads", rxcui);
string datastr = "test";
try
{
(new WebClient()).UploadString(_url, datastr);
}
catch
{
Console.WriteLine("Post Upload Error");
}
}
The server sees the post request, but returns 500. It appears that it's not routing properly, because It won't go into my post handling code and print a simple test string. Any thoughts on why the POST command is not being handled appropriately?
Try to replace res.end with res.send(). Something like this
.post( function (req, res) {
Console.log("insideit");
Console.log(req.body);
res.send('got something?');
});
This should work.

Understanding Toast push notifications and how to display text on them and launch app by pressing on them. Windows Phone 8.1

I am trying to set my application to receive toast push notifications from a server.
Since this server in handled by someone else and he did not request the token to the WSN, I followed an example and I am sending the notifications using a "local web page"
protected void ButtonSendToast_Click(object sender, EventArgs e)
{
try
{
// Get the URI that the Microsoft Push Notification Service returns to the push client when creating a notification channel.
// Normally, a web service would listen for URIs coming from the web client and maintain a list of URIs to send
// notifications out to.
string subscriptionUri = TextBoxUri.Text.ToString();
HttpWebRequest sendNotificationRequest = (HttpWebRequest)WebRequest.Create(subscriptionUri) as HttpWebRequest;
// Create an HTTPWebRequest that posts the toast notification to the Microsoft Push Notification Service.
// HTTP POST is the only method allowed to send the notification.
sendNotificationRequest.Method = "POST";
// The optional custom header X-MessageID uniquely identifies a notification message.
// If it is present, the same value is returned in the notification response. It must be a string that contains a UUID.
// sendNotificationRequest.Headers.Add("X-MessageID", "<UUID>");
// Create the toast message.
string toastMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<wp:Notification xmlns:wp=\"WPNotification\">" +
"<wp:Toast>" +
"<wp:Text1>" + TextBoxTitle.Text.ToString() + "</wp:Text1>" +
"<wp:Text2>" + TextBoxSubTitle.Text.ToString() + "</wp:Text2>" +
"<wp:Param>/Page2.xaml?NavigatedFrom=Toast Notification</wp:Param>" +
"</wp:Toast> " +
"</wp:Notification>";
// Set the notification payload to send.
byte[] notificationMessage = Encoding.Default.GetBytes(toastMessage);
// Set the web request content length.
sendNotificationRequest.Headers.Add("Authorization", String.Format("Bearer {0}", "EgAdAQMAAAAEgAAAC4AATIYp8fmpjFpbdnRTjf2qfP/GqZ8Bbb62bH6N+0MhSztcV/wXfv9aVjiwbVgF5EX0fgBXC6LvJCpl1+ze7ts9h5je4e1QekryEFqfWl36BtTBnmWqBFk0WmwxpdIgGqhVjAtRdnJ3ODnFSBCfd7dq8nFiFTFDxPcTXhdDbu9W3BKMAFoAjAAAAAAAHFAXTMH+bVbB/m1W60gEAA8AMTkwLjE5My42OS4yMzMAAAAAAF0AbXMtYXBwOi8vcy0xLTE1LTItMTU5OTEyNjk1NS0zODAwNDMxNzQ0LTk2OTg4NTEzNi0xNjkxMDU1MjI4LTcwOTcyNTQ0NC00MDYxNzA4MDczLTI0Mzg0MzM1MzQA"));
sendNotificationRequest.ContentLength = notificationMessage.Length;
sendNotificationRequest.ContentType = "text/xml";
sendNotificationRequest.Headers.Add("X-WNS-Type", "wns/toast");
using (Stream requestStream = sendNotificationRequest.GetRequestStream())
{
requestStream.Write(notificationMessage, 0, notificationMessage.Length);
}
// Send the notification and get the response.
HttpWebResponse response = (HttpWebResponse)sendNotificationRequest.GetResponse();
string notificationStatus = response.Headers["X-NotificationStatus"];
string notificationChannelStatus = response.Headers["X-SubscriptionStatus"];
string deviceConnectionStatus = response.Headers["X-DeviceConnectionStatus"];
// Display the response from the Microsoft Push Notification Service.
// Normally, error handling code would be here. In the real world, because data connections are not always available,
// notifications may need to be throttled back if the device cannot be reached.
TextBoxResponse.Text = notificationStatus + " | " + deviceConnectionStatus + " | " + notificationChannelStatus;
}
catch (Exception ex)
{
TextBoxResponse.Text = "Exception caught sending update: " + ex.ToString();
}
}
Now, in my app, I have requested the channel uri and a handler that is called when this channel receives a push
PushNotificationChannel channel = null;
try
{
channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
Debug.WriteLine(channel.Uri);
if (channel.Uri != null)
{
var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
localSettings.Values.Remove("PushToken");
localSettings.Values["PushToken"] = channel.Uri;
channel.PushNotificationReceived += channel_PushNotificationReceived;
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
}
}
async void channel_PushNotificationReceived(PushNotificationChannel sender, PushNotificationReceivedEventArgs e)
{
String notificationContent = String.Empty;
notificationContent = e.ToastNotification.Content.GetXml();
Debug.WriteLine(notificationContent);
}
So far, so good: I receive a notification when my app is running and when my app is closed. But it only says "New notification" and nothing happens when I click on it.
I have tried to add an event
e.ToastNotification.Activated += ToastNotification_Activated;
But that does not work and after reading like 20 documents I am very confused about the toast templates and how can I use it to display what I receive from the server
So, what is the real way to do this, to display in the toast some of the data received in the push, and to make the app to "launch / go to a certain page" when the user clicks on it?
For app to go to a certain page
Since Windows 8/8.1, applications has always been expected to handle activations in response to a user clicking on a toast notification from this app – the app should respond by performing navigation and displaying UI specific to the toast. This is accomplished through an activation string that you include in the toast payload, which is then passed to your app as an argument in the activation event.
for Navigation to certain page you need to override OnActivated event in App.Xaml.cs and and handle to string parameter.
For Display toast
If you want to show text in tiles then it try Badge notifications or only data received through toast then handle it on page after navigation from onactivated event. Also Check this link. It shows standard templates for different notifications. Hope it helps.
You'll get a "New notification" when the system doesn't recognize your toast notification payload. That's because you used the MPNS quickstart, not the WNS quickstart and got most of the stuff moved to WNS except for your toast notification format. You want your toast content to look something like this:
<toast launch="">
<visual lang="en-US">
<binding template="ToastImageAndText01">
<image id="1" src="World" />
<text id="1">Hello</text>
</binding>
</visual>
</toast>
Here's the WNS quickstart guide: https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh868252.aspx
As well as the WNS toast schema: https://msdn.microsoft.com/en-us/library/windows/apps/br230849.aspx

Can not send message to specific user By ConnectionId in SignalR

i can not send message to specific user by connectionId when I try to send all users like this: context.Clients.All.updateMessages(message) - this code is working.
hare is Hub code:
public void Send(string userToId, string userForId, string message)
{
//Get Recipent (userIdfor) connectionId
var signalrhelper = new HomeController();
string userForconnectionId = signalrhelper.GetConnecionIdByUserId(userForId);
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<ChatHubs>();
string messageSenderConnId= signalrhelper.GetConnecionIdByUserId(userToId);
//Call Receiver
context.Clients.Client(userForconnectionId).updateMessages(message);
//Call Sender
context.Clients.Client(messageSenderConnId).updateMessages(message);
}
Hare is My View:
$(function() {
// Declare a proxy to reference the hub.
var notifications = $.connection.chatHubs;
// Create a function that the hub can call to broadcast messages.
notifications.client.updateMessages = function(data) {
if (window.location.href.indexOf("Messages/DetailMessage?userId") > -1) {
$('#timeline-messages').append('{0}'.Stringformat(data));
} else {
ReplaceUpdateTargetIdToReturnData("Messages/GetMessages", "#header_inbox_bar", "#systemMessage");
}
};
$.connection.hub.start().done(function() {
var myClientId = $.connection.hub.id;
GetConnectionIdToSignalR("Home", "SaveConnectionIdbyUserName", "userId", #Session["UserId"], "hubConnectionId", myClientId);
$('#sendMessageButton').click(function() {
if ($('#sendMessageFiled').val().length > 1) {
// Call the Send method on the hub.
notifications.server.send(#Session["UserId"], myClientId, $('#sendMessageButton').attr("title"), $('#sendMessageFiled').val());
// Clear text box and reset focus for next comment.
$('#sendMessageFiled').val('').focus();
} else {
$('#sendMessageFiled').focus();
}
});
}).fail(function (e) {
alert(e);
});
});
Can anybody Know what's happen ?
By user, I assume you mean an authenticated user? If that is the case, you have to map connections to users first. For instance, a user can have 2 or more signalr connections. So the first step is mapping users to connections, then you can send a message to the user and all his/her connected agents will receive it.
There are several ways to map connections to users, the guide is here: http://www.google.co.uk/url?q=http://www.asp.net/signalr/overview/guide-to-the-api/mapping-users-to-connections&sa=U&ei=Tjj-VJuPMsPBOZGGgYgH&ved=0CAsQFjAA&usg=AFQjCNFXoGJOm3mzenAJbz46TUq-Lx2bvA
Although this post is already old: I had a similar issue yesterday and it took me hours! I had the connectionIds but no client received a notification. Context.Clients.All.aMethod(...) worked fine, but Context.Clients.Client(theid).aMethod(...) did not work. I finally realized that I stored the connection-ids in the database as an uniqueIdentifier and MS SQL converted the uniqueIdentifier values to uppercase and therefore the connectionids were not valid any more. I converted the connectinIds to lowercase before publishing to my connected clients and then it worked...maybe you experience a similar problem. But your post with an invalid connectionid because of blanks helped my finding the problem.

how to send message to whatsapp number programmatically using C#

how to send message(text,image,videos) to whatsapp from C# Asp.net Application.
Sending bulk messages programmatically through WhatsApp is definitively against their Terms of Service, and even the author of the (third-party) API posted by andrey.shedko doesn't maintain it or accept any responsibility for it. There's a function within the app to send as many messages as you want to the people you actually know - use that instead.
Within their Legal section:
(iii) you will not attempt to reverse engineer, alter or modify any part of the Service
and
C. You agree not to use or launch any automated system, including without limitation, "robots," "spiders," "offline readers," etc. or "load testers" such as wget, apache bench, mswebstress, httpload, blitz, Xcode Automator, Android Monkey, etc., that accesses the Service in a manner that sends more request messages to the WhatsApp servers in a given period of time than a human can reasonably produce in the same period by using a WhatsApp application
They do not provide a public API, so there's no wiggle room here.
i find perfect solution on This Link.
Following code (C#) I used for sending message-
//Send ( button_click )
string from = "9199********";
string to = txtTo.Text;//Sender Mobile
string msg = txtMessage.Text;
WhatsApp wa = new WhatsApp(from, "BnXk*******B0=", "NickName", true, true);
wa.OnConnectSuccess += () =>
{
MessageBox.Show("Connected to whatsapp...");
wa.OnLoginSuccess += (phoneNumber, data) =>
{
wa.SendMessage(to, msg);
MessageBox.Show("Message Sent...");
};
wa.OnLoginFailed += (data) =>
{
MessageBox.Show("Login Failed : {0}", data);
};
wa.Login();
};
wa.OnConnectFailed += (ex) =>
{
MessageBox.Show("Connection Failed...");
};
wa.Connect();

Categories

Resources