perlquestion
DaisyLou
~~~~~~~~~~~~~~~~
edit: SOLUTION BELOW.
~~~~~~~~~~~~~~~~
I'm trying to send a WNS notification via Perl with the following code using OAuth2.
When I step through the code, it appends the site to make it https://login.live.com/oauth/authorize, which (I think) is causing it to fail.
Any suggestions on how to send WNS notifications from Perl?
<code>
my $auth = Net::OAuth2::Profile::Password->new(
site => 'https://login.live.com/'
, site_path => '/accesstoken.srf'
, client_id => uri_escape('ms-app://xxxxx')
, client_secret => uri_escape('yyyyyyyy')
, grant_type => 'client_credentials'
, scope => 'notify.windows.com'
);
my $token = $auth->get_access_token();
</code>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I gave up using OAuth2 and rolled my own. Hopefully you folks will find this helpful.
You can't just copy and paste, but if you follow the directions in the ##comments, you should be 90% there.
This sends a Toast message via WNS to a unique ID (in my case, a user ID) associated with a WNS Channel.
The XML payload can obviously be changed to send badges or whatever you want. I store everything in a database. You can use constants, except for the access token which changes, and you'll need to find some way to persist it.
To use: sendPushNotificationToast($uniqID);.
This will resolve the uniqID to its corresponding WNS channel(which again, in my case is stored in the database next to uniqID), and try to send a push notification.
<code>
use DBI;
use URI::Escape;
use LWP::Simple;
use JSON qw( decode_json );
use Data::Dumper;
##################################################################
sub getWNSChannel
{
## The "ID" here is an ID associated with a WNS channel
## An ID is a unique user or computer
my ($id) = @_;
## I persist the WNSURI in a database.
## Fetch WNSURI from persistent store, using ID as a key
# Sanity check
if ($WNSChannel =~ m#^https://.*notify\.windows\.com\/\?#) {
return $WNSChannel;
}
return $WNS_OFFLINE;
}
##################################################################
sub sendWNSPost
{
my ( $server_endpoint,
$payload,
$hdr_Authorization,
$hdr_ContentType,
$hdr_XWNSType,
$hdr_XWNSCachePolicy,
$hdr_XWNSRequestForStatus,
$hdr_XWNSTag,
$hdr_XWNSTTL ) = @_;
my $ua = LWP::UserAgent->new;
$ua->agent("ArbitraryUserAgent/0.1 ");
my $req = HTTP::Request->new(POST => $server_endpoint);
$req->header('Authorization' => $hdr_Authorization ) if defined $hdr_Authorization;
$req->header('Content-Type' => $hdr_ContentType ) if defined $hdr_ContentType;
$req->header('X-WNS-Type' => $hdr_XWNSType ) if defined $hdr_XWNSType;
$req->header('X-WNS-Cache-Policy' => $hdr_XWNSCachePolicy ) if defined $hdr_XWNSCachePolicy;
$req->header('X-WNS-RequestForStatus' => $hdr_XWNSRequestForStatus ) if defined $hdr_XWNSRequestForStatus;
$req->header('X-WNS-Tag' => $hdr_XWNSTag ) if defined $hdr_XWNSTag;
$req->header('X-WNS-TTL' => $hdr_XWNSTTL ) if defined $hdr_X-WNS-TTL;
$req->content($payload);
return $ua->request($req);
}
sub getNewAccessToken
{
## Fetch WNS client ID, secret, and URI from persistent store
## (or you can hard code them/put in config file, whatever)
## and put them into client_id, client_secret URL encoded
$authenticationUri = "https://login.live.com/accesstoken.srf";
$client_id = uri_escape($client_id);
$client_secret = uri_escape($client_secret);
my $payload = "grant_type=client_credentials&client_id=$client_id&client_secret=$client_secret&scope=notify.windows.com";
my $resp = sendWNSPost( $authenticationUri,
$payload,
undef,
'application/x-www-form-urlencoded', # Content-Type
undef, undef, undef, undef, undef);
my $decoded_json = decode_json( $resp->content );
my $access_token = $decoded_json->{'access_token'};
if ($resp->is_success) {
## Update the access token in the database/persistent store
} else {
## Yeeps! Something went wrong. Send an alert, log, etc here.
}
}
sub getWNSAccessToken
{
# Get the current access token
## Nothing fancy.
## Just fetch current WNS access token from persistent store and return it
return $accessToken;
}
sub sendPushNotificationToast
{
my ($id) = @_;
my $WNSChannel = getWNSChannel($id);
my $accessToken = getWNSAccessToken();
if ($WNSChannel == $WNS_OFFLINE) {
return $WNS_OFFLINE;
}
# Sanity check
if ($WNSChannel !~ /^https.*notify\.windows\.com/) {
return $WNS_ERR;
}
# Get customer details for toast
my $toastLine1 = "Toast Line 1";
my $toastLine2 = "Toast Line 2";
my $payload = "<toast duration='long'><visual version='1'><binding template='ToastText02'><text id='1'>$toastLine1</text><text id='2'>$toastLine2</text></binding></visual></toast>";
my $resp;
$resp = sendWNSPost ($WNSChannel, $payload, "Bearer $accessToken", "text/xml", "wns/toast", "no-cache", "true", undef, undef);
if ($resp->is_error) {
## If at first you don't succeed,
## get a new access token and try again.
getNewAccessToken();
$accessToken = getWNSAccessToken();
$resp = sendWNSPost ($WNSChannel, $payload, "Bearer $accessToken", "text/xml", "wns/toast", "no-cache", "true", undef, 600); # TTL = 10 minutes here
}
if ($resp->is_success) {
my $connectionStatus = $resp->header('X-WNS-DeviceConnectionStatus');
if ($connectionStatus eq 'connected') {
return $WNS_OK;
} elsif ($connectionStatus eq 'tempdisconnected') {
## Handle 'tempdisconnected' in whatever way you please.
return $WNS_OK; # or whatever you want
} elsif ($connectionStatus eq 'disconnected') {
return $WNS_OFFLINE;
}
}
return $WNS_ERR;
}
;
</code>