Building your own Windows Live Messenger Events Agent
- Posted: Mar 08, 2007 at 5:06 AM
- 1,224 Views
- 5 Comments
Loading User Information from Channel 9
Something went wrong getting user information from Channel 9
Loading User Information from MSDN
Something went wrong getting user information from MSDN
Loading Visual Studio Achievements
Something went wrong getting the Visual Studio Achievements
|
This articles shows how to build a Windows Live Messenger Add-in that connects to Windows Sharepoint Services. |
|
Difficulty: Intermediate
Time Required:
1-3 hours
Cost: Free
Software: Visual Basic or Visual C# Express Editions,
Windows Live Messenger, Windows Sharepoint Services 2.0 with Service Pack 2
Hardware: None
Download: Download
|
|
Summary
The guide takes you on your new text-based adventure, as you'll learn to build a little gadget that will answer questions instead of you. This pretty piece of software is nothing more than a Windows Live Messenger Add-In that will register people for events that are tracked using Windows SharePoint Services. In the end, you'll be able to activate an agent on your messenger that will interact with your friends in live scenarios using text messages.
Introduction
Microsoft Windows SharePoint Services is a versatile technology that people can use to increase the efficiency of business processes and improve team productivity. Let's see how we can extend this wonderful technology: you are probably spending almost all your time online and using Windows Live Messenger to talk to your friends; more, you may be a very organized person that keeps track of every event (e.g. movie nights, snowboarding weekends etc.), so why not let your friends register for your events in a totally unique way. Sounds good? You'll even be able to supervise the conversation and supply answers to questions that your agent is not prepared to answer.
Prerequisites
Please download Windows Live Messenger and install it on your computer if you have not done so already. With Windows Live Messenger version 8, you can even talk with your Yahoo! friends; we'll talk about
Windows Live Messenger and Yahoo! Messenger later on.
I hope you are already using
Visual Studio Express
C# or VB since you're on the Coding4Fun web site (you're ok with any choice, as the sample is available is both languages).
You'll also need a place to set up your Windows SharePoint Services; depending on your mood, two options are available:
I personally tried them both and the project worked properly, so it's up to you. Now that you have everything up and ready, let's get started: it's Coding4Fun time!
Getting Started with Windows Live Messenger Add-Ins
From a developer's perspective, Windows Live Messenger delivers a great instant messaging platform that provides support for custom add-ins through its
Messenger Add-In API. However, this is not available out of the box; to take advantage of this feature, we must explicitly tell Windows Live Messenger that we want to use add-ins by tweaking
a registry key.
Open the Registry Editor and set HKEY_CURRENT_USER\Software\Microsoft\MSNMessenger\AddInFeatureEnabled to a
DWORD entry with the value equal to 1. It's time to check whether the change in the registry caused the desired effect in your
Personal Settings, as illustrated in [Fig. 1]. A new tab called Add-ins should be visible to you.
[Figure 1]
Getting Started with Windows SharePoint Services
Here, I'll try to make you understand how Windows SharePoint Services works. Please excuse me in case you find these obvious, or I'm repeating myself; it's better to clear things out from the early stages than getting into code and not understanding the
basic concepts.
Windows SharePoint Services is designed as a platform to support different types of sites, also named templates. Natively installed unique types in Windows SharePoint Services include the STS type, which defines the Team Site, Blank Site, and Document Workspace
configurations, and the MPS type, which defines the Basic Meeting Workspace, Blank Meeting Workspace, Decision Meeting Workspace, Social Meeting Workspace, and Multipage Meeting Workspace configurations; throughout the tutorial, we are going to work only with
Team Web Site and Basic Meeting Workspace.
Let's organize your default Team Web Site to be ready for use in the upcoming custom add-in; navigate to your default web site location, as shown in [Fig. 2].
Notice that the page contains several panels named Announcements, Events and Links; in SharePoint terms, these are called lists. We are going to work only with the Events list; you are not constrained to work only with these predefined lists, you can create your own or use other predefined lists at anytime; just needs a little more investigation from your side.
Adding a New Event to the Events List
It's simple to add a new event; just click Add new event below the Events list. You'll have to complete a three-page process in order to create a valid event.
That's it! A new event that uses a Meeting Workspace should take you to the new workspace web site, like in [Fig. 4]. Again, you can see that the page contains several panels named Objectives, Attendees, Agenda and Document Library; these again are lists.
At this point you got the idea: Windows SharePoint Services is made up from different types of sites (or sub-sites), and that each site (or sub-site) contains different types of lists depending on the template it's using.
The Attendees list already contains the email of the user who created it (that's me). When a friend of yours registers for an event (e.g. Microsoft Academic Tour), you should be able to see its email together with its response here. Every event has its own Meeting Workspace, so don't expect to see emails for all events here. Return to the Team Web Site Home (by clicking the Up to Team Web Site link located in the top-right corner) and repeat these steps as many times it's necessary. For example, you should add movie nights and snowboarding weekends as events; be sure to set up the occurrence date correctly as this is quite important when querying for events that occur within a week, month or year time.
Building the Windows Live Messenger Add-In
The current version of messenger provides only text message support for add-ins, although you have probably already seen that it's more than capable of handling sound and video when communicating with your friends; nevertheless it's enough for what we intend
to build. Fire up your code editor, we're a step closer from writing code!
The add-in will be able to:
Several basic requirements need to be met in order for an add-in to function as expected. Please note that they are already done and included in the sample. However, it's better for you to understand how it actually works and not just find them somewhere in the sample and wonder why did I do that:
Exchanging Text Messages
By this time, it's clear that we are going to work with many text messages; it's always better to keep text that you're probably going to modify in the near future (you may even want to localize the messages in your language) in resources. We have several types of text messages, which all are located in Resources.resx:
| Name | Value | Details |
| ITextMessage_AllEvents | show events | Request to see all upcoming events. |
| ITextMessage_YearEvents | show year events | Request to see all upcoming events in a year time. |
| ITextMessage_MonthEvents | show month events | Request to see all upcoming events in a month time. |
| ITextMessage_WeekEvents | show week events | Request to see all upcoming events in a week time. |
| ITextMessage_EventDetails | show details for event [event_id] | Request to see details for a selected event. |
| ITextMessage_RegisterForEvent | register for event [event_id] | Request to register for a selected event. |
| ITextMessage_UnregisterForEvent | unregister for event [event_id] | Request to unregister for a selected event. |
| ITextMessage_Help | help | Request to help. |
| Name | Value | Details |
| OTextMessage_Welcome | Hy {0}, I'm the Events Agent (*). Please ask me something like "show events", "show month events" or "help" etc. | Show that the add-in is alive and your friend is welcome to use it. {0} - your friend's name. |
| OTextMessage_EndWelcome | I can see that you are not busy, so why not register for one of the events... | When your friend's status is other than busy. |
| OTextMessage_BeginEvents | You are welcome to participate in {0} event(s): | {0} - number of events that matched your friend's query. |
| OTextMessage_EventDescription | Event {0} - {1}; | {0} - event id; {1} - event title. |
| OTextMessage_EndEvents | You can view details and register for any event by saying something like "show details for event 2" and "register for event 2". Hope you register! | Show a tip to remind your friend how it can register for events. |
| OTextMessage_ZeroAvailableEvents | I'm sorry |
When there is no event available that matched your friend's query. |
| OTextMessage_EventDetails | Event {0} - {1} takes place on {2} in {3}. | {0} - event id; {1} - event title; {2} - event date; {3} - event location. |
| OTextMessage_Help | You can talk to me by saying: show events, show year|month|week events, show details for event 2, register for event 1, unregister for event 2, help. | Show all available commands that the add-in is prepared to answer. |
| OTextMessage_RegisteredForEvent | You have been successfully |
{0} - event id; {1} - event title. |
| OTextMessage_NotRegisteredForEvent | You don't seem to be registered for event {0} - {1}. | {0} - event id; {1} - event title. |
| OTextMessage_UnregisteredForEvent | :-O It's a great event! I'm sorry you do not wish to attend anymore. You have been unregistered for event {0} - {1}. | {0} - event id; {1} - event title. |
| OTextMessage_InvalidEvent | Sorry, you wanted to perform an action on an invalid event. Try to query for events first, like "show events" etc. | When your friend uses an invalid event. |
| OTextMessage_Exception | Ooops! |
When an exception occurred; it can happen. |
| Name | Value | Details |
| MessengerAddIn_Description | Windows Live Messenger + Windows SharePoint Services Events | The description of the add-in. |
| MessengerAddIn_FriendlyName | Events Agent | The friendly name of the add-in. |
| MessengerAddIn_PersonalStatusMessage | Register For Events Now! | Your default personal status message when the add-in starts. |
| MessengerAddIn_PlusOnePersonalStatusMessage | + {0} Person(s) Registered! Registration Is Open! | Your personal status message when you have at least one person registered. {0} - number of registered friends. |
| Name | Value | Details |
| SharePoint_AttendeesListName | Attendees | No need to change in case you went with the default installation. |
| SharePoint_EventsListName | Events | No need to change in case you went with the default installation. |
| SharePoint_MeetingSeriesListName | Meeting Series | No need to change in case you went with the default installation. |
| SharePoint_ServerName | http://sharepoint.borza.ro | Modify to target your Windows SharePoint Services server name or web site that contains the Events list. |
The following rule applies to all text messages that are sent in and out the network: a text message can have only 400 characters; in case a reply exceeds this limit, it won't send the text message, and you'll just wonder what happened. Take into account that a large number of events may increase the size of a query reply considerably. In case you have many events, you'll have to create a paging mechanism that meets your needs.
Consuming Windows SharePoint Services Web Services
There are many web services that can be used to work remotely with a deployment of Windows SharePoint Services: Administration, Alerts, Document Workspace, Forms, Imaging, List Data Retrieval, Lists, Meetings, Permissions etc; we are going to learn how to use Lists and Meetings web services, since these are the only ones we need. When adding web references to the web services, make sure you enter the URLs like these: http://ServerName/_vti_bin/Lists.asmx?WSDL and http://ServerName/_vti_bin/Meetings.asmx?WSDL. I know that you are eager to write some code; hang on, we are just one paragraph away from coding.
Looking at the Windows Live Messenger Add-In Code
I thought I would never reach the coding stuff... we'll start by looking at the way the add-in comes to life. In MessengerAddIn.cs or MessengerAddIn.vb, the class MessengerAddIn (to remind you, the assembly name is EventsAgent.MessengerAddIn.dll) is implementing the Microsoft.Messenger.IMessengerAddIn.
Visual C#
14 public class MessengerAddIn : IMessengerAddIn
15 {
16 private MessengerClient MeClient;
17
18 private System.Collections.Generic.Dictionary<String, Conversation> People =
19 new Dictionary<string, Conversation>();
Visual Basic
14 Public Class MessengerAddIn
15 Implements IMessengerAddIn
16
17 Private MeClient As MessengerClient
18 Private People As Dictionary(Of String, Conversation) = New Dictionary(Of String, Conversation)()
Any class that implements this interface must also implement its Initialize method. The Initialize method is the first one which gets called when you click Turn on "Events Agent", as in [Fig. 5]; and receives the Windows Live Messenger client as an argument, which is saved in a local variable for use later on. One should use the initialization to properly change the friendly name, description and personal status message. The project only uses one messenger event, which is fired up each time a new text message arrives; in case you decide to use other events, just uncomment them.
Visual C#
25 public void Initialize(MessengerClient messenger)
26 {
27 MeClient = messenger;
28
29 //Messenger Add-In friendly texts
30 MeClient.AddInProperties.FriendlyName = Resources.MessengerAddIn_FriendlyName;
31 MeClient.AddInProperties.Description = Resources.MessengerAddIn_Description;
32 MeClient.AddInProperties.PersonalStatusMessage = Resources.MessengerAddIn_PersonalStatusMessage;
33
34 //Messenger text events
35 MeClient.IncomingTextMessage +=
36 new EventHandler<IncomingTextMessageEventArgs>(this.IncomingTextMessage);
37 //MeClient.OutgoingTextMessage +=
38 // new EventHandler<OutgoingTextMessageEventArgs>(this.OutgoingTextMessage);
39 //MeClient.ShowOptionsDialog += new EventHandler(this.ShowOptionsDialog);
40 }
Visual Basic
24 Public Sub Initialize(ByVal messenger As MessengerClient) Implements IMessengerAddIn.Initialize
25 MeClient = messenger
26
27 'Messenger Add-In friendly texts
28 MeClient.AddInProperties.FriendlyName = My.Resources.MessengerAddIn_FriendlyName
29 MeClient.AddInProperties.Description = My.Resources.MessengerAddIn_Description
30 MeClient.AddInProperties.PersonalStatusMessage = My.Resources.MessengerAddIn_PersonalStatusMessage
31
32 'Messenger text events
33 AddHandler MeClient.IncomingTextMessage, AddressOf Me.IncomingTextMessage
34 'AddHandler MeClient.OutgoingTextMessage, AddressOf Me.OutgoingTextMessage
35 'AddHandler MeClient.ShowOptionsDialog, AddressOf Me.ShowOptionsDialog
36 End Sub
For handling incoming text messages, I have written and registered my own event handler. The event is raised regardless who sent the text message, so we need a way to track users: have we talked with a user before, what kind of query has the user previously made etc.; this is achieved using a dictionary having the user's unique id as key, and an instance of the Conversation class as value. On line 58 (in C#) or 52 (in VB) you can see how easy it is to detect the user's status - nothing complicated. Take care at: Windows Live Messenger Add-Ins ca only send a (one) text message as a reply to a (one) incoming text message, that's why I always use a return statement after I send a message (an exception is raised when you try to send multiple messages). I don't like this issue, you don't like it, but it keeps me and you secure from malicious add-ins.
Visual C#
47 private void IncomingTextMessage(object sender, IncomingTextMessageEventArgs e)
48 {
49 // Check if the text message comes from a new user;
50 // with whom we have not talked before.
51 if (!People.ContainsKey(e.UserFrom.UniqueId))
52 {
53 People.Add(e.UserFrom.UniqueId, new Conversation());
54 string message = String.Format(CultureInfo.CurrentCulture,
55 Resources.OTextMessage_Welcome, e.UserFrom.FriendlyName);
56
57 // Verify the status of the user.
58 if (e.UserFrom.Status != UserStatus.Busy)
59 message += Resources.OTextMessage_EndWelcome;
60
61 MeClient.SendTextMessage(message, e.UserFrom);
62 return;
63 }
64
65 // Pull the previous conversation with the user.
66 Conversation person = null;
67 People.TryGetValue(e.UserFrom.UniqueId, out person);
Visual Basic
43 Private Sub IncomingTextMessage(ByVal sender As Object, ByVal e As IncomingTextMessageEventArgs)
44 ' Check if the text message comes from a new user;
45 ' with whom we have not talked before.
46 If Not People.ContainsKey(e.UserFrom.UniqueId) Then
47 People.Add(e.UserFrom.UniqueId, New Conversation)
48 Dim message As String = String.Format(CultureInfo.CurrentCulture, _
49 My.Resources.OTextMessage_Welcome, e.UserFrom.FriendlyName)
50
51 ' Verify the status of the user.
52 If (e.UserFrom.Status <> UserStatus.Busy) Then
53 message += My.Resources.OTextMessage_EndWelcome
54 End If
55
56 MeClient.SendTextMessage(message, e.UserFrom)
57 Return
58 End If
59
60 ' Pull the previous conversation with the user.
61 Dim person As Conversation = Nothing
62 People.TryGetValue(e.UserFrom.UniqueId, person)
What happens when a friend of yours sends you a show events text message and the add-in intercepts it? When a text message validates the condition from line 78-79 (in C#) or 72-73 (in VB), SharePointWrapper.GetEvents (we'll talk about this later on) is called and used to complete the request; plus, a friendly text message that reflects the previous call is built and sent back. You must always send the reply to the user from which the request came from; otherwise, an exception will be thrown.
Visual C#
78 if (e.TextMessage.StartsWith(Resources.ITextMessage_AllEvents,
79 true, CultureInfo.CurrentCulture))
80 {
81 try
82 {
83 person.Events = SharePointWrapper.GetEvents(
84 new Uri(Resources.SharePoint_ServerName), SharePointWrapper.QuerySpan.All);
85
86 MeClient.SendTextMessage(ReplyGetEvents(person.Events), e.UserFrom);
87 return;
88 }
89 catch (WebException)
90 {
91 MeClient.SendTextMessage(Resources.OTextMessage_Exception, e.UserFrom);
92 return;
93 }
94 }
Visual Basic
72 If e.TextMessage.StartsWith(My.Resources.ITextMessage_AllEvents, _
73 True, CultureInfo.CurrentCulture) Then
74 Try
75 person.Events = SharePointWrapper.GetEvents( _
76 New Uri(My.Resources.SharePoint_ServerName), SharePointWrapper.QuerySpan.All)
77
78 MeClient.SendTextMessage(ReplyGetEvents(person.Events), e.UserFrom)
79 Return
80 Catch ex As WebException
81 MeClient.SendTextMessage(My.Resources.OTextMessage_Exception, e.UserFrom)
82 Return
83 End Try
84 End If
The code that is executed when a user requests help, show events that occur in a week, month or year time is either similar with the previous code, or too simple and I'm not going to display it here. Instead, I'll show you what happens when a user requests to register for an event. A text message that would trigger the code looks like register for event 2; all I did was get the id from the incoming message and processed the request; the rest you'll understand as it looks almost the same. When a user wants to perform an action on an invalid event, the add-in notifies him or her of the mistake.
Visual C#
184 if (e.TextMessage.StartsWith(Resources.ITextMessage_RegisterForEvent,
185 true, CultureInfo.CurrentCulture))
186 {
187 try
188 {
189 int id = Convert.ToInt32(e.TextMessage.ToLower().Replace(
190 Resources.ITextMessage_RegisterForEvent, "").Trim(), CultureInfo.CurrentCulture);
191
192 MeClient.SendTextMessage(ReplyRegisterForEvent(
193 person.Events.Data.GetListItem(id), e.UserFrom.Email), e.UserFrom);
194 return;
195 }
196 catch (ArgumentNullException)
197 {
198 MeClient.SendTextMessage(Resources.OTextMessage_InvalidEvent, e.UserFrom);
199 return;
200 }
201 catch (WebException)
202 {
203 MeClient.SendTextMessage(Resources.OTextMessage_Exception, e.UserFrom);
204 return;
205 }
206 catch (Exception)
207 {
208 MeClient.SendTextMessage(Resources.OTextMessage_Exception, e.UserFrom);
209 return;
210 }
211 }
Visual Basic
153 If e.TextMessage.StartsWith(My.Resources.ITextMessage_RegisterForEvent, _
154 True, CultureInfo.CurrentCulture) Then
155 Try
156 Dim id As Integer = Convert.ToInt32(e.TextMessage.ToLower().Replace( _
157 My.Resources.ITextMessage_RegisterForEvent, "").Trim, CultureInfo.CurrentCulture)
158
159 MeClient.SendTextMessage(ReplyRegisterForEvent( _
160 person.Events.Data.GetListItem(id), e.UserFrom.Email), e.UserFrom)
161 Return
162 Catch ex As ArgumentNullException
163 MeClient.SendTextMessage(My.Resources.OTextMessage_InvalidEvent, e.UserFrom)
164 Return
165 Catch ex As WebException
166 MeClient.SendTextMessage(My.Resources.OTextMessage_Exception, e.UserFrom)
167 Return
168 Catch ex As Exception
169 MeClient.SendTextMessage(My.Resources.OTextMessage_Exception, e.UserFrom)
170 Return
171 End Try
172 End If
Requests to unregister for events and show details for an event are similar and there is no reason to talk about them. Each task (request) uses its own method to create its reply - that's were many conditions are checked and a proper response is built. Be sure to download the sample, which is available in both languages and discover the hidden aspects for yourself.
Looking at the Windows SharePoint Services Wrapper Code
Most Windows SharePoint Services web services use as arguments Collaborative Application Markup Language (CAML), so they're quite flexible. In case you want to do more with SharePoint than I have done here, you'll have to learn CAML. It's important to notice that the authentication to the web service, located on line 46 (in C#) or 43 (in VB), is made through the GetSharePointCredentials method; don't forget to supply your credentials as described within the body of the method.
Depending on the time span, the appropriate CAML query is built - it checks for a greater date than now and a less date than the one specified in the query span.
Right now I can think of three methods to process the response from the web services: navigate your way using xpaths, work with datasets or... deserialize the response into objects. Although you have to write more code, deserialization brings a huge benefit
- it's easier to work with later on; three classes were used to achieve that:
SharePointListRoot, SharePointListData and SharePointListItem.
Visual C#
37 public static SharePointListRoot GetEvents(Uri websiteUri, QuerySpan span)
38 {
39 if (websiteUri == null)
40 throw new ArgumentNullException("websiteUri");
41 try
42 {
43 // Work with the Lists web service
44 Lists wsLists = new Lists();
45 wsLists.Url = websiteUri.ToString() + "/_vti_bin/Lists.asmx";
46 wsLists.Credentials = GetSharePointCredentials();
47
48 XmlDocument xDocument = new XmlDocument();
49 XmlNode xQuery = xDocument.CreateNode(XmlNodeType.Element, "Query", "");
50 XmlNode xViewFields = xDocument.CreateNode(XmlNodeType.Element, "ViewFields", "");
51 XmlNode xQueryOptions = xDocument.CreateNode(XmlNodeType.Element, "QueryOptions", "");
52 string viewName = "";
53 string rowLimit = "0";
54
55 // CAML (Collaborative Application Markup Language)
56 switch (span)
57 {
58 case QuerySpan.All:
59 xQuery.InnerXml =
60 "<Where>" +
61 "<Gt><FieldRef Name='EventDate'/><Value Type='DateTime'>" +
62 DateTime.UtcNow.ToString("s", CultureInfo.InvariantCulture) + "</Value></Gt>" +
63 "</Where>";
64 break;
65 case QuerySpan.InAYear:
66 xQuery.InnerXml =
67 "<Where><And>" +
68 "<Gt><FieldRef Name='EventDate'/><Value Type='DateTime'>" +
69 DateTime.UtcNow.ToString("s", CultureInfo.InvariantCulture) + "</Value></Gt>" +
70 "<Lt><FieldRef Name='EventDate'/><Value Type='DateTime'>" +
71 DateTime.UtcNow.AddYears(1).ToString("s", CultureInfo.InvariantCulture) + "</Value></Lt>" +
72 "</And></Where>";
73 break;
74 case QuerySpan.InAMonth:
75 xQuery.InnerXml =
76 "<Where><And>" +
77 "<Gt><FieldRef Name='EventDate'/><Value Type='DateTime'>" +
78 DateTime.UtcNow.ToString("s", CultureInfo.InvariantCulture) + "</Value></Gt>" +
79 "<Lt><FieldRef Name='EventDate'/><Value Type='DateTime'>" +
80 DateTime.UtcNow.AddMonths(1).ToString("s", CultureInfo.InvariantCulture) + "</Value></Lt>" +
81 "</And></Where>";
82 break;
83 case QuerySpan.InAWeek:
84 xQuery.InnerXml =
85 "<Where><And>" +
86 "<Gt><FieldRef Name='EventDate'/><Value Type='DateTime'>" +
87 DateTime.UtcNow.ToString("s", CultureInfo.InvariantCulture) + "</Value></Gt>" +
88 "<Lt><FieldRef Name='EventDate'/><Value Type='DateTime'>" +
89 DateTime.UtcNow.AddDays(7).ToString("s", CultureInfo.InvariantCulture) + "</Value></Lt>" +
90 "</And></Where>";
91 break;
92 };
93
94 XmlNode xResult = wsLists.GetListItems(Resources.SharePoint_EventsListName,
95 viewName, xQuery, xViewFields, rowLimit, xQueryOptions);
96
97 // Create an SharePointListRoot object from the web response.
98 XmlTextReader xReader = new XmlTextReader(xResult.OuterXml, XmlNodeType.Element, null);
99 XmlSerializer xSerializer = new XmlSerializer(new SharePointListRoot().GetType());
100
101 return (SharePointListRoot)xSerializer.Deserialize(xReader);
102 }
103 catch (WebException)
104 {
105 throw;
106 }
107 }
Visual Basic
35 Public Shared Function GetEvents(ByVal websiteUri As Uri, ByVal span As QuerySpan) As SharePointListRoot
36 If (websiteUri Is Nothing) Then
37 Throw New ArgumentNullException("websiteUri")
38 End If
39 Try
40 ' Work with the Lists web service
41 Dim wsLists As Lists = New Lists()
42 wsLists.Url = websiteUri.ToString() & "/_vti_bin/Lists.asmx"
43 wsLists.Credentials = GetSharePointCredentials()
44
45 Dim xDocument As XmlDocument = New XmlDocument()
46 Dim xQuery As XmlNode = xDocument.CreateNode(XmlNodeType.Element, "Query", "")
47 Dim xViewFields As XmlNode = xDocument.CreateNode(XmlNodeType.Element, "ViewFields", "")
48 Dim xQueryOptions As XmlNode = xDocument.CreateNode(XmlNodeType.Element, "QueryOptions", "")
49 Dim viewName As String = ""
50 Dim rowLimit As String = "0"
51
52 ' CAML (Collaborative Application Markup Language)
53 Select Case (span)
54 Case QuerySpan.All
55 xQuery.InnerXml = _
56 "<Where>" & _
57 "<Gt><FieldRef Name='EventDate'/><Value Type='DateTime'>" & _
58 DateTime.UtcNow.ToString("s", CultureInfo.InvariantCulture) & "</Value></Gt>" & _
59 "</Where>"
60 Case QuerySpan.InAYear
61 xQuery.InnerXml = _
62 "<Where><And>" & _
63 "<Gt><FieldRef Name='EventDate'/><Value Type='DateTime'>" & _
64 DateTime.UtcNow.ToString("s", CultureInfo.InvariantCulture) & "</Value></Gt>" & _
65 "<Lt><FieldRef Name='EventDate'/><Value Type='DateTime'>" & _
66 DateTime.UtcNow.AddYears(1).ToString("s", CultureInfo.InvariantCulture) & "</Value></Lt>" & _
67 "</And></Where>"
68 Case QuerySpan.InAMonth
69 xQuery.InnerXml = _
70 "<Where><And>" & _
71 "<Gt><FieldRef Name='EventDate'/><Value Type='DateTime'>" & _
72 DateTime.UtcNow.ToString("s", CultureInfo.InvariantCulture) & "</Value></Gt>" & _
73 "<Lt><FieldRef Name='EventDate'/><Value Type='DateTime'>" & _
74 DateTime.UtcNow.AddMonths(1).ToString("s", CultureInfo.InvariantCulture) & "</Value></Lt>" & _
75 "</And></Where>"
76 Case QuerySpan.InAWeek
77 xQuery.InnerXml = _
78 "<Where><And>" & _
79 "<Gt><FieldRef Name='EventDate'/><Value Type='DateTime'>" & _
80 DateTime.UtcNow.ToString("s", CultureInfo.InvariantCulture) & "</Value></Gt>" & _
81 "<Lt><FieldRef Name='EventDate'/><Value Type='DateTime'>" & _
82 DateTime.UtcNow.AddDays(7).ToString("s", CultureInfo.InvariantCulture) & "</Value></Lt>" & _
83 "</And></Where>"
84 End Select
85
86 Dim xResult As XmlNode = wsLists.GetListItems(My.Resources.SharePoint_EventsListName, _
87 viewName, xQuery, xViewFields, rowLimit, xQueryOptions)
88
89 ' Create an SharePointListRoot object from the web response.
90 Dim xReader As XmlTextReader = New XmlTextReader(xResult.OuterXml, XmlNodeType.Element, Nothing)
91 Dim xSerializer As XmlSerializer = New XmlSerializer(New SharePointListRoot().GetType())
92
93 Return CType(xSerializer.Deserialize(xReader), SharePointListRoot)
94 Catch ex As WebException
95 Throw
96 End Try
97 End Function
When you want to add a new attendee, you basically need to add a new item to the Attendees list. A trick to notice: every site has its own web services, so you'll need to change the URL of the web service to target the web service of the workspace site that contains the Attendees list you want to modify; otherwise, a null reference exception will be raised. For example, a Team Web Site is not a Meeting Workspace and does not have a meetings web service. Always take care at this issue when you work with SharePoint web services.
Visual C#
115 public static bool AddAttendee(Uri workspaceUri, string email)
116 {
117 if (workspaceUri == null)
118 throw new ArgumentNullException("workspaceUri");
119 try
120 {
121 // Work with the Lists web service
122 Lists wsLists = new Lists();
123 wsLists.Url = workspaceUri.GetLeftPart(UriPartial.Path) + "/_vti_bin/Lists.asmx";
124 wsLists.Credentials = GetSharePointCredentials();
125
126 XmlDocument xDocument = new XmlDocument();
127 XmlNode xUpdates = xDocument.CreateNode(XmlNodeType.Element, "Batch", "");
128
129 // CAML (Collaborative Application Markup Language)
130 xUpdates.InnerXml =
131 "<Method ID='1' Cmd='New'>" +
132 "<Field Name='Title'>" + email + "</Field>" +
133 "<Field Name='Status'>Accepted</Field>" +
134 "<Field Name='Attendance'>Optional</Field>" +
135 "</Method>";
136
137 XmlNode xResult = wsLists.UpdateListItems(Resources.SharePoint_AttendeesListName,
138 xUpdates);
139
140 // Since we are performing only one action (a new), we get as response only one node;
141 // we check to see if there was an error completing the task.
142 return (xResult.ChildNodes[0].ChildNodes[0].InnerText == "0x00000000");
143 }
144 catch (WebException)
145 {
146 throw;
147 }
148 }
Visual Basic
105 Public Shared Function AddAttendee(ByVal workspaceUri As Uri, ByVal email As String) As Boolean
106 If (workspaceUri Is Nothing) Then
107 Throw New ArgumentNullException("workspaceUri")
108 End If
109 Try
110 ' Work with the Lists web service
111 Dim wsLists As Lists = New Lists()
112 wsLists.Url = workspaceUri.GetLeftPart(UriPartial.Path) & "/_vti_bin/Lists.asmx"
113 wsLists.Credentials = GetSharePointCredentials()
114
115 Dim xDocument As XmlDocument = New XmlDocument()
116 Dim xUpdates As XmlNode = xDocument.CreateNode(XmlNodeType.Element, "Batch", "")
117
118 ' CAML (Collaborative Application Markup Language)
119 xUpdates.InnerXml = _
120 "<Method ID='1' Cmd='New'>" & _
121 "<Field Name='Title'>" & email & "</Field>" & _
122 "<Field Name='Status'>Accepted</Field>" & _
123 "<Field Name='Attendance'>Optional</Field>" & _
124 "</Method>"
125
126 Dim xResult As XmlNode = wsLists.UpdateListItems(My.Resources.SharePoint_AttendeesListName, _
127 xUpdates)
128
129 ' Since we are performing only one action (a new), we get as response only one node;
130 ' we check to see if there was an error completing the task.
131 Return (xResult.ChildNodes(0).ChildNodes(0).InnerText = "0x00000000")
132 Catch ex As WebException
133 Throw
134 End Try
135 End Function
When a user wants to re-register for an event, we won't add it again to the Attendees list, because we would have duplicate items; instead, we are going to use the Meetings web service to set its response back from Declined to Accepted, or from Accepted to Declined, in the unregister case. Again, the URLs targeting the web services are set as they should be. This is one of the places you need to modify in order to make it work with non-recurrent events; check the comments within the code for more details.
Visual C#
198 public static void SetAttendee(Uri workspaceUri, string email, AttendeeResponse response)
199 {
200 if (workspaceUri == null)
201 throw new ArgumentNullException("workspaceUri");
202 try
203 {
204 // Work with the Lists web service
205 Lists wsLists = new Lists();
206 wsLists.Url = workspaceUri.GetLeftPart(UriPartial.Path) + "/_vti_bin/Lists.asmx";
207 wsLists.Credentials = GetSharePointCredentials();
208
209 // Also work with the Meetings web service
210 Meetings wsMeetings = new Meetings();
211 wsMeetings.Url = workspaceUri.GetLeftPart(UriPartial.Path) + "/_vti_bin/Meetings.asmx";
212 wsMeetings.Credentials = GetSharePointCredentials();
213
214 XmlDocument xDocument = new XmlDocument();
215 XmlNode xQuery = xDocument.CreateNode(XmlNodeType.Element, "Query", "");
216 XmlNode xViewFields = xDocument.CreateNode(XmlNodeType.Element, "ViewFields", "");
217 XmlNode xQueryOptions = xDocument.CreateNode(XmlNodeType.Element, "QueryOptions", "");
218 string viewName = "";
219 string rowLimit = "0";
220
221 // For non-recurring events, SharePoint sets the default id to 1;
222 // so it's safe to query only for the meeting with the instance id equal to 1.
223 xQuery.InnerXml =
224 "<Where>" +
225 "<Eq><FieldRef Name='ID'/><Value Type='Counter'>" + 1 + "</Value></Eq>" +
226 "</Where>";
227
228 XmlNode xResult = wsLists.GetListItems(Resources.SharePoint_MeetingSeriesListName,
229 viewName, xQuery, xViewFields, rowLimit, xQueryOptions);
230
231 // We know the instance id, but we need the unique id of the meeting.
232 string uid = xResult.ChildNodes[1].ChildNodes[1].Attributes["ows_EventUID"].InnerText;
233
234 wsMeetings.SetAttendeeResponse(email, 0, uid, 1, DateTime.UtcNow, DateTime.UtcNow, response);
235 }
236 catch (WebException)
237 {
238 throw;
239 }
240 }
Visual Basic
182 Public Shared Sub SetAttendee(ByVal workspaceUri As Uri, ByVal email As String, ByVal response As AttendeeResponse)
183 If (workspaceUri Is Nothing) Then
184 Throw New ArgumentNullException("workspaceUri")
185 End If
186 Try
187 ' Work with the Lists web service
188 Dim wsLists As Lists = New Lists()
189 wsLists.Url = workspaceUri.GetLeftPart(UriPartial.Path) + "/_vti_bin/Lists.asmx"
190 wsLists.Credentials = GetSharePointCredentials()
191
192 ' Also work with the Meetings web service
193 Dim wsMeetings As Meetings = New Meetings()
194 wsMeetings.Url = workspaceUri.GetLeftPart(UriPartial.Path) + "/_vti_bin/Meetings.asmx"
195 wsMeetings.Credentials = GetSharePointCredentials()
196
197 Dim xDocument As XmlDocument = New XmlDocument()
198 Dim xQuery As XmlNode = xDocument.CreateNode(XmlNodeType.Element, "Query", "")
199 Dim xViewFields As XmlNode = xDocument.CreateNode(XmlNodeType.Element, "ViewFields", "")
200 Dim xQueryOptions As XmlNode = xDocument.CreateNode(XmlNodeType.Element, "QueryOptions", "")
201 Dim viewName As String = ""
202 Dim rowLimit As String = "0"
203
204 ' For non-recurring events, SharePoint sets the default id to 1;
205 ' so it's safe to query only for the meeting with the instance id equal to 1.
206 xQuery.InnerXml = _
207 "<Where>" & _
208 "<Eq><FieldRef Name='ID'/><Value Type='Counter'>" & 1 & "</Value></Eq>" & _
209 "</Where>"
210
211 Dim xResult As XmlNode = wsLists.GetListItems(My.Resources.SharePoint_MeetingSeriesListName, _
212 viewName, xQuery, xViewFields, rowLimit, xQueryOptions)
213
214 ' We know the instance id, but we need the unique id of the meeting.
215 Dim uid As String = xResult.ChildNodes(1).ChildNodes(1).Attributes("ows_EventUID").InnerText
216
217 wsMeetings.SetAttendeeResponse(email, 0, uid, 1, DateTime.UtcNow, DateTime.UtcNow, response)
218 Catch ex As WebException
219 Throw
220 End Try
221 End Sub
This concludes our overview of the code. Not every line of code is displayed here, but the number preceding each line of code is the actual line number from the sample code files; so, if you have any questions about the code you'll be able to find it in a snap.
It Works on Windows Live Messenger Network
Although you can talk with your Yahoo! friends using text messages, Windows Live Messenger Add-Ins do not work with Yahoo! contacts; in other words, the add-in won't receive the text messages from them, although you see them, so it does not have how to send
text messages back. You'll have to find another suitable alternative for them to register for your events.
The usual way of testing a messenger add-in is to ask a friend of yours to send you text messages. Of course, you can have multiple Windows Live Ids and use
Virtual PC to install and run more messengers; but hey, I did not want to do that. So, at one moment I had an interesting idea: why not make
Encarta Instant Answers (which is a BOT) make it send me some text messages that matched my patterns (e.g. show events). In case you say to Encarta Instant Answers
show events, it will respond with something like: Why must I show events? (the response you get may differ). It should have worked, but unfortunately it's the same as in the Yahoo! case.
The good news is that it works with your friends that are connected to the Windows Live Messenger Network using Pocket MSN. I was curious and tried MSN Messenger on a device running Windows Mobile 5.1 Pocket PC Phone Edition and it worked fine; I was able to
interact with the add-in. Do not get me wrong, add-ins can be loaded only on a Windows Live Messenger, and not on a Pocket MSN.
And a dummy remark: people who use Windows Live Messenger have no problems using Windows Live Messenger Add-Ins.
Demo
In the end, I just want to show you how it works, because so far we have only talked around the subject without seeing it in action. Return to your Personal Settings > Add-ins, as illustrated in [Fig. 1] and click Add to Messenger; navigate to your sample installation folder and select the EventsAgent.MessengerAddIn.dll assembly. After completing this, the combo should have the Events Agent value; optionally, you may want to check the Automatically turn on this add-in when my status is anything other than Online or Appear Offline box. By clicking Turn on "Events Agent", like in [Fig. 6] you are activating your add-in and allowing it to interact with your friends, and this is what we expect it to do!
Before I give it a spin, please check my sample events that will be used to demonstrate the project. In [Fig. 6] you can see that I am using three events that occur at different time intervals, and all of them are using meeting workspaces to allow people to enroll in events as attendees.
It's spinning... in [Fig. 7] you can watch my conversation with one of my friends. A golden bar tells us that we have almost reached our goal; you'll be able to recognize text messages sent by the add-in when a message begins with a Paul-Valentin Borza's add-in "Events Agent" says: text.
Oh, you're right it's not my conversation - I haven't written a single word, or character: our project, the Windows Live Messenger Add-In Events Agent has done all the work for me. And that's great because that means my work here is done! Don't forget to look at the result: there are two attendees now.
Conclusion
I'm happy that you learned how to use and combine two powerful technologies: Windows Live Messenger Add-Ins and Windows SharePoint Services. You have built your own events agent that will serve you every time you start your favorite messenger. You'll now
be able to show it to your friends while talking to them, from the same windows you have always did before! I'm sure they'll be quite surprised... mine were. You can try my events agent on my Windows Live Messenger Id
windowslive@borza.ro; you'll even be able to talk with me in case you need any assistance.
Being at my second Coding4Fun article, I feel great when I can help people achieve a greater potential that has always been within their reach; and all of these happening while simply coding for fun. Thanks to the Microsoft Academic Program Team Romania for
support.
Improvements
I encourage you to extend my work by:
Bio
Paul-Valentin Borza is in its second year of study at the Babes-Bolyai University of Cluj-Napoca, Faculty of Mathematics and Computer Science. Since 2005, he is involved in the Microsoft Student Partners - Microsoft Academic Program Romania. He can be reached through his web site at www.borza.ro.
Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation,
please create a new thread in our Forums,
or
Contact Us and let us know.
Follow the Discussion
Oops, something didn't work.
What does this mean?
Following an item on Channel 9 allows you to watch for new content and comments that you are interested in. You need to be signed in to Channel 9 to use this feature.What does this mean?
Following an item on Channel 9 allows you to watch for new content and comments that you are interested in and view them all on your notifications page.sign up for email notifications?
My second (2nd!) Coding4Fun article just got published on MSDN Coding4Fun! I want to thank Todi Pruteanu...
Could u tell me how to change the nickname?
i couldn't reach the : HKEY_CURRENT_USER\Software\Microsoft\MSNMessenger\AddInFeatureEnabled by the way i m using WLM 8.5 any idea????
And why don't some channels show up, like mine!? ,
I cant find any MessengerClient.dll file on my PC.
I am using Windows Live Messenger V14. Are these addons still supported?
Remove this comment
Remove this thread
close