How to use Pinpoint send messages to specific devices using the Java AWS SDK for Pinpoint - aws-pinpoint

I have been able to get my mobile Android app to receive messages generated from the Pinpoint Campaign console (https://console.aws.amazon.com/pinpoint/home) to a specific device by targeting the segment to a custom attribute that only that device has.
Pinpoint Campaign config
Mobile push channel
Standard campaign
Segment defined using custom attributes, holdout 0%
Silent notification
Custom JSON
Launch immediate
Now I would like to implement this feature in my Java app using the SDK APIs and target the device's Pinpoint endpoint.
GetEndpointRequest getEndpointRequest = new GetEndpointRequest()
.withApplicationId(appId)
.withEndpointId(endpointId);
GetEndpointResult endpointResult = getAmazonPinpointClient().getEndpoint(getEndpointRequest);
DirectMessageConfiguration directMessageConfiguration =
new DirectMessageConfiguration().withGCMMessage(new GCMMessage().withBody(body).withSilentPush(true).withAction(Action.OPEN_APP));
AddressConfiguration addressConfiguration = new AddressConfiguration().withChannelType(ChannelType.GCM);
MessageRequest messageRequest = new MessageRequest().withMessageConfiguration(directMessageConfiguration)
.addAddressesEntry(endpointResponse.getAddress(), addressConfiguration);
SendMessagesRequest sendMessagesRequest = new SendMessagesRequest()
.withApplicationId(appId)
.withMessageRequest(messageRequest);
The "body" is the same JSON I put in the Pinpoint Campaign console. When I run this, I get back a DeliveryStatus of SUCCESSFUL but the device never receives the message.
{ApplicationId: MY_APP_ID,Result: {clrVUcv-AwA:APA91bHGXkxpDJiw5kOMROA2XTJXuKreMklq9jemHO_KGYTIw6w84Fw9zLv9waMgLgha61IR-kZxgmrnFu-OGp8l6WFgp4Wolh4oOvZwMobGYNgzivv3bGIK83t-e4hiLx1TTaEIeRdQ={DeliveryStatus: SUCCESSFUL,StatusCode: 200,StatusMessage: {"multicast_id":4803589342422496921,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1515105369948916%c551fa42f9fd7ecd"}]},}}}
I have also tried this via the AWS CLI:
aws pinpoint send-messages --application-id MY_APP_ID --message-request "{\"Addresses\":{\"clrVUcv-AwA:APA91bHGXkxpDJiw5kOMROA2XTJXuKreMklq9jemHO_KGYTIw6w84Fw9zLv9waMgLgha61IR-kZxgmrnFu-OGp8l6WFgp4Wolh4oOvZwMobGYNgzivv3bGIK83t-e4hiLx1TTaEIeRdQ\":{\"ChannelType\":\"GCM\"}},\"MessageConfiguration\":{\"GCMMessage\":{\"Body\":\"{\\\"message\\\":\\\"stuff\\\"}\",\"SilentPush\":true}}}"
with a similar result (get 200 status code and DeliveryStatus of SUCCESSFUL but the app never receives). I tried using the "Direct" message in the AWS Pinpoint console but they do not seem to support the same format (forces Action and Title/Message instead of silent push message with custom JSON).
Am I getting the endpoint incorrectly? How do I translate the above campaign into a message? I see there is a sendUserMessages() API call as well but that doesn't seem to be right one (I couldn't find where to specify the specific user endpoint)?
The client receives the campaign via the registered Service:
public class PushListenerService extends GcmListenerService {
#Override
public void onMessageReceived(final String from, final Bundle data) {
AWSMobileClient.initializeMobileClientIfNecessary(this.getApplicationContext());
final NotificationClient notificationClient = AWSMobileClient.defaultMobileClient()
.getPinpointManager().getNotificationClient();
NotificationClient.CampaignPushResult pushResult =
notificationClient.handleGCMCampaignPush(from, data, this.getClass());
Log.e(LOG_TAG, " onMessageReceived - got messages" + data);
Do GCM direct messages get sent through the same campaign method or do I have to register a different service to process these?

Found the solution based on the AWS CLI command I was able to run. Should have been using the "Data" element and not the "Body" and need to enable "SilentPush".
EndpointResponse endpointResponse = getPinpointEndpointResponse(appId, pinpointEndpointId);
Map<String, String> data = new HashMap<>();
// construct data here, currently only supports Map<String, String>
// why not HashMap<String, Object> so it can support full JSON????
DirectMessageConfiguration directMessageConfiguration =
new DirectMessageConfiguration().withGCMMessage(new GCMMessage().withData(data).withSilentPush(true));
AddressConfiguration addressConfiguration = new AddressConfiguration().withChannelType(ChannelType.GCM);
MessageRequest messageRequest = new MessageRequest().withMessageConfiguration(directMessageConfiguration)
.addAddressesEntry(endpointResponse.getAddress(), addressConfiguration);
SendMessagesRequest sendMessagesRequest = new SendMessagesRequest()
.withApplicationId(appId)
.withMessageRequest(messageRequest);

Related

Send the message to personal chat instead of group chat using ms teams chatbot

I would like to post a message to personal chat instead of group chat while message post from group chat how to achieve this ?.
Is there is any methods available related to this issue ?.
As the others have noted in this thread, you need to use something called "Proactive Messaging". I see you're using node.js though, so here is a better sample than the C# or Java people have posted already: https://github.com/pnp/teams-dev-samples/tree/main/samples/bot-proactive-messaging . I put both a dotnet as well as a node.js version in the sample, and there are some links at the bottom of the page to read more about the topic. Here is also a link to a video session where I talk more about the concept: https://www.youtube.com/watch?v=mM7-fYdcJhw&t=1398s
It is important to know that proactive messaging will only work if you have a "context" with the user already, which basically means they have to have installed your app already. It is possible to pre-install it on their behalf though. You need to use Graph to do this, and you can read more about it here: https://learn.microsoft.com/en-us/graph/api/userteamwork-post-installedapps?view=graph-rest-1.0&tabs=http .
You can post a proactive personal message to a user:
Please find below sample code:
Also, before running this code, make sure that the user has installed the bot app in the personal scope or is a member of a Team which has the bot installed.
Reference docs:
https://learn.microsoft.com/en-us/java/api/com.microsoft.cognitiveservices.speech.transcription.conversation.createconversationasync?view=azure-java-stable
using Microsoft.Bot.Builder;
using Microsoft.Bot.Connector;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Bot.Schema;
using Microsoft.Bot.Schema.Teams;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Teams.Bot.Conversations
{
class Program
{
public static async Task Main(string[] args)
{
//Teams internal id
string teamInternalId = "19:96391bb270744e218c04dc8f571d3d8b#thread.skype";
//The Bot Service Url needs to be dynamically stored and fetched from the Team. Recommendation is to store the serviceUrl from the bot Payload and later re-use it to send proactive messages.
string serviceUrl = "https://smba.trafficmanager.net/emea/";
//the upn of the user who should recieve the personal message
string mentionUserPrincipalName = "user#tenant.onmicrosoft.com";
//Office 365/Azure AD tenant id for the
string tenantId = "<tenant-GUID>";
//From the Bot Channel Registration
string botClientID = "<client-id>";
string botClientSecret = "<client-secret>";
var connectorClient = new ConnectorClient(new Uri(serviceUrl), new MicrosoftAppCredentials(botClientID, botClientSecret));
var user = await ((Microsoft.Bot.Connector.Conversations)connectorClient.Conversations).GetConversationMemberAsync(mentionUserPrincipalName, teamInternalId, default);
var personalMessageActivity = MessageFactory.Text($"Personal message from the Bot!");
var conversationParameters = new ConversationParameters()
{
ChannelData = new TeamsChannelData
{
Tenant = new TenantInfo
{
Id = tenantId,
}
},
Members = new List<ChannelAccount>() { user }
};
var response = await connectorClient.Conversations.CreateConversationAsync(conversationParameters);
await connectorClient.Conversations.SendToConversationAsync(response.Id, personalMessageActivity);
}
}
}

Messages not coming thru to Azure SignalR Service

I'm implementing Azure SignalR service in my ASP.NET Core 2.2 app with React front-end. When I send a message, I'm NOT getting any errors but my messages are not reaching the Azure SignalR service.
To be specific, this is a private chat application so when a message reaches the hub, I only need to send it to participants in that particular chat and NOT to all connections.
When I send a message, it hits my hub but I see no indication that the message is making it to the Azure Service.
For security, I use Auth0 JWT Token authentication. In my hub, I correctly see the authorized user claims so I don't think there's any issues with security. As I mentioned, the fact that I'm able to hit the hub tells me that the frontend and security are working fine.
In the Azure portal however, I see no indication of any messages but if I'm reading the data correctly, I do see 2 client connections which is correct in my tests i.e. two open browsers I'm using for testing. Here's a screen shot:
Here's my Startup.cs code:
public void ConfigureServices(IServiceCollection services)
{
// Omitted for brevity
services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(jwtOptions => {
jwtOptions.Authority = authority;
jwtOptions.Audience = audience;
jwtOptions.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
// Check to see if the message is coming into chat
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
(path.StartsWithSegments("/im")))
{
context.Token = accessToken;
}
return System.Threading.Tasks.Task.CompletedTask;
}
};
});
// Add SignalR
services.AddSignalR(hubOptions => {
hubOptions.KeepAliveInterval = TimeSpan.FromSeconds(10);
}).AddAzureSignalR(Configuration["AzureSignalR:ConnectionString"]);
}
And here's the Configure() method:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// Omitted for brevity
app.UseSignalRQueryStringAuth();
app.UseAzureSignalR(routes =>
{
routes.MapHub<Hubs.IngridMessaging>("/im");
});
}
Here's the method I use to map a user's connectionId to the userName:
public override async Task OnConnectedAsync()
{
// Get connectionId
var connectionId = Context.ConnectionId;
// Get current userId
var userId = Utils.GetUserId(Context.User);
// Add connection
var connections = await _myServices.AddHubConnection(userId, connectionId);
await Groups.AddToGroupAsync(connectionId, "Online Users");
await base.OnConnectedAsync();
}
Here's one of my hub methods. Please note that I'm aware a user may have multiple connections simultaneously. I just simplified the code here to make it easier to digest. My actual code accounts for users having multiple connections:
[Authorize]
public async Task CreateConversation(Conversation conversation)
{
// Get sender
var user = Context.User;
var connectionId = Context.ConnectionId;
// Send message to all participants of this chat
foreach(var person in conversation.Participants)
{
var userConnectionId = Utils.GetUserConnectionId(user.Id);
await Clients.User(userConnectionId.ToString()).SendAsync("new_conversation", conversation.Message);
}
}
Any idea what I'm doing wrong that prevents messages from reaching the Azure SignalR service?
It might be caused by misspelled method, incorrect method signature, incorrect hub name, duplicate method name on the client, or missing JSON parser on the client, as it might fail silently on the server.
Taken from Calling methods between the client and server silently fails
:
Misspelled method, incorrect method signature, or incorrect hub name
If the name or signature of a called method does not exactly match an appropriate method on the client, the call will fail. Verify that the method name called by the server matches the name of the method on the client. Also, SignalR creates the hub proxy using camel-cased methods, as is appropriate in JavaScript, so a method called SendMessage on the server would be called sendMessage in the client proxy. If you use the HubName attribute in your server-side code, verify that the name used matches the name used to create the hub on the client. If you do not use the HubName attribute, verify that the name of the hub in a JavaScript client is camel-cased, such as chatHub instead of ChatHub.
Duplicate method name on client
Verify that you do not have a duplicate method on the client that differs only by case. If your client application has a method called sendMessage, verify that there isn't also a method called SendMessage as well.
Missing JSON parser on the client
SignalR requires a JSON parser to be present to serialize calls between the server and the client. If your client doesn't have a built-in JSON parser (such as Internet Explorer 7), you'll need to include one in your application.
Update
In response to your comments, I would suggest you try one of the Azure SignalR samples, such as
Get Started with SignalR: a Chat Room Example to see if you get the same behavior.
Hope it helps!

Convert QueueClient.Create to MessagingFactory.CreateQueueClient

Trying to convert an implementation using the .net library from using QueueClient.Create to the MessagingFactory.CreateQueueClient to be able to better control the BatchFlushInterval as well as to allowing the use of multiple factories over multiple connections to increase send throughput but running into roadblocks.
Right now we are creating QueueClients (they are maintained throughout the app) like this:
QueueClient.CreateFromConnectionString(address, queueName, ReceiveMode.PeekLock); // address is the connection string from the azure portal in the form of Endpoint=sb....
Trying to change it to creating a MessagingFactory in the class construtor that will be used to create the QueueClients:
messagingFactory = MessagingFactory.Create(address.Replace("Endpoint=",""),mfs);
// later on in another part of the class
messagingFactory.CreateQueueClient(queueName, ReceiveMode.PeekLock);
// error Endpoint not found.,
This throws the error Endpoint not found. If I don't replace the Endpoint= it won't even create the MessagingFactory. What is the proper way to handle this?
Notes:
address = Endpoint=sb://pmg-bus-mybus.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=somekey
As an aside, we have a process that is trying to push as many messages as possible to a queue and others reading it. The readers seem to easily keep up with the sender and I'm trying to maximize the send rate.
The address is the base address of namespace(sb://yournamespace.servicebus.windows.net/) you are connecting to. For more information, please refer to MessagingFactory. The following is the demo code :
var Address = "sb://yournamespace.servicebus.windows.net/"; //base address of namespace you are connecting to.
MessagingFactorySettings MsgFactorySettings = new MessagingFactorySettings
{
NetMessagingTransportSettings = new NetMessagingTransportSettings
{
BatchFlushInterval = TimeSpan.FromSeconds(2)
},
TokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider("RootManageSharedAccessKey", "balabala..."),
OperationTimeout = TimeSpan.FromSeconds(30)
}; //specify operating timeout (optional)
MessagingFactory messagingFactory = MessagingFactory.Create(Address, MsgFactorySettings);
var queue = messagingFactory.CreateQueueClient("queueName",ReceiveMode.PeekLock);
var message = queue.Receive(TimeSpan.Zero);

How to add more entries in customDimensions in to Application Insights Telemetry in Azure Function

I am trying to integrate Azure App Insights with an Azure Function App (HttpTriggered). I want to add my own keys and values in the "customDimensions" object of the requests table. Right now it only shows the following:
On query
requests
| where iKey == "449470fb-****" and id == "5e17e23e-****"
I get this:
LogLevel: Information
Category: Host.Results
FullName: Functions.FTAID
StartTime: 2017-07-14T14:24:10.9410000Z
param__context: ****
HttpMethod: POST
param__req: Method: POST, Uri: ****
Succeeded: True
TriggerReason: This function was programmatically called via the host APIs.
EndTime: 2017-07-14T14:24:11.6080000Z
I want to add more key values such as:
EnvironmentName: Development
ServiceLine: Business
Based on this answer, I implemented the ITelemetryInitializer interface as follows:
public class CustomTelemetry : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
var requestTelemetry = telemetry as RequestTelemetry;
if (requestTelemetry == null) return;
requestTelemetry.Context.Properties.Add("EnvironmentName", "Development");
}
}
Here is how the run.csx code for the Azure Function App looks like:
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, ExecutionContext context, TraceWriter log)
{
// Initialize the App Insights Telemetry
TelemetryConfiguration.Active.InstrumentationKey = System.Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY", EnvironmentVariableTarget.Process);
TelemetryConfiguration.Active.TelemetryInitializers.Add(new CustomTelemetry());
TelemetryClient telemetry = new TelemetryClient();
var jsonBody = await req.Content.ReadAsStringAsync();
GetIoItemID obj = new GetIoItemID();
JArray output = obj.GetResponseJson(jsonBody, log, telemetry);
var response = req.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(output.ToString(), System.Text.Encoding.UTF8, "application/json");
return response;
}
But this did not work...
I believe, since you're creating the TelemetryClient yourself in this example, you don't need to bother with the telemetry initializer, you could just do
var telemetry = new TelemetryClient();
telemetry.Context.Properties["EnvironmentName"] = "Development";
directly, and everything sent by that instance of that telemetry client will have those properties set.
You'd need that telemetry initializer if you don't have control over who's creating the telemetry client and want to touch every item of telemetry created wherever?
I don't know how that TelemetryClient instance gets used downstream in azure functions though, so i'm not entirely positive, though.
Edit: from azure functions post about this, it says:
We’ll be working hard to get Application Insights ready for production
workloads. We’re also listening for any feedback you have. Please file
it on our GitHub. We’ll be adding some new features like better
sampling controls and automatic dependency tracking soon. We hope
you’ll give it a try and start to gain more insight into how your
Functions are behaving. You can read more about how it works at
https://aka.ms/func-ai
and the example from that func-ai link has a couple things:
1) it creates the telemetry client statically up front once (instead of in each call to the function)
private static TelemetryClient telemetry = new TelemetryClient();
private static string key = TelemetryConfiguration.Active.InstrumentationKey = System.Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY", EnvironmentVariableTarget.Process);
and inside the function it is doing:
telemetry.Context.Operation.Id = context.InvocationId.ToString();
to properly do correlation with events you might create with your telemetry client so you might want to do that too.
2) it appears that the telemetry client you create you can use, but they create their own telemetry client and send data there, so anything you touch in your telemetry client's context isn't seen by azure functions itself.
so, to me that leads me to something you can try:
add a static constructor in your class, and in that static constructor, do the telemetry initializer thing you were doing above. possibly this gets your telemetry initializer added to the context before azure functions starts creating its request and calling your method?
If that doesn't work, you might need to post on their GitHub or email the person listed in the article for more details on how to do this?

