EWS PullSubscription for New or Modified Calendar Events - c#

I have been searching for an EWS pullsubscription example that allows me to get a list of calendar events that have been created or modified for a user since the pull subscription has been started. I have working code to get this info for the Inbox but I haven't found a good example for how to do this for the Calendar.
Below is an example for the Inbox; can anyone provide me with a link or a code example to accomplish the same thing for Calendar events or appointments using an Exchange Web Service pull subscription?
ExchangeService service;
PullSubscription subscriptionInbox;
private void SetService() {
service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
service.Url = new Uri("https://mail.myserver.com/EWS/Exchange.asmx");
}
private void SetSubscription() {
if(service == null) {
SetService();
}
// Subscribe to pull notifications in the Inbox folder, and get notified when
// a new mail is received, when an item or folder is created, or when an item
// or folder is deleted.
subscriptionInbox = service.SubscribeToPullNotifications(
new FolderId[] { WellKnownFolderName.Inbox },
5 /* timeOut: the subscription will end if the server is not polled within 5 minutes. */,
null /* watermark: null to start a new subscription. */,
EventType.NewMail, EventType.Modified);
}
private void btnGetLatestMessages_Click(object sender, EventArgs e) {
if(subscriptionInbox == null) {
SetSubscription();
}
GetEventsResults eventsInbox = subscriptionInbox.GetEvents();
EmailMessage message;
// Loop through all item-related events.
foreach(ItemEvent itemEvent in eventsInbox.ItemEvents) {
switch(itemEvent.EventType) {
case EventType.NewMail:
try {
Item item = Item.Bind(service, itemEvent.ItemId);
if(item.ItemClass.ToLower() == "IPM.Note".ToLower()) {
message = EmailMessage.Bind(service, itemEvent.ItemId);
MessageBox.Show("Inbox/NewMail - " + message.Subject);
}
} catch(Exception ex) {
MessageBox.Show("EventType.NewMail - " + itemEvent.ItemId);
}
break;
case EventType.Modified:
try {
Item item = Item.Bind(service, itemEvent.ItemId);
if(item.ItemClass.ToLower() == "IPM.Note".ToLower()) {
message = EmailMessage.Bind(service, itemEvent.ItemId);
MessageBox.Show("Inbox/Modified - " + message.Subject);
}
} catch(Exception ex) {
MessageBox.Show("EventType.NewMail - " + itemEvent.ItemId);
}
break;
}
}
}

Simply subscribe to the calendar instead of your inbox with the event types specified.
var subscriptionCalendar = service.SubscribeToPullNotifications(
new[] { new FolderId(WellKnownFolderName.Calendar) },
1440,
null,
EventType.Created, EventType.Modified);
Alternatively, you could create one pull notification for FolderId(WellKnownFolderName.Inbox) and FolderId(WellKnownFolderName.Calendar) with the EventType's you want.

Related

how to get information from user on alexa skill launch

I've got an Alexa app that on first launch looks for the user's id in a dynamoDB. If it isn't there I'd like it to ask the user for their ip address.
I have an intent that can collect the IP but I was wondering if I could trigger the intent from the launch request?
private SkillResponse LaunchRequestHandler(SkillRequest input, ILambdaContext context)
{
// Initialise response
var skillResponse = new SkillResponse
{
Version = "1.0",
Response = new ResponseBody()
};
// Output speech
SsmlOutputSpeech ssmlResponse = new SsmlOutputSpeech();
try
{
try
{
var strUserId = input.Session.User.UserId;
var request = new GetItemRequest
{
TableName = tableName,
Key = new Dictionary<string, AttributeValue>() { { "strUserId", new AttributeValue { S = strUserId } } },
};
var response = client.GetItemAsync(request);
// Check the response.
var result = response.Result;
var attributeMap = result.Item;
if (result.Item.Count() < 1)
{
ssmlResponse.Ssml = "<speak></speak>";
// Trigger intent to get IP address and port number.
}
else
{
ssmlResponse.Ssml = "<speak>Hi there. I'm not Cortana.</speak>";
// Give command guidance prompt.
}
}
catch (AmazonDynamoDBException e) { ssmlResponse.Ssml = "<speak>" + e.InnerException.Message + "</speak>"; }
catch (AmazonServiceException e) { ssmlResponse.Ssml = "<speak>" + e.Message + "</speak>"; }
catch (Exception e) { ssmlResponse.Ssml = "<speak>" + e.Message + "</speak>"; }
skillResponse.Response.OutputSpeech = ssmlResponse;
skillResponse.Response.ShouldEndSession = true;
}
catch
{
//ssmlResponse.Ssml = "<speak><audio src='/samples/ImSorryDave'/></speak>";
ssmlResponse.Ssml = "<speak>I'm sorry Dave. I'm afraid I can't do that.</speak>";
skillResponse.Response.OutputSpeech = ssmlResponse;
}
skillResponse.Response.ShouldEndSession = true;
return skillResponse;
}
Two options I can think of:
Have you tried just asking for the IP address? <speak> Hi there. What is your IP address?</speak> If you make sure your IP address intent has examples of how you might expect a user to respond to that question, that intent should be the triggered and sent to your skill to process.
Also look into how Alexa can handle some of the dialog management for you with intent chaining. The example there sounds very similar to your use case.

