I have created a simple stock bot from the video. I have registered the bot for Skype, Facebook Messenger and E-Mail.
Skype, Facebook messenger works great and replies to the messages are received.
The problem which I have is with the E-Mail. When I send a E-Mail to the bot, for example, "Stock Price of MSFT" it sends an reply e-mail with the stock price and then it sends the same mail again and again without stopping until i remove the e-mail from the dev.botframework.com. Has anyone faced this issue. Is there any additional configuration which is needed to avoid this.
Eg: I sent a message to the bot e-mail saying "What is the stock price of msft" ?
I get a reply back with "Stock Price of msft is ... " from the bot. And the same reply e-mail I am getting continuously until I disable the E-MAIL Bot service..
MessageControl.cs:
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Description;
using Microsoft.Bot.Connector;
using Newtonsoft.Json;
namespace StockBot2
{
[BotAuthentication]
public class MessagesController : ApiController
{
/// <summary>
/// POST: api/Messages
/// Receive a message from a user and reply to it
/// </summary>
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
string StockRateString;
StockLUIS StLUIS = await GetEntityFromLUIS(activity.Text);
if (StLUIS.intents.Count() > 0)
{
switch (StLUIS.intents[0].intent)
{
case "StockPrice":
StockRateString = await GetStock(StLUIS.entities[0].entity);
break;
case "StockPrice2":
StockRateString = await GetStock(StLUIS.entities[0].entity);
break;
default:
StockRateString = "Sorry, I am not getting you...";
break;
}
}
else
{
StockRateString = "Sorry, I am not getting you...";
}
// return our reply to the user
Activity reply = activity.CreateReply(StockRateString);
await connector.Conversations.ReplyToActivityAsync(reply);
}
else
{
HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
private Activity HandleSystemMessage(Activity message)
{
if (message.Type == ActivityTypes.DeleteUserData)
{
// Implement user deletion here
// If we handle user deletion, return a real message
}
else if (message.Type == ActivityTypes.ConversationUpdate)
{
// Handle conversation state changes, like members being added and removed
// Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info
// Not available in all channels
}
else if (message.Type == ActivityTypes.ContactRelationUpdate)
{
// Handle add/remove from contact lists
// Activity.From + Activity.Action represent what happened
}
else if (message.Type == ActivityTypes.Typing)
{
// Handle knowing tha the user is typing
}
else if (message.Type == ActivityTypes.Ping)
{
}
return null;
}
private async Task<string> GetStock(string StockSymbol)
{
double? dblStockValue = await YahooBot.GetStockRateAsync(StockSymbol);
if (dblStockValue == null)
{
return string.Format("This \"{0}\" is not an valid stock symbol", StockSymbol);
}
else
{
return string.Format("Stock Price of {0} is {1}", StockSymbol, dblStockValue);
}
}
private static async Task<StockLUIS> GetEntityFromLUIS(string Query)
{
Query = Uri.EscapeDataString(Query);
StockLUIS Data = new StockLUIS();
using (HttpClient client = new HttpClient())
{
string RequestURI = "https://api.projectoxford.ai/luis/v1/application?id=1c85d9de-7c8d-4ceb-ba11-860f02ce911b&subscription-key=fe48030063c6458587138c4dcd258737&q=" + Query;
HttpResponseMessage msg = await client.GetAsync(RequestURI);
if (msg.IsSuccessStatusCode)
{
var JsonDataResponse = await msg.Content.ReadAsStringAsync();
Data = JsonConvert.DeserializeObject<StockLUIS>(JsonDataResponse);
}
}
return Data;
}
}
}
YahooBot.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web;
namespace StockBot2
{
public class YahooBot
{
public static async Task<double?> GetStockRateAsync(string StockSymbol)
{
try
{
string ServiceURL = $"http://finance.yahoo.com/d/quotes.csv?s={StockSymbol}&f=sl1d1nd";
string ResultInCSV;
using (WebClient client = new WebClient())
{
ResultInCSV = await client.DownloadStringTaskAsync(ServiceURL).ConfigureAwait(false);
}
var FirstLine = ResultInCSV.Split('\n')[0];
var Price = FirstLine.Split(',')[1];
if (Price != null && Price.Length >= 0)
{
double result;
if (double.TryParse(Price, out result))
{
return result;
}
}
return null;
}
catch (WebException ex)
{
//handle your exception here
throw ex;
}
}
}
}
Related
Is there a way to create a hero card that has buttons that keep working when it is forwarded to another user?
We want to build a chatbot that enables you to share things with someone else. So first you talk to the chatbot (mainly via buttons) to setup a thing and then you forward that to one of your contacts so that they can participate. With Facebook Messenger we can directly call a share action that pops up the share dialog with which the user can forward a card to a contact. Using a m.me/123?ref=456 URL in a button of the card the receiver can open a conversation with the chatbot in the correct context. I managed to do something similar for Telegram.
I try to replicate this with other services. With Skype there is no way to explicitly call the share action, but I can write a message "Please forward the next card to the user you want to share this with:" [and then I display that card]. Now I give that card a button with a postBack action that should open the correct context, but that button seems to be non functional. When tapping it no post back is sent to the chatbot.
Is there a way to implement something like what I described for Facebook Messenger with Skype? Or is there a way to deep link to a chatbot passing some reference?
(I'd rather have the later for a second use case anyway, where a user puts a share button on their website that then opens Skype in a conversation with the chatbot, passing the reference. I have that for Messenger and Telegram.)
Edit: What I would like to do would basically be the following. I use nodejs, but only the ChatConnector class and not the UniversalBot class.
connector.send([
{
type: 'message',
address,
textFomrat: 'plain',
text: 'Forward the next message to people you want to share THING with:',
},
{
type: 'message',
address,
attachments: [{
contentType: 'application/vnd.microsoft.card.hero',
content: {
title: name,
images: [{
url: image_url
}],
buttons: [{
type: 'postBack',
title: 'Chat with this bot about THING',
value: 'open:' + thing_id,
}]
}
}],
}
], callback);
I'm not sure how you're sending cards to the other user, but here's an example in .net where the buttons sent will function as expected for the other user:
using Microsoft.Bot.Builder.Dialogs;
using System;
using System.Threading.Tasks;
using Microsoft.Bot.Connector;
using Microsoft.Bot.Builder.FormFlow;
using System.Collections.Generic;
using System.Collections.Concurrent;
using Microsoft.Bot.Builder.ConnectorEx;
using Newtonsoft.Json;
namespace CardsBot
{
[Serializable]
public class SendToUserDialog : IDialog<object>
{
static ConcurrentDictionary<string, ConversationReference> _cachedConversationReferences = new ConcurrentDictionary<string, ConversationReference>();
private const string HelpString = "Enter 'Send Card' to send a card to another user. 'Send Message' to send a json message to another user. Or 'List Users' to see who is ready to receive messages.";
public async Task StartAsync(IDialogContext context)
{
context.Wait(this.MessageReceivedAsync);
}
public async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
{
var message = await argument;
string upperText = message.Text.Replace(" ", "").ToUpper();
if (upperText == "SENDCARD")
{
IFormDialog<SendCardForm> form = new FormDialog<SendCardForm>(new SendCardForm(), SendCardForm.BuildForm, FormOptions.PromptInStart);
context.Call(form, AfterSendCardComplete);
}
else if (upperText == "SENDMESSAGE")
{
IFormDialog<SendMessageForm> form = new FormDialog<SendMessageForm>(new SendMessageForm(), SendMessageForm.BuildForm, FormOptions.PromptInStart);
context.Call(form, AfterSendMessageFormComplete);
}
else if (upperText == "LISTUSERS")
{
var names = String.Join(", ", _cachedConversationReferences.Keys);
await context.PostAsync($"Users : {names}");
}
else
{
if (!context.UserData.ContainsKey("name"))
{
var getNamePrompt = new PromptDialog.PromptString("What is your name?", "Please enter your name.", 3);
context.Call(getNamePrompt, AfterNamePrompt);
}
else
{
await context.PostAsync($"You said: {message.Text} ({HelpString})");
context.Wait(MessageReceivedAsync);
}
}
}
private async Task AfterSendMessageFormComplete(IDialogContext context, IAwaitable<SendMessageForm> result)
{
var sendMessageForm = await result;
if (string.IsNullOrEmpty(sendMessageForm.RecipientName))
{
await context.PostAsync("A recipient name was not provided. Cannot send the message to nobody.");
}
else
{
ConversationReference conversation = null;
if (_cachedConversationReferences.TryGetValue(sendMessageForm.RecipientName, out conversation))
{
var originalReply = conversation.GetPostToBotMessage().CreateReply();
var replyMessage = GetMessage(originalReply, sendMessageForm.MessageJson);
var connector = new ConnectorClient(new Uri(originalReply.ServiceUrl));
await connector.Conversations.ReplyToActivityAsync(replyMessage);
await context.PostAsync($"Message was sent to {sendMessageForm.RecipientName} on {replyMessage.ChannelId} channel.");
}
else
{
await context.PostAsync($"No recipient found matching the name {sendMessageForm.RecipientName}");
}
}
}
private async Task AfterNamePrompt(IDialogContext context, IAwaitable<string> result)
{
var name = await result;
context.UserData.SetValue("name", name);
await context.PostAsync($"Thanks. What would you like to do now {name}? {HelpString}");
var conversationReference = context.Activity.ToConversationReference();
_cachedConversationReferences.AddOrUpdate(name, conversationReference, (oldVal, newVal) => conversationReference);
}
public Activity GetMessage(Activity originalReply, string messageJson)
{
var reply = JsonConvert.DeserializeObject<Activity>(messageJson);
reply.ChannelId = originalReply.ChannelId;
reply.Timestamp = originalReply.Timestamp;
reply.From = originalReply.From;
reply.Conversation = originalReply.Conversation;
reply.Recipient = originalReply.Recipient;
reply.Id = originalReply.Id;
reply.ReplyToId = originalReply.ReplyToId;
reply.ServiceUrl = originalReply.ServiceUrl;
return reply;
}
private async Task AfterSendCardComplete(IDialogContext context, IAwaitable<SendCardForm> result)
{
var SendCardForm = await result;
if (string.IsNullOrEmpty(SendCardForm.RecipientName))
{
await context.PostAsync("A recipient name was not provided. Cannot send a card to nobody.");
}
else
{
ConversationReference conversation = null;
if (_cachedConversationReferences.TryGetValue(SendCardForm.RecipientName, out conversation))
{
var reply = conversation.GetPostToBotMessage().CreateReply();
reply.Attachments.Add(GetCard(SendCardForm.Text, SendCardForm.Buttons.Value));
var connector = new ConnectorClient(new Uri(reply.ServiceUrl));
await connector.Conversations.ReplyToActivityAsync(reply);
}
else
{
await context.PostAsync($"No recipient found matching the name {SendCardForm.RecipientName}");
}
}
}
public Attachment GetCard(string text, int numberOfButtons)
{
var card = new HeroCard(text);
card.Buttons = new List<CardAction>(GetCardActions(numberOfButtons));
return card.ToAttachment();
}
private IEnumerable<CardAction> GetCardActions(int numberOfButtons)
{
for (int counter = 1; counter < numberOfButtons; counter++)
{
yield return new CardAction()
{
Title = $"button{counter}",
Type = ActionTypes.ImBack,
Value = $"button{counter}"
};
}
}
}
[Serializable]
public class SendMessageForm
{
[Prompt("Who would you like to send this message to (enter the name of the recipient)?")]
public string RecipientName { get; set; }
[Prompt("Paste in the .json of the message you would like to Send.")]
public string MessageJson { get; set; }
public static IForm<SendMessageForm> BuildForm()
{
return new FormBuilder<SendMessageForm>()
.AddRemainingFields()
.Confirm("Is this information correct?{*}")
.Build();
}
}
[Serializable]
public class SendCardForm
{
[Prompt("What would you like for the card text?")]
public string Text { get; set; }
[Prompt("How many buttons?")]
public int? Buttons { get; set; }
[Prompt("Who would you like to send the card to?")]
public string RecipientName { get; set; }
public static IForm<SendCardForm> BuildForm()
{
return new FormBuilder<SendCardForm>()
.AddRemainingFields()
.Confirm("Is this information correct?{*}")
.Build();
}
}
}
I am trying to implement media/rich/enhanced notifications in ios. I have an iPhone 6, 6s and 7. The image I send in the payload appears in the rich notification on the 6 , but not on the 6s or 7. The code seems to just stop at the CreateDownloadTask (I have verified that I can change the Body text just before that line of code, but I can’t after). I have even had simpler version of this use NSData.FromUrl(url) but the code “breaks” at that line. The odd think is that it doesn’t truly break, it displays the text for the Body element that was originally pushed. Even a try catch doesn’t grab the error. FYI..category is there for the custom ui I am building. Can't figure out why it only works properly on iphone 6 (all the phone are on 10.2.x or above)
the payload is {"aps":{"alert":{"title":"title", "subtitle":"subtitle","body":"body"}, "category":"pushWithImage","mutable-content":1}, "pushImage":"https://ewcweb.azurewebsites.net/media/boldlythumb.png"}
I can’t send project but below is the service extension code:
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Foundation;
using SDWebImage;
using UIKit;
using UserNotifications;
namespace notifications
{
[Register ("NotificationService")]
public class NotificationService : UNNotificationServiceExtension
{
UNMutableNotificationContent BestAttemptContent { get; set; }
public Action ContentHandler { get; set; }
const string ATTACHMENT_IMAGE_KEY = "pushImage";
const string ATTACHMENT_FILE_NAME = "-attachment-image.";
protected NotificationService (IntPtr handle) : base (handle)
{
// Note: this .ctor should not contain any initialization logic.
}
public async Task<byte[]> LoadImage (string imageUrl)
{
var httpClient = new HttpClient ();
var contentsTask = await httpClient.GetByteArrayAsync (imageUrl);
return contentsTask;
}
public override void DidReceiveNotificationRequest (UNNotificationRequest request, Action<UNNotificationContent> contentHandler)
{
string imageURL = null;
ContentHandler = contentHandler;
BestAttemptContent = request.Content.MutableCopy () as UNMutableNotificationContent;
if (BestAttemptContent != null) {
if (BestAttemptContent.UserInfo.ContainsKey (new NSString (ATTACHMENT_IMAGE_KEY))) {
imageURL = BestAttemptContent.UserInfo.ValueForKey (new NSString (ATTACHMENT_IMAGE_KEY)).ToString ();
}
if (imageURL == null) {
ContentHandler (BestAttemptContent);
return;
}
var url = new NSUrl (imageURL.ToString ());
NSError err = null;
var task = NSUrlSession.SharedSession.CreateDownloadTask ( new NSMutableUrlRequest (url),(tempfile, response, error) => {
if (error != null)
{
ContentHandler (BestAttemptContent);
return;
}
if (tempfile == null)
{
ContentHandler (BestAttemptContent);
return;
}
var cache = NSSearchPath.GetDirectories (NSSearchPathDirectory.CachesDirectory, NSSearchPathDomain.User, true);
var cachesFolder = cache [0];
var guid = NSProcessInfo.ProcessInfo.GloballyUniqueString;
var fileName = guid + ".png";
var cacheFile = cachesFolder + "/" + fileName;
var attachmentURL = NSUrl.CreateFileUrl (cacheFile, false, null);
NSFileManager.DefaultManager.Move(tempfile, attachmentURL, out err);
if (err != null)
{
ContentHandler (BestAttemptContent);
return;
}
// Create attachment;
var attachmentID = "image";
var options = new UNNotificationAttachmentOptions ();
var attachment = UNNotificationAttachment.FromIdentifier (attachmentID, attachmentURL, options, out err);
BestAttemptContent.Attachments = new UNNotificationAttachment [] { attachment };
BestAttemptContent.Title = BestAttemptContent.Title;
BestAttemptContent.Body = BestAttemptContent.Body;
BestAttemptContent.CategoryIdentifier = BestAttemptContent.CategoryIdentifier;
BestAttemptContent.Subtitle = BestAttemptContent.Subtitle;
//Display notification
ContentHandler (BestAttemptContent);
});
task.Resume ();
} else {
// Display notification
ContentHandler (BestAttemptContent);
}
}
public override void TimeWillExpire ()
{
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
ContentHandler (BestAttemptContent);
}
}
}
In the bot web chat, when I type a message, the bot says "sending" first and then changes to "couldn't send, Retry". But the message is sent and I am getting the reply. How can I avoid this? Do I need to increase the message timeout? If so, where I need to set it?
This is the code snippet. I am using C# SDK where I have coded in MessageReceivedASync method
namespace Bot_Application1.Dialogs
{
public class HRBotDialog : IDialog<object>
{
public static string dialogcontext = "";
public async Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
{
// Get the text passed
var message = await argument;
string typedText = message.Text.ToLower();
string answer = "My Answer";
if ((typedText == "hi") || (typedText == "hello"))
{
answer = message.Text;
}
else if ((typedText == "how many personal days do i have left") || (typedText.Contains("personal days")))
{
answer = "Looks like you have 2 remaining for this year";
}
I am adding the controller code here
//[BotAuthentication]
public class MessagesController : ApiController
{
/// <summary>
/// POST: api/Messages
/// Receive a message from a user and reply to it
/// </summary>
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
await Conversation.SendAsync(activity, () => new HRBotDialog());
}
else
{
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
var reply = HandleSystemMessage(activity);
if (reply != null)
await connector.Conversations.ReplyToActivityAsync(reply);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
private Activity HandleSystemMessage(Activity message)
{
if (message.Type == ActivityTypes.DeleteUserData)
{
// Implement user deletion here
// If we handle user deletion, return a real message
}
else if (message.Type == ActivityTypes.ConversationUpdate)
{
string replyMessage = string.Empty;
if (message.MembersAdded.Any(o => o.Id == message.Recipient.Id))
{
replyMessage += $"How can I help you? \n";
return message.CreateReply(replyMessage);
}
}
else if (message.Type == ActivityTypes.ContactRelationUpdate)
{
// Handle add/remove from contact lists
// Activity.From + Activity.Action represent what happened
}
else if (message.Type == ActivityTypes.Typing)
{
// Handle knowing tha the user is typing
}
else if (message.Type == ActivityTypes.Ping)
{
}
return null;
}
}
Please try adding the Serializable attribute to your HRBotDialog like this:
[Serializable]
public class HRBotDialog : IDialog<object>
{
//...code...
}
We have developed a Android App using Xamarin and we are facing problem in Azure Notification Hub. We were trying to send push notifications to Android devices registered in our Azure Mobile Service and no registered android devices are receiving the notifications. When we tried to debug it via Notification Hub in Azure Portal, the results are showing that the notifications are sent to the devices. However, the devices are not receiving the notifications which was receiving before.
Kindly let us know either we are missing something in our code (Find the code below) or is there any problem in Azure Notification Hub (for Android GCM).
Note: All the android permissions for push notifications are given in the same code file below and not in the Android manifest.
Our GCM Service Code Below:
using System.Text;
using Android.App;
using Android.Content;
using Android.Util;
using Gcm.Client;
//VERY VERY VERY IMPORTANT NOTE!!!!
// Your package name MUST NOT start with an uppercase letter.
// Android does not allow permissions to start with an upper case letter
// If it does you will get a very cryptic error in logcat and it will not be obvious why you are crying!
// So please, for the love of all that is kind on this earth, use a LOWERCASE first letter in your Package Name!!!!
using ByteSmith.WindowsAzure.Messaging;
using System.Diagnostics;
using System.Collections.Generic;
using System;
[assembly: Permission(Name = "#PACKAGE_NAME#.permission.C2D_MESSAGE")]
[assembly: UsesPermission(Name = "#PACKAGE_NAME#.permission.C2D_MESSAGE")]
[assembly: UsesPermission(Name = "com.google.android.c2dm.permission.RECEIVE")]
//GET_ACCOUNTS is only needed for android versions 4.0.3 and below
[assembly: UsesPermission(Name = "android.permission.GET_ACCOUNTS")]
[assembly: UsesPermission(Name = "android.permission.INTERNET")]
[assembly: UsesPermission(Name = "android.permission.WAKE_LOCK")]
namespace seeMuscatAndroidApp
{
//You must subclass this!
[BroadcastReceiver(Permission= Gcm.Client.Constants.PERMISSION_GCM_INTENTS)]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_MESSAGE }, Categories = new string[] { "#PACKAGE_NAME#" })]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_REGISTRATION_CALLBACK }, Categories = new string[] { "#PACKAGE_NAME#" })]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_LIBRARY_RETRY }, Categories = new string[] { "#PACKAGE_NAME#" })]
public class PushHandlerBroadcastReceiver : GcmBroadcastReceiverBase<GcmService>
{
public static string[] SENDER_IDS = new string[] { Constants.SenderID };
public const string TAG = "GoogleCloudMessaging";
}
[Service] //Must use the service tag
public class GcmService : GcmServiceBase
{
public static string RegistrationID { get; private set; }
private NotificationHub Hub { get; set; }
Context _generalContext;
public GcmService() : base(PushHandlerBroadcastReceiver.SENDER_IDS)
{
Log.Info(PushHandlerBroadcastReceiver.TAG, "GcmService() constructor");
}
protected override async void OnRegistered (Context context, string registrationId)
{
Log.Verbose(PushHandlerBroadcastReceiver.TAG, "GCM Registered: " + registrationId);
RegistrationID = registrationId;
_generalContext = context;
//createNotification("GcmService Registered...", "The device has been Registered, Tap to View!");
Hub = new NotificationHub(Constants.NotificationHubPath, Constants.ConnectionString);
try
{
await Hub.UnregisterAllAsync(registrationId);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
Debugger.Break();
}
var tags = new List<string>() { main.userCountry, main.userCity, main.userLatitude, main.userLongitude, main.userPhoneMake, main.userPhoneModel, main.userPhoneName, main.userPhoneAndroidVersion, main.userAppVersion,main.userUID};
Console.WriteLine("///////////HUB TAGS///////////////////");
Console.WriteLine("Country:" + main.userCountry);
Console.WriteLine("City:" + main.userCity);
Console.WriteLine("Latitude:" + main.userLatitude);
Console.WriteLine("Longitude:"+main.userLongitude);
Console.WriteLine("Make:" + main.userPhoneMake);
Console.WriteLine("Model:" + main.userPhoneModel);
Console.WriteLine("Phone Name:" + main.userPhoneName);
Console.WriteLine("Android Version:" + main.userPhoneAndroidVersion);
Console.WriteLine("App version:" + main.userAppVersion);
Console.WriteLine("User ID:" + main.userUID);
Console.WriteLine("///////////END OF HUB TAGS///////////////////");
try
{
var hubRegistration = await Hub.RegisterNativeAsync(registrationId, tags);
Debug.WriteLine("RegistrationId:" + hubRegistration.RegistrationId);
}
catch (Exception ex)
{
Debug.WriteLine("#########$$$$Error:"+ex.Message);
}
}
protected override void OnUnRegistered (Context context, string registrationId)
{
Log.Verbose(PushHandlerBroadcastReceiver.TAG, "GCM Unregistered: " + registrationId);
//Remove from the web service
// var wc = new WebClient();
// var result = wc.UploadString("http://your.server.com/api/unregister/", "POST",
// "{ 'registrationId' : '" + lastRegistrationId + "' }");
//createNotification("GcmService Unregistered...", "The device has been unregistered, Tap to View!");
}
protected override void OnMessage (Context context, Intent intent)
{
Log.Info(PushHandlerBroadcastReceiver.TAG, "GCM Message Received!");
Debug.WriteLine("/********* GCM Received ****************");
var msg = new StringBuilder();
if (intent != null && intent.Extras != null)
{
foreach (var key in intent.Extras.KeySet())
msg.AppendLine(key + "=" + intent.Extras.Get(key).ToString());
}
//Store the message
var prefs = GetSharedPreferences(context.PackageName, FileCreationMode.Private);
var edit = prefs.Edit();
edit.PutString("last_msg", msg.ToString());
edit.Commit();
string message = intent.Extras.GetString("message");
if (!string.IsNullOrEmpty(message))
{
createNotification("New todo item!", "Todo item: " + message);
return;
}
string msg2 = intent.Extras.GetString("msg");
string notititle = intent.Extras.GetString("notititle");
if (!string.IsNullOrEmpty(msg2))
{
createNotification(notititle, msg2);
return;
}
// createNotification("PushSharp-GCM Msg Rec'd", "Message Received for C2DM-Sharp... Tap to View!");
//createNotification("Unknown message details", msg.ToString());
}
protected override bool OnRecoverableError (Context context, string errorId)
{
Log.Warn(PushHandlerBroadcastReceiver.TAG, "Recoverable Error: " + errorId);
return base.OnRecoverableError (context, errorId);
}
protected override void OnError (Context context, string errorId)
{
Log.Error(PushHandlerBroadcastReceiver.TAG, "GCM Error: " + errorId);
}
void createNotification(string title, string desc)
{
//Create notification
var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
//Create an intent to show ui
Intent uiIntent = new Intent();
uiIntent.SetClass(this, typeof(dealsview));
uiIntent.PutExtra("contentID", "todaydeals");
uiIntent.PutExtra("contentName", "");
uiIntent.PutExtra("isSale", "");
//Create the notification
var notification = new Notification(Resource.Drawable.Icon, title);
//Auto cancel will remove the notification once the user touches it
notification.Flags = NotificationFlags.AutoCancel;
notification.Defaults = NotificationDefaults.All;
//Set the notification info
//we use the pending intent, passing our ui intent over which will get called
//when the notification is tapped.
notification.SetLatestEventInfo(this, title, desc, PendingIntent.GetActivity(this, 0, uiIntent, 0));
//Show the notification
notificationManager.Notify(1, notification);
}
}
}
Permission are tags in the Manifest file that has XML data. While compiling DVM checks the Manifest file for permissions and other necessary data to package in the .apk. You should always follow the same format.
Please have a look at this tutorial for more information.
I want to integrate Microsoft.AspNet.SignalR version="2.1.2" with Microsoft.AspNet.WebApi version="5.2.2" so that the API can communicate in REAL-TIME. I found one VERY NICE SAMPLE that implements/works exactly the same way I want, but the sample uses jquery.signalR-0.5.0.js. Some of the earlier implementations have changed, and so far here is what I have done in a failed effort to upgrade the solution to use the latest signalr, asp.net web api and owin.
I left the Hub as it is
using SignalR.Hubs;
namespace NdcDemo.Hubs
{
// This hub has no inbound APIs, since all inbound communication is done
// via the HTTP API. It's here for clients which want to get continuous
// notification of changes to the ToDo database.
[HubName("todo")]
public class ToDoHub : Hub { }
}
I also left the ApiControllerWithHub class as it is
using System;
using System.Web.Http;
using SignalR;
using SignalR.Hubs;
namespace NdcDemo.Controllers
{
public abstract class ApiControllerWithHub<THub> : ApiController
where THub : IHub
{
Lazy<IHubContext> hub = new Lazy<IHubContext>(
() => GlobalHost.ConnectionManager.GetHubContext<THub>()
);
protected IHubContext Hub
{
get { return hub.Value; }
}
}
}
For the ToDoController, I changed the
Hub.Clients.addItem(item), Hub.Clients.updateItem(toUpdate),
Hub.Clients.deleteItem(id)
to
Hub.Clients.All.addItem(item), Hub.Clients.All.updateItem(toUpdate),
Hub.Clients.All.deleteItem(id)
and this is now the full ToDoController class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Web.Http;
using NdcDemo.Hubs;
using NdcDemo.Models;
namespace NdcDemo.Controllers
{
[InvalidModelStateFilter]
public class ToDoController : ApiControllerWithHub<ToDoHub>
{
private static List<ToDoItem> db = new List<ToDoItem>
{
new ToDoItem { ID = 0, Title = "Do a silly demo on-stage at NDC" },
new ToDoItem { ID = 1, Title = "Wash the car" },
new ToDoItem { ID = 2, Title = "Get a haircut", Finished = true }
};
private static int lastId = db.Max(tdi => tdi.ID);
public IEnumerable<ToDoItem> GetToDoItems()
{
lock (db)
return db.ToArray();
}
public ToDoItem GetToDoItem(int id)
{
lock (db)
{
var item = db.SingleOrDefault(i => i.ID == id);
if (item == null)
throw new HttpResponseException(
Request.CreateResponse(HttpStatusCode.NotFound)
);
return item;
}
}
public HttpResponseMessage PostNewToDoItem(ToDoItem item)
{
lock (db)
{
// Add item to the "database"
item.ID = Interlocked.Increment(ref lastId);
db.Add(item);
// Notify the connected clients
Hub.Clients.addItem(item);
// Return the new item, inside a 201 response
var response = Request.CreateResponse(HttpStatusCode.Created, item);
string link = Url.Link("apiRoute", new { controller = "todo", id = item.ID });
response.Headers.Location = new Uri(link);
return response;
}
}
public ToDoItem PutUpdatedToDoItem(int id, ToDoItem item)
{
lock (db)
{
// Find the existing item
var toUpdate = db.SingleOrDefault(i => i.ID == id);
if (toUpdate == null)
throw new HttpResponseException(
Request.CreateResponse(HttpStatusCode.NotFound)
);
// Update the editable fields and save back to the "database"
toUpdate.Title = item.Title;
toUpdate.Finished = item.Finished;
// Notify the connected clients
Hub.Clients.updateItem(toUpdate);
// Return the updated item
return toUpdate;
}
}
public HttpResponseMessage DeleteToDoItem(int id)
{
lock (db)
{
int removeCount = db.RemoveAll(i => i.ID == id);
if (removeCount <= 0)
return Request.CreateResponse(HttpStatusCode.NotFound);
// Notify the connected clients
Hub.Clients.deleteItem(id);
return Request.CreateResponse(HttpStatusCode.OK);
}
}
}
}
And then I put app.MapSignalR();
But the demo doesn't work...the API doesn't contact clients...Where am I going wrong?
I would still appreciate any more simpler recommendations based on this solution.
Solution from OP.
Answer:
After a a cup of camomile tea, i found out that the clients had to include the KEYWORD CLIENT before the dynamic methods in Todo.js... So, here is what that needs to be modified so that the sample works
hub.client.addItem = function (item) {
alert("i just received something...");
viewModel.add(item.ID, item.Title, item.Finished);
};
hub.client.deleteItem = function (id) {
viewModel.remove(id);
};
hub.client.updateItem = function (item) {
viewModel.update(item.ID, item.Title, item.Finished);
};
And it works!