Multiple PushNotification Subscriptions some work properly and some don't - exchangewebservices

I tried posting this on the Exchange Development forum and didnt get any replies, so I will try here. Link to forum
I have a windows services that fires every fifteen minutes to see if there is any subscriptions that need to be created or updated. I am using the Managed API v1.1 against Exchange 2007 SP1. I have a table that stores all the users that want there mailbox monitored. So that when a notifcation comes in to the "Listening Service" I am able to look up the user and access the message to log it into the application we are building. In the table I have the following columns that store the subscription information:
SubscriptionId - VARCHAR(MAX)
Watermark - VARCHAR(MAX)
LastStatusUpdate - DATETIME
My services calls a function that queries the data needed (based on which function it is doing). If the user doesn't have a subscription already the service will go and create one. I am using impersonation to access the mailboxes. Here is my "ActiveSubscription" method that is fired when a user needs the subscription either created or updated.
private void ActivateSubscription(User user)
{
if (user.ADGUID.HasValue)
{
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, Settings.ActiveDirectoryServerName, Settings.ActiveDirectoryRootContainer);
using (UserPrincipal up = UserPrincipal.FindByIdentity(ctx, IdentityType.Guid, user.ADGUID.Value.ToString()))
{
ewService.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SID, up.Sid.Value);
}
}
else
{
ewService.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, user.EmailAddress);
}
PushSubscription pushSubscription = ewService.SubscribeToPushNotifications(
new FolderId[] { WellKnownFolderName.Inbox, WellKnownFolderName.SentItems },
Settings.ListenerService, 30, user.Watermark,
EventType.NewMail, EventType.Created);
user.Watermark = pushSubscription.Watermark;
user.SubscriptionID = pushSubscription.Id;
user.SubscriptionStatusDateTime = DateTime.Now.ToLocalTime();
_users.Update(user);
}
We have also ran the following cmdlet to give the user we are accessing the EWS with the ability to impersonate on the Exchange Server.
Get-ExchangeServer | where {$_.IsClientAccessServer -eq $TRUE} | ForEach-Object {Add-ADPermission -Identity $_.distinguishedname -User (Get-User -Identity mailmonitor | select-object).identity -extendedRight ms-Exch-EPI-Impersonation}
The "ActivateSubscription" code above works as expected. Or so I thought. When I was testing it I had it monitoring my mailbox and it worked great. The only problem I had to work around was that the subscription was firing twice when the item was a new mail in the inbox, I got a notification for the NewMail event and Created event. I implemented a work around that checks to make sure the message hasn't already been logged on my Listening service. It all worked great.
Today, we started testing two mailboxes being monitor at the same time. The two mailboxes were mine and another developers mailbox. We found the strangest behavior. My subscription worked as expected. But his didn't, the incoming part of his subscription work properly but any email he sent out the listening service never was sent a notification. Looking at the mailbox properties on Exchange I don't see any difference between his mailbox and mine. We even compared options/settings in Outlook. I can see no reasons why it works on my mailbox and not on his.
Is there something that I am missing when creating the subscription. I didn't think there was since my subscription works as expected.
My listening service code works perfectly well. I have placed the code below incase someone wants to see it to make sure it is not the issue.
Thanks in advance, Terry
Listening Service Code:
/// <summary>
/// Summary description for PushNotificationClient
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class PushNotificationClient : System.Web.Services.WebService, INotificationServiceBinding
{
ExchangeService ewService = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
public PushNotificationClient()
{
//todo: init the service.
SetupExchangeWebService();
}
private void SetupExchangeWebService()
{
ewService.Credentials = Settings.ServiceCreds;
try
{
ewService.AutodiscoverUrl(Settings.AutoDiscoverThisEmailAddress);
}
catch (AutodiscoverRemoteException e)
{
//log auto discovery failed
ewService.Url = Settings.ExchangeService;
}
}
public SendNotificationResultType SendNotification(SendNotificationResponseType SendNotification1)
{
using (var _users = new ExchangeUser(Settings.SqlConnectionString))
{
var result = new SendNotificationResultType();
var responseMessages = SendNotification1.ResponseMessages.Items;
foreach (var responseMessage in responseMessages)
{
if (responseMessage.ResponseCode != ResponseCodeType.NoError)
{
//log error and unsubscribe.
result.SubscriptionStatus = SubscriptionStatusType.Unsubscribe;
return result;
}
var sendNoficationResponse = responseMessage as SendNotificationResponseMessageType;
if (sendNoficationResponse == null)
{
result.SubscriptionStatus = SubscriptionStatusType.Unsubscribe;
return result;
}
var notificationType = sendNoficationResponse.Notification;
var subscriptionId = notificationType.SubscriptionId;
var previousWatermark = notificationType.PreviousWatermark;
User user = _users.GetById(subscriptionId);
if (user != null)
{
if (user.MonitorEmailYN == true)
{
BaseNotificationEventType[] baseNotifications = notificationType.Items;
for (int i = 0; i < notificationType.Items.Length; i++)
{
if (baseNotifications[i] is BaseObjectChangedEventType)
{
var bocet = baseNotifications[i] as BaseObjectChangedEventType;
AccessCreateDeleteNewMailEvent(bocet, ref user);
}
}
_PreviousItemId = null;
}
else
{
user.SubscriptionID = String.Empty;
user.SubscriptionStatusDateTime = null;
user.Watermark = String.Empty;
_users.Update(user);
result.SubscriptionStatus = SubscriptionStatusType.Unsubscribe;
return result;
}
user.SubscriptionStatusDateTime = DateTime.Now.ToLocalTime();
_users.Update(user);
}
else
{
result.SubscriptionStatus = SubscriptionStatusType.Unsubscribe;
return result;
}
}
result.SubscriptionStatus = SubscriptionStatusType.OK;
return result;
}
}
private string _PreviousItemId;
private void AccessCreateDeleteNewMailEvent(BaseObjectChangedEventType bocet, ref User user)
{
var watermark = bocet.Watermark;
var timestamp = bocet.TimeStamp.ToLocalTime();
var parentFolderId = bocet.ParentFolderId;
if (bocet.Item is ItemIdType)
{
var itemId = bocet.Item as ItemIdType;
if (itemId != null)
{
if (string.IsNullOrEmpty(_PreviousItemId) || (!string.IsNullOrEmpty(_PreviousItemId) && _PreviousItemId != itemId.Id))
{
ProcessItem(itemId, ref user);
_PreviousItemId = itemId.Id;
}
}
}
user.SubscriptionStatusDateTime = timestamp;
user.Watermark = watermark;
using (var _users = new ExchangeUser(Settings.SqlConnectionString))
{
_users.Update(user);
}
}
private void ProcessItem(ItemIdType itemId, ref User user)
{
try
{
ewService.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, user.EmailAddress);
EmailMessage email = EmailMessage.Bind(ewService, itemId.Id);
using (var _entity = new SalesAssistantEntityDataContext(Settings.SqlConnectionString))
{
var direction = EmailDirection.Incoming;
if (email.From.Address == user.EmailAddress)
{
direction = EmailDirection.Outgoing;
}
int? bodyType = (int)email.Body.BodyType;
var _HtmlToRtf = new HtmlToRtf();
var message = _HtmlToRtf.ConvertHtmlToText(email.Body.Text);
bool? IsIncoming = Convert.ToBoolean((int)direction);
if (IsIncoming.HasValue && IsIncoming.Value == false)
{
foreach (var emailTo in email.ToRecipients)
{
_entity.InsertMailMessage(email.From.Address, emailTo.Address, email.Subject, message, bodyType, IsIncoming);
}
}
else
{
if (email.ReceivedBy != null)
{
_entity.InsertMailMessage(email.From.Address, email.ReceivedBy.Address, email.Subject, message, bodyType, IsIncoming);
}
else
{
var emailToFind = user.EmailAddress;
if (email.ToRecipients.Any(x => x.Address == emailToFind))
{
_entity.InsertMailMessage(email.From.Address, emailToFind, email.Subject, message, bodyType, IsIncoming);
}
}
}
}
}
catch(Exception e)
{
//Log exception
using (var errorHandler = new ErrorHandler(Settings.SqlConnectionString))
{
errorHandler.LogException(e, user.UserID, user.SubscriptionID, user.Watermark, user.SubscriptionStatusDateTime);
}
throw e;
}
}
}