Adwords Api How to Pause Ads on Ad level

I am trying to PAUSE the Ads with the AdId and AdGroupID. I have successfully paused an AdGroup but i want to pause Ads individually. Is this possible on Adwords API. I tried the code below but It seems it only works on AdGroup level. Also checked the AdService but seems that there is no option to edit the Status.
I am using Ads.AdWords.v201809
Thanks in advance
public void googleEnableDisableAds(AdWordsUser user, long adGroupId, long AdID, AdGroupAdStatus AdStatus)
{
using (AdGroupAdService adGroupAdService =
(AdGroupAdService)user.GetService(AdWordsService.v201809.AdGroupAdService))
{
List<AdGroupAdOperation> operations = new List<AdGroupAdOperation>();
// Create the expanded text ad.
ExpandedTextAd expandedTextAd = new ExpandedTextAd
{
//CR[i].
id = AdID
};
AdGroupAd expandedTextAdGroupAd = new AdGroupAd
{
adGroupId = adGroupId,
ad = expandedTextAd,
// Optional: Set the status.
status = AdStatus
};
// Create the operation.
AdGroupAdOperation operation = new AdGroupAdOperation
{
#operator = Operator.SET,
operand = expandedTextAdGroupAd
};
operations.Add(operation);
AdGroupAdReturnValue retVal = null;
try
{
if (operations.Count > 0)
{
// Create the ads.
retVal = adGroupAdService.mutate(operations.ToArray());
// Display the results.
if (retVal != null && retVal.value != null)
{
foreach (AdGroupAd adGroupAd in retVal.value)
{
ExpandedTextAd newAd = adGroupAd.ad as ExpandedTextAd;
Console.WriteLine(
"Expanded text ad with ID '{0}' and headline '{1} - {2}' " +
"was added.", newAd.id, newAd.headlinePart1, newAd.headlinePart2);
//adGroupId
}
}
else
{
Console.WriteLine("No expanded text ads were created.");
}
}
adGroupAdService.Close();
}
catch (Exception e)
{
throw new System.ApplicationException("Failed to create expanded text ad.", e);
}
}
}
Here is an example from API docs https://developers.google.com/adwords/api/docs/samples/csharp/basic-operations#pause-an-ad
The key idea is to set status property to PAUSED
AdGroupAdStatus status = AdGroupAdStatus.PAUSED

EWS - Get a custom header