Windows Azure worker role and SendGrid "Bad Key Path!" Error

I'm trying to use SendGrid to send an email from an Azure worker role every time there are certain exceptions, but I can't get the email to send. I am using SendGridMail version 6.1.0.0 and SendGrid.SmtpApi version 1.3.1.0 which I installed via nuget and .Net 4.5. I am currently debugging locally with plans to deploy to Azure if i can get the emails to successfully send.
SendGridMessage myMessage = new SendGridMessage();
List<String> recipients = new List<String> { #"John Doe <johnd#outlook.com>", #"Peter Howe <perterhowe#gmail.com>" };
myMessage.AddTo(recipients);
myMessage.From = new MailAddress("myemail#test.com");
myMessage.Subject = "Error in Update";
myMessage.Text = "TESTING 123";
string username = XXXXXX;
string password = XXXXXXX;
// Create credentials, specifying your user name and password.
var credentials = new NetworkCredential(username, password);
// Create an Web transport for sending email.
var transportWeb = new Web(credentials);
// Send the email.
await transportWeb.DeliverAsync(myMessage);
As far as I can see I'm not getting any errors except when I debug and look at myMessage the Header has an error.
When I tried initializing a new empty header (var header = new Header();) I noticed there were still errors on that
To = 'header.To' threw an exception of type 'System.ArgumentException' Message = "Bad key path!"
Does anyone know what this means? Or if this could be causing the emails not to send?
The answer to your other question actually uses SendGrid:
Alerts for exceptions in an Azure worker role
There are three globalvariables:
public const string SmtpServerHost = "smtp.sendgrid.net";
public const string SmtpServerUserName = "[useridfromsendgrid#azure.com]";
public const string SmtpServerPassword = "[password from sendgrid]";
You actually do not need to use the SDK, just setup the account in Azure portal, and save your creds in your project.
You can send emails locally, but if you are on a work network, the firewall may block the emails from being sent. The code I posted I placed in an email service in my namespace.
It has be deployed to Azure to work. It won't work locally.

Resources