I have two answers for you.
At first you will have to create one instance of ExchangeService per user. Like I understand your Code you just create one instance and switch the impersonation, which is not supported. I developed a windowsservice which is pretty similar to yours. Mine is synchronising the mails between our CRM and Exchange. So at startup I create an instance per user and Cache it as long as the application runs.
Now about cache-mode. The diffrence between using cache-mode and not is just a timing gab. In cache-mode Outlook synchronizes from time to time. And non cached it's in time. When you use the cache-mode and want the Events immediatly on your Exchange-Server you can press the "send and receive"-button in Outlook to force the sync.
Hope that helps you...

Related

Blazor WASM Passing value from server to the client dynamically

I've made an application with Blazor WebAssembly with a 5min timer in a BackgroundService into my SERVER.
Now, everytime a second change I would like to notify my client to update the timer on the CLIENT page. (it's a Component)
I was wondering if there was a solution to call a CLIENT C# Method from my SERVER by using a SERVICE between this two ?
Do you have any suggestion ?
I've already created the shared SERVICE but I don't know how I can call my CLIENT C# method from my Service automatically.
ps : I've already trying to pass my values from my SERVER to a SERVICE who call a JAVASCRIPT function (with IJSRuntime) who will call my C# function to update the timer. But actually my Js function seems to not working.
[My Background Task]
`
// A 5min timer who create a Draw every 5min and update it when count is at 0
public async Task ClockCountDownAsync()
{
if (secTime == 0 && minTime != 0)
{
if (minTime == 5)
{
Draw newDraw = new Draw();
_unitOfWork.Draws.Add(newDraw);
await _unitOfWork.Complete();
}
minTime = minTime - 1;
secTime = 59;
}
else if (secTime != 0)
{
secTime = secTime - 1;
}
else if(minTime == 0 && secTime == 0)
{
await DrawAndAddNumbers();
minTime = 5;
secTime = 0;
}
int[] timeTab = new int[] { minTime, secTime };
_timerService.GetTimeFromCounter(timeTab);
}
`
[My Client Function]
`
public async Task UpdateTimer(int[] timeTab)
{
minTime = timeTab[0];
secTime = timeTab[1];
await InvokeAsync(StateHasChanged);
}
`
[The function in a Service shared who is called by the SERVER who need to call the function from my CLIENT]
`
public void GetTimeFromCounter(int[] timeTab)
{
// UpdateTimer(timeTab);
}
`

How to send JSON objects to another computer?

Using Websockets, I am able to use 1 computer and 1 kinect v2.0 to generate JSON objects of the joints x,y, and z coordinates in real-time. The next steps is to have this data transferred to another computer, using possibly TCP/IP network. My question is to anyone who knows how this is done. Having this data transferred to another computer.
Output that needs to be transferred to another computer in real-time
namespace WebSockets.Server
{
class Program
{
// Store the subscribed clients.
static List<IWebSocketConnection> clients = new List<IWebSocketConnection>();
// Initialize the WebSocket server connection.
static Body[] bodies = new Body[6];
//static KinectSensor kinectSensor = null;
static CoordinateMapper _coordinateMapper;
static Mode _mode = Mode.Color;
static void Main(string[] args)
{
//const string SkeletonStreamName = "skeleton";
//SkeletonStreamMessage skeletonStreamMessage;// = new SkeletonStreamMessage { stream = SkeletonStreamName };
KinectSensor kinectSensor = KinectSensor.GetDefault();
BodyFrameReader bodyFrameReader = null;
bodyFrameReader = kinectSensor.BodyFrameSource.OpenReader();
ColorFrameReader colorFrameReader = null;
colorFrameReader = kinectSensor.ColorFrameSource.OpenReader();
_coordinateMapper = kinectSensor.CoordinateMapper;
kinectSensor.Open();
WebSocketServer server = new WebSocketServer("ws://localhost:8001");
server.Start(socket =>
{
socket.OnOpen = () =>
{
// Add the incoming connection to our list.
clients.Add(socket);
};
socket.OnClose = () =>
{
// Remove the disconnected client from the list.
clients.Remove(socket);
};
socket.OnMessage = message =>
{
if (message == "get-video")
{
int NUMBER_OF_FRAMES = new DirectoryInfo("Video").GetFiles().Length;
// Send the video as a list of consecutive images.
for (int index = 0; index < NUMBER_OF_FRAMES; index++)
{
foreach (var client in clients)
{
string path = "Video/" + index + ".jpg";
byte[] image = ImageUtil.ToByteArray(path);
client.Send(image);
}
// We send 30 frames per second, so sleep for 34 milliseconds.
System.Threading.Thread.Sleep(270);
}
}
else if (message == "get-bodies")
{
if (kinectSensor.IsOpen)
{
if (bodyFrameReader != null)
{
bodyFrameReader.FrameArrived += bodyFrameReader_FrameArrived;
}
}
}
else if (message == "get-color")
{
if (kinectSensor.IsOpen)
{
if (colorFrameReader != null)
{
colorFrameReader.FrameArrived += colorFrameReader_FrameArrived;
}
}
}
};
});
// Wait for a key press to close...
Console.ReadLine();
kinectSensor.Close();
}
private static void colorFrameReader_FrameArrived(object sender, ColorFrameArrivedEventArgs e)
{
//throw new NotImplementedException();
using (ColorFrame colorFrame = e.FrameReference.AcquireFrame()) {
if (colorFrame != null) {
var blob = colorFrame.Serialize();
foreach (var client in clients)
{
if (blob != null)
{
client.Send(blob);
Console.WriteLine("After color Blob sent");
}
}
}
}
}
private static void bodyFrameReader_FrameArrived(object sender, BodyFrameArrivedEventArgs e)
{
//throw new NotImplementedException();
bool dataReceived = false;
using (BodyFrame bodyFrame = e.FrameReference.AcquireFrame())
{
if (bodyFrame != null)
{
if (bodies == null)
{
bodies = new Body[bodyFrame.BodyCount];
}
// The first time GetAndRefreshBodyData is called, Kinect will allocate each Body in the array.
// As long as those body objects are not disposed and not set to null in the array,
// those body objects will be re-used.
bodyFrame.GetAndRefreshBodyData(bodies);
dataReceived = true;
}
}
if (dataReceived)
{
foreach (var client in clients)
{
var users = bodies.Where(s => s.IsTracked.Equals(true)).ToList();
if (users.Count>0){
string json = users.Serialize(_coordinateMapper, _mode);
Console.WriteLine("jsonstring: " + json);
Console.WriteLine("After body serialization and to send");
}
}
}
}
}
}
Try changing it from
WebSocketServer server = new WebSocketServer("ws://localhost:8001");
to
WebSocketServer server = new WebSocketServer(" ws://192.168.X.X:8001");
on the client end. Enter the IP address of the client computer and make sure both server and client are on the same network.

Azure Stream Analytics Error : Could not deserialize the input event(s) from IOT hub

I have created the Stream Analytics job to read data from IOT hub as input and and to store data to SQL DB.
Here are the some important input details configured for Steam Analytics job are
Event Serialization format: JSON
Encoding :utf-8
The message is sent to IOT Hub from Dotnet simulated code.
When I am running my job I am getting the following error:
Could not deserialize the input event as Json. Some possible reasons:
1) Malformed events
2) Input source configured with incorrect serialization format
And here is the my dotnet code.
private static async void ReceiveC2dAsync()
{
Console.WriteLine("\nReceiving cloud to device messages from service");
while (true)
{
Message receivedMessage = await deviceClient.ReceiveAsync();
if (receivedMessage == null) continue;
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Received message: {0}", Encoding.ASCII.GetString(receivedMessage.GetBytes()));
Console.ResetColor();
await deviceClient.CompleteAsync(receivedMessage);
}
}
private static async void SendDeviceToCloudMessagesAsync()
{
double minTemperature = 20;
double minHumidity = 60;
int messageId = 1;
Random rand = new Random();
while (true)
{
double currentTemperature = minTemperature + rand.NextDouble() * 15;
double currentHumidity = minHumidity + rand.NextDouble() * 20;
var telemetryDataPoint = new
{
messageId = messageId++,
deviceId = "myFirstDevice",
temperature = currentTemperature,
humidity = currentHumidity
};
var messageString = JsonConvert.SerializeObject(telemetryDataPoint);
string levelValue;
string temperatureAlert = "false";
if (rand.NextDouble() > 0.7)
{
if (rand.NextDouble() > 0.5)
{
messageString = "This is a critical message";
levelValue = "critical";
}
else
{
messageString = "This is a storage message";
levelValue = "storage";
}
}
else
{
levelValue = "normal";
}
if(currentTemperature > 30)
{
temperatureAlert = "true";
}
var message = new Message(Encoding.UTF8.GetBytes(messageString));
message.Properties.Add("level", levelValue);
message.Properties.Add("temperatureAlert", temperatureAlert);
await deviceClient.SendEventAsync(message);
Console.WriteLine("{0} > Sending message: {1}", DateTime.Now, messageString);
await Task.Delay(1000);
}
}
it looks like your simulated device generated non-json formatted messages such as "This is a critical message" and "This is a storage message".
Basically, you have two choices to fix this issue:
1. comment this part in the simulated code or
2. add the filter in the Azure IoT Hub Routes for these messages

Maintaining session in Gupshup bot calls to Api.ai

I am building a bot in Gupshup with Api.ai integration. I have an agent in Api.ai with several intents and each of them linked through contexts(input & output contexts). When I use the following code to call Api.ai, the first intent is called and I get the reply. However when the second message is given, the bot takes it as a completely new message, without identifying its relation with first.
How can I solve this issue? Kindly help
function MessageHandler(context, event) {
// var nlpToken = "xxxxxxxxxxxxxxxxxxxxxxx";//Your API.ai token
// context.sendResponse(JSON.stringify(event));
sendMessageToApiAi({
message : event.message,
sessionId : new Date().getTime() +'api',
nlpToken : "3626fe2d46b64cf8a9c0d3bee99a9sb3",
callback : function(res){
//Sample response from apiai here.
context.sendResponse(JSON.parse(res).result.fulfillment.speech);
}
},context)
}
function sendMessageToApiAi(options,botcontext) {
var message = options.message; // Mandatory
var sessionId = options.sessionId || ""; // optinal
var callback = options.callback;
if (!(callback && typeof callback == 'function')) {
return botcontext.sendResponse("ERROR : type of options.callback should be function and its Mandatory");
}
var nlpToken = options.nlpToken;
if (!nlpToken) {
if (!botcontext.simpledb.botleveldata.config || !botcontext.simpledb.botleveldata.config.nlpToken) {
return botcontext.sendResponse("ERROR : token not set. Please set Api.ai Token to options.nlpToken or context.simpledb.botleveldata.config.nlpToken");
} else {
nlpToken = botcontext.simpledb.botleveldata.config.nlpToken;
}
}
var query = '?v=20150910&query='+ encodeURIComponent(message) +'&sessionId='+sessionId+'&timezone=Asia/Calcutta&lang=en '
var apiurl = "https://api.api.ai/api/query"+query;
var headers = { "Authorization": "Bearer " + nlpToken};
botcontext.simplehttp.makeGet(apiurl, headers, function(context, event) {
if (event.getresp) {
callback(event.getresp);
} else {
callback({})
}
});
}
/** Functions declared below are required **/
function EventHandler(context, event) {
if (!context.simpledb.botleveldata.numinstance)
context.simpledb.botleveldata.numinstance = 0;
numinstances = parseInt(context.simpledb.botleveldata.numinstance) + 1;
context.simpledb.botleveldata.numinstance = numinstances;
context.sendResponse("Thanks for adding me. You are:" + numinstances);
}
function HttpResponseHandler(context, event) {
// if(event.geturl === "http://ip-api.com/json")
context.sendResponse(event.getresp);
}
function DbGetHandler(context, event) {
context.sendResponse("testdbput keyword was last get by:" + event.dbval);
}
function DbPutHandler(context, event) {
context.sendResponse("testdbput keyword was last put by:" + event.dbval);
}
The sessionId has to be fixed for a user. There are two ways you can do this in the Gupshup bot code -
Use the unique userID which is sent to the bot for every user.
To get this value you can use -
event.senderobj.channelid
But this value is dependent on how different messaging channels provides it and api.ai has a limit of 36 characters.
Sample code -
function MessageHandler(context, event) {
sendMessageToApiAi({
message : event.message,
sessionId : event.senderobj.channelid,
nlpToken : "3626fe2d46b64cf8a9c0d3bee99a9sb3",
callback : function(res){
//Sample response from apiai here.
context.sendResponse(JSON.parse(res).result.fulfillment.speech);
}
},context)
}
Generate a unique sessionId for each user and store it in the database to utilise it. In the below sample , I am storing the sessionId at roomleveldata which is the default persistance provided by Gupshup, to know more check this guide.
Sample code -
function MessageHandler(context, event) {
sendMessageToApiAi({
message : event.message,
sessionId : sessionId(context),
nlpToken : "84c813598fb34dc5b1f3e1c695e49811",
callback : function(res){
//Sample response from apiai here.
context.sendResponse(JSON.stringify(res));
}
},context)
}
function sessionId(context){
var userSession = context.simpledb.roomleveldata.sessionID;
if(!userSession){
userSession = new Date().getTime() +'api';
context.simpledb.roomleveldata.sessionID = userSession;
return userSession;
}else{
return userSession;
}
}
Remember that sessionId should not exceed 36 characters.
Suresh,
It seems you generate new session id for every request:
new Date().getTime() +'api'
But if you want to make contexts work it must be one fixed value for all requests belonging to one user. For example, you could use some global variable for it.

EWS managed API AutoDiscover is running very slow

I'm using EWS API for consuming outlook 365 mail service.
When I'm performing any mail operation it's running slow.
I have written the code mentioned below:
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
service.Credentials = new WebCredentials("usernm", "pwd");
service.EnableScpLookup = false;
service.AutodiscoverUrl("user",RedirectionUrlValidationCallback);
That last line takes 16 seconds before the connection is successful.
Is there any way to make the performance faster?
Hard to say...not saying this is the answer
but try this and let me know.
I see a difference in that i dont pass RedirectionUrlValidationCallback in the autoDiscoverUrl and I don't set EnableScpLookup flag, dont know what that is for. let me know
public ExchangeService GetService( string autoDiscoverEmailAddress, string authEmailAddress,string authEmailPassword, string domain = null, ExchangeVersion verion = ExchangeVersion.Exchange2010_SP2 )
{
try
{
ServicePointManager.ServerCertificateValidationCallback = CertificateValidationCallBack;
ExchangeService svc = new ExchangeService(verion);
//svc.UseDefaultCredentials = true;
if (!string.IsNullOrWhiteSpace(domain))
{
svc.Credentials = new WebCredentials(authEmailAddress, authEmailPassword, domain);
}
else
{
svc.Credentials = new WebCredentials(authEmailAddress, authEmailPassword);
}
svc.AutodiscoverUrl(autoDiscoverEmailAddress);
return svc;
}
catch (Exception)
{
throw;
}
}