Hello I am using the following code to get the custom header from EWS.
But unfortunately it's not returning the header. I looked into the outlook for the headers using Mapi tool, where I can see the headers.
Any suggestions please.
service = ExchangeServiceHelpers.GetBinding();
// Bind the Inbox folder to the service object
var inbox = Folder.Bind(service, WellKnownFolderName.Inbox);
var searchFilter = ExchangeServiceHelpers.PopulateSearchFilters();
var view = new ItemView(int.MaxValue); // Search operation should return maximum number of elements.
// Defines a property set that contains the schematized Internet message headers.
var headerProperty = new ExtendedPropertyDefinition(
DefaultExtendedPropertySet.InternetHeaders,
"x-worksitefolderemailid",
MapiPropertyType.String);
var columns = new PropertySet(BasePropertySet.IdOnly, EmailMessageSchema.InternetMessageId, headerProperty);
view.PropertySet = columns;
// Fire the query for the unread items
var findResults = inbox.FindItems(searchFilter, view);
// Loop through the search results.
foreach (EmailMessage message in findResults)
{
try
{
message.Load(
new PropertySet(new PropertyDefinitionBase[] { ItemSchema.MimeContent, ItemSchema.Subject}));
string mailAddress = GetFolderId(message, headerProperty); // Get internet header
if (string.IsNullOrEmpty(mailAddress))
{
Logger.Info(
string.Format("Email '{0}' doesn't have folder id address. Marking as Read Item.",
message.Subject));
ExchangeServiceHelpers.MarkMessageAsRead(service, message.Id); // Marking the email item as Read prevents the item to be returned in further search results.
continue;
}
}
catch (Exception e)
{
Logger.Error(e);
}
}
private static string GetFolderId(EmailMessage message, ExtendedPropertyDefinition headerProperty)
{
try
{
if (message.ExtendedProperties == null || message.ExtendedProperties.Count == 0)
{
Logger.Info(
string.Format("Email '{0}' doesn't have any extended properties. Marking as Read Item.",
message.Subject));
return string.Empty;
}
//message.InternetMessageHeaders
foreach (ExtendedProperty property in message.ExtendedProperties)
{
if (property.PropertyDefinition == headerProperty)
{
return property.Value.ToString();
}
}
}
catch (Exception ex)
{
Logger.Error(ex);
}
return string.Empty;
}
Naresh,
The inbox.FindItems() call won't return the internet headers. You'll need to update message.Load() to use a property set that includes headerProperty.
With regards,

How can I get task-specific properties from a MailItem

I've been having a little weekend project for myselff which involves getting all my ToDo tasks from Outlook, put them in a DataGridView and me being able to edit and export them.
The only problem I've been running into is me being unable to get the task specific properties for flagged emails while they still exist, I just don't see any way to access them.
Here is a portion of my current code.
private void retrieveTasks()
{
//Clear datagrid so we won't have duplicate information
taskList.Rows.Clear();
//Define some properties so we can use these to retrieve the tasks
Outlook.Application app = null;
_NameSpace ns = null;
Store outlookStore = null;
Outlook.MAPIFolder taskFolder = null;
Outlook.MAPIFolder specialFolder = null;
TaskItem task = null;
DateTime nonDate = new DateTime(4501, 1, 1);
try
{
//Connect to Outlook via MAPI
app = new Outlook.Application();
ns = app.GetNamespace("MAPI");
ns.Logon(null, null, false, false);
/*
outlookStore = ns.GetDefaultFolder(Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderInbox).Store;
taskFolder = outlookStore.GetSpecialFolder(Microsoft.Office.Interop.Outlook.OlSpecialFolders.olSpecialFolderAllTasks);
*/
//Get the taskfolder containing the Outlook tasks
//taskFolder = ns.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderTasks);
outlookStore = ns.GetDefaultFolder(Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderInbox).Store;
taskFolder = outlookStore.GetSpecialFolder(OlSpecialFolders.olSpecialFolderAllTasks);
//Console.WriteLine(specialFolder.Items[1].Subject.ToString());
/*for (int i = 1; i <= specialFolder.Items.Count; i++)
{
//task = (Outlook.TaskItem)specialFolder.Items[i];
Console.WriteLine(specialFolder.Items[i].ToString());
}*/
for (int i = 1; i <= taskFolder.Items.Count; i++)
{
//Get task from taskfolder
Object item = taskFolder.Items[i];
if (item is Outlook.MailItem)
{
MailItem mail = (Outlook.MailItem)item;
if (mail.TaskCompletedDate.Equals(nonDate))
{
string percentComplete = "";
string taskPrio = "";
if (mail.UserProperties.Find("Prio") == null)
{
taskPrio = "";
}
else
{
taskPrio = mail.UserProperties.Find("Prio").Value.ToString();
}
//mail.UserProperties.
if (mail.UserProperties.Find("% Complete") == null)
{
percentComplete = "";
}
else
{
percentComplete = mail.UserProperties.Find("% Complete").Value.ToString();
}
//Add the tasks details to the datagrid
taskList.Rows.Add(
i.ToString(),
checkForNull(mail.Subject),
parseDate(mail.TaskStartDate.ToString()),
parseDate(mail.TaskDueDate.ToString()),
percentComplete, "Taak voltooid",
//statusToFriendlyName(mail.Status.ToString()),
taskPrio
);
}
}
else if (item is Outlook.TaskItem)
{
task = (Outlook.TaskItem)item;
Console.WriteLine(task.Subject);
if (task.Complete == false)
{
string taskPrio = "";
//Make sure custom task property is failed or set it to empty text to prevent crashes
if (task.UserProperties.Find("Prio") == null)
{
taskPrio = "";
}
else
{
taskPrio = task.UserProperties.Find("Prio").Value.ToString();
}
//Add the tasks details to the datagrid
taskList.Rows.Add(
i.ToString(),
task.Subject.ToString(),
parseDate(task.StartDate.ToString()),
parseDate(task.DueDate.ToString()),
task.PercentComplete.ToString(),
statusToFriendlyName(task.Status.ToString()),
taskPrio
);
}
}
}
}
catch (System.Runtime.InteropServices.COMException ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
//Release Outlook sources
ns = null;
app = null;
}
}
So specifically I am looking for the "% complete" property and the status property, everything else I am able to work around on.
It would make my life SO MUCH EASIER if I can get this to work :)
Hope to hear from anyone on here!
Jeffrey
Do you mean you need to retrieve the task specific properties from a MailItem object (as opposed to the TaskItem object)?
Take a look at the message with OutlookSpy (I am its author) - click IMessage button, look at the MAPI properties in the GetProps tab. Any property can be accessed using MailItem.PropertyAccessor.GetProperty. The DASL name to be used by GetProperty is displayed by OutlookSpy (select the property in the IMessage window, see the DASL edit box).

EWS Save Guid per Calendar Appointment

I need to save a Guid per every Appointment.
I've tried to use PolicyTag and ArchiveTag,but got an ,
"The property PolicyTag is valid only for Exchange Exchange2013 or
later versions.",
exception.
Does we have something similar for Exchange 2010?
As i understand there is appointment.ID that contains self-generated id.
I prefer not to use it.
Thank you.
A way to deal with this problem is to create an extended property, and put guid for the appointment, and it wont change unless you made a copy from another appointment (after all it is just a property)
private static readonly PropertyDefinitionBase AppointementIdPropertyDefinition = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.PublicStrings, "AppointmentID", MapiPropertyType.String);
public static PropertySet PropertySet = new PropertySet(BasePropertySet.FirstClassProperties, AppointementIdPropertyDefinition);
//Setting the property for the appointment
public static void SetGuidForAppointement(Appointment appointment)
{
try
{
appointment.SetExtendedProperty((ExtendedPropertyDefinition)AppointementIdPropertyDefinition, Guid.NewGuid().ToString());
appointment.Update(ConflictResolutionMode.AlwaysOverwrite, SendInvitationsOrCancellationsMode.SendToNone);
}
catch (Exception ex)
{
// logging the exception
}
}
//Getting the property for the appointment
public static string GetGuidForAppointement(Appointment appointment)
{
var result = "";
try
{
appointment.Load(PropertySet);
foreach (var extendedProperty in appointment.ExtendedProperties)
{
if (extendedProperty.PropertyDefinition.Name == "AppointmentID")
{
result = extendedProperty.Value.ToString();
}
}
}
catch (Exception ex)
{
// logging the exception
}
return result;
}
This solution works very well in case of single appointments and in case of using On Premises Exchange. The problem here is in case of meetings in the Online Web Access (OWA), such as Office 365, the properties of the appointments are copied from the original appointment from the organizer, among these properties are the extended properties which the AppoinmentID is among them.
Therefore to avoid this trouble, we make the appointment id of the attendee similar one to the original in the organizer, and just add the email address for the service owner (the service that produced the notification).
Without this solution , the appointment in the internal system will have similar AppointmentID to the original booking and it will be considered as one, or you might have different two appointments with the same ID.
private static void SetGuidForMeetingAppiontment(Appointment appointment)
{
var log = "";
try
{
if (!appointment.IsMeeting) return;
if (appointment.Service.ImpersonatedUserId == null) return;
/*
* The only tricky case is that if the appointment is created at the attendee with no Guid.
* In this case the application should look for the original appointment from the organizer's side, and get its guid, to paste it in the new booking
* from the attendee side, and add the attendee emailAddress.
*/
if (GetGuidForMeetingAppointement(appointment).Length <= 36)
{
// If it was an attendee, then look for the original appointment from the organizer's service
if (appointment.Service.ImpersonatedUserId.Id != appointment.Organizer.Address)
{
log += "1/5 Getting the original event of the meeting\n";
if (ExchangeLiteService.Services.ContainsKey(appointment.Organizer.Address))
{
// FindItemsResults<Appointment> originalAppointments;
var originalAppointments = ExchangeLiteService.Services[appointment.Organizer.Address].FindAppointments(WellKnownFolderName.Calendar, new CalendarView(appointment.Start, appointment.End, 1));
if (originalAppointments == null) return; //there must be an original appointment.
if (!originalAppointments.Any()) return; //there must be an original appointment.
var originalAppointment = originalAppointments.First(); // there should be only one appointment at a specifict time and date.
log += "2/5 Getting the Guid for the original event of the meeting\n";
var originalAppointmentID = GetGuidForMeetingAppointement(originalAppointment);
if (string.IsNullOrEmpty(originalAppointmentID)) return; // the original appointment must have a guid already.
var orignalAppointmentIDGuid = originalAppointmentID.Substring(0, 36);
log += "3/5 Attaching the email address to the guid extracted\n";
var newAppointmentID = orignalAppointmentIDGuid + "_" + appointment.Service.ImpersonatedUserId.Id;
log += "4/5 Setting the new Guid to the meeting appointment\n";
appointment.SetExtendedProperty((ExtendedPropertyDefinition)AppointementIdPropertyDefinition, newAppointmentID);
log += "5/5 Updateing the meeting appointment\n";
appointment.Update(ConflictResolutionMode.AlwaysOverwrite, SendInvitationsOrCancellationsMode.SendToNone);
}
else //Then the user is invited from an organizer outside the system.
{
// Delete if there are anything similar
ExchangeLiteService.OnCallDeleteBookingFromInternal(appointment.Service.ImpersonatedUserId.Id, appointment.Start, appointment.End);
//Assign a new
var appointmentIDGuid = Guid.NewGuid().ToString();
var newAppointmentID = appointmentIDGuid + "_" + appointment.Service.ImpersonatedUserId.Id;
appointment.SetExtendedProperty((ExtendedPropertyDefinition)AppointementIdPropertyDefinition, newAppointmentID);
appointment.Update(ConflictResolutionMode.AlwaysOverwrite, SendInvitationsOrCancellationsMode.SendToNone);
}
//Get only the guid part of it (without the address of the organizer)
}
else // if he was the organizer
{
log += "If it was new meeting appointment and the notification from the organizer\n";
var appointmentIDGuid = Guid.NewGuid().ToString();
var newAppointmentID = appointmentIDGuid + "_" + appointment.Service.ImpersonatedUserId.Id;
appointment.SetExtendedProperty((ExtendedPropertyDefinition)AppointementIdPropertyDefinition, newAppointmentID);
appointment.Update(ConflictResolutionMode.AlwaysOverwrite, SendInvitationsOrCancellationsMode.SendToNone);
}
}
else
{
log += "If it was an updated meeting appointment and has Guid already\n";
var appointmentID = GetGuidForMeetingAppointement(appointment);
var appointmentIDGuid = appointmentID.Substring(0, 36);
var newAppointmentID = appointmentIDGuid + "_" + appointment.Service.ImpersonatedUserId.Id;
appointment.SetExtendedProperty((ExtendedPropertyDefinition)AppointementIdPropertyDefinition, newAppointmentID);
appointment.Update(ConflictResolutionMode.AlwaysOverwrite, SendInvitationsOrCancellationsMode.SendToNone);
}
}
catch (Exception ex)
{
//Logging the exception
}
}

Categories

Resources