~ubuntu-branches/ubuntu/natty/smuxi/natty

« back to all changes in this revision

Viewing changes to src/Engine-Twitter/Protocols/Twitter/TwitterProtocolManager.cs

  • Committer: Bazaar Package Importer
  • Author(s): Mirco Bauer
  • Date: 2010-01-11 22:47:12 UTC
  • mfrom: (1.2.3 upstream)
  • mto: This revision was merged to the branch mainline in revision 13.
  • Revision ID: james.westby@ubuntu.com-20100111224712-zyrhyny5gbx4t3cv
Tags: upstream-0.7
ImportĀ upstreamĀ versionĀ 0.7

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// $Id$
 
2
// 
 
3
// Smuxi - Smart MUltipleXed Irc
 
4
// 
 
5
// Copyright (c) 2009 Mirco Bauer <meebey@meebey.net>
 
6
// 
 
7
// Full GPL License: <http://www.gnu.org/licenses/gpl.txt>
 
8
// 
 
9
// This program is free software; you can redistribute it and/or modify
 
10
// it under the terms of the GNU General Public License as published by
 
11
// the Free Software Foundation; either version 2 of the License, or
 
12
// (at your option) any later version.
 
13
// 
 
14
// This program is distributed in the hope that it will be useful,
 
15
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
16
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
17
// GNU General Public License for more details.
 
18
// 
 
19
// You should have received a copy of the GNU General Public License
 
20
// along with this program; if not, write to the Free Software
 
21
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 
22
 
 
23
using System;
 
24
using System.Threading;
 
25
using System.Collections.Generic;
 
26
using Twitterizer.Framework;
 
27
using Smuxi.Common;
 
28
 
 
29
namespace Smuxi.Engine
 
30
{
 
31
    public enum TwitterChatType {
 
32
        FriendsTimeline,
 
33
        Replies,
 
34
        DirectMessages
 
35
    }
 
36
 
 
37
    [ProtocolManagerInfo(Name = "Twitter", Description = "Twitter Micro-Blogging", Alias = "twitter")]
 
38
    public class TwitterProtocolManager : ProtocolManagerBase
 
39
    {
 
40
#if LOG4NET
 
41
        private static readonly log4net.ILog f_Logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
 
42
#endif
 
43
        static readonly string f_LibraryTextDomain = "smuxi-engine-twitter";
 
44
        static readonly TextColor f_BlueTextColor = new TextColor(0x0000FF);
 
45
        Twitter                 f_Twitter;
 
46
        TwitterUser             f_TwitterUser;
 
47
        string                  f_Username;
 
48
        ProtocolChatModel       f_ProtocolChat;
 
49
        List<PersonModel>       f_Friends;
 
50
        List<GroupChatModel>    f_GroupChats = new List<GroupChatModel>();
 
51
 
 
52
        GroupChatModel          f_FriendsTimelineChat;
 
53
        AutoResetEvent          f_FriendsTimelineEvent = new AutoResetEvent(false);
 
54
        Thread                  f_UpdateFriendsTimelineThread;
 
55
        int                     f_UpdateFriendsTimelineInterval = 120;
 
56
        Int64?                  f_LastFriendsTimelineStatusID;
 
57
        DateTime                f_LastFriendsUpdate;
 
58
 
 
59
        GroupChatModel          f_RepliesChat;
 
60
        Thread                  f_UpdateRepliesThread;
 
61
        int                     f_UpdateRepliesInterval = 120;
 
62
        Int64?                  f_LastReplyStatusID;
 
63
 
 
64
        GroupChatModel          f_DirectMessagesChat;
 
65
        AutoResetEvent          f_DirectMessageEvent = new AutoResetEvent(false);
 
66
        Thread                  f_UpdateDirectMessagesThread;
 
67
        int                     f_UpdateDirectMessagesInterval = 120;
 
68
        Int64?                  f_LastDirectMessageReceivedStatusID;
 
69
        Int64?                  f_LastDirectMessageSentStatusID;
 
70
 
 
71
        bool                    f_Listening;
 
72
        bool                    f_IsConnected;
 
73
 
 
74
        public override string NetworkID {
 
75
            get {
 
76
                return "Twitter";
 
77
            }
 
78
        }
 
79
 
 
80
        public override string Protocol {
 
81
            get {
 
82
                return "Twitter";
 
83
            }
 
84
        }
 
85
 
 
86
        public override ChatModel Chat {
 
87
            get {
 
88
                return f_ProtocolChat;
 
89
            }
 
90
        }
 
91
 
 
92
        public TwitterProtocolManager(Session session) : base(session)
 
93
        {
 
94
            Trace.Call(session);
 
95
 
 
96
            f_ProtocolChat = new ProtocolChatModel(NetworkID, "Twitter", this);
 
97
 
 
98
            f_FriendsTimelineChat = new GroupChatModel(
 
99
                TwitterChatType.FriendsTimeline.ToString(),
 
100
                _("Friends Timeline"),
 
101
                this
 
102
            );
 
103
            f_GroupChats.Add(f_FriendsTimelineChat);
 
104
 
 
105
            f_RepliesChat = new GroupChatModel(
 
106
                TwitterChatType.Replies.ToString(),
 
107
                _("Replies"),
 
108
                this
 
109
            );
 
110
            f_GroupChats.Add(f_RepliesChat);
 
111
 
 
112
            f_DirectMessagesChat = new GroupChatModel(
 
113
                TwitterChatType.DirectMessages.ToString(),
 
114
                _("Direct Messages"),
 
115
                this
 
116
            );
 
117
            f_GroupChats.Add(f_DirectMessagesChat);
 
118
        }
 
119
 
 
120
        public override void Connect(FrontendManager fm, string host, int port,
 
121
                                     string username, string password)
 
122
        {
 
123
            Trace.Call(fm, host, port, username, "XXX");
 
124
 
 
125
            f_Username = username;
 
126
            f_Twitter = new Twitter(username, password, "Smuxi");
 
127
 
 
128
            Session.AddChat(f_ProtocolChat);
 
129
            Session.SyncChat(f_ProtocolChat);
 
130
 
 
131
            string msg;
 
132
            msg = String.Format(_("Connecting to Twitter..."));
 
133
            fm.SetStatus(msg);
 
134
            Session.AddTextToChat(f_ProtocolChat, "-!- " + msg);
 
135
            try {
 
136
                // for some reason VerifyCredentials() always fails
 
137
                //bool login = Twitter.VerifyCredentials(username, password);
 
138
                bool login = true;
 
139
                if (!login) {
 
140
                    fm.SetStatus(_("Login failed!"));
 
141
                    Session.AddTextToChat(f_ProtocolChat,
 
142
                        "-!- " + _("Login failed! Username and/or password are " +
 
143
                        "incorrect.")
 
144
                    );
 
145
                    return;
 
146
                }
 
147
            } catch (Exception ex) {
 
148
                fm.SetStatus(_("Connection failed!"));
 
149
                Session.AddTextToChat(f_ProtocolChat,
 
150
                    "-!- " + _("Connection failed! Reason: ") + ex.Message
 
151
                );
 
152
                return;
 
153
            }
 
154
            f_IsConnected = true;
 
155
            msg =_("Successfully connected to Twitter.");
 
156
            fm.SetStatus(msg);
 
157
            Session.AddTextToChat(f_ProtocolChat, "-!- " + msg);
 
158
 
 
159
            f_Listening = true;
 
160
            // twitter is sometimes pretty slow, so fetch this in the background
 
161
            ThreadPool.QueueUserWorkItem(delegate {
 
162
                try {
 
163
                    msg =_("Fetching user details from Twitter, please wait...");
 
164
                    Session.AddTextToChat(f_ProtocolChat, "-!- " + msg);
 
165
 
 
166
                    UpdateUser();
 
167
 
 
168
                    msg =_("Finished fetching user details.");
 
169
                    Session.AddTextToChat(f_ProtocolChat, "-!- " + msg);
 
170
 
 
171
                    f_FriendsTimelineChat.PersonCount = f_TwitterUser.NumberOfFriends;
 
172
                    f_RepliesChat.PersonCount = f_TwitterUser.NumberOfFriends;
 
173
                    f_DirectMessagesChat.PersonCount = f_TwitterUser.NumberOfFriends;
 
174
                } catch (Exception ex) {
 
175
                    msg =_("Failed to fetch user details from Twitter. Reason: ");
 
176
#if LOG4NET
 
177
                    f_Logger.Error("Connect(): " + msg, ex);
 
178
#endif
 
179
                    Session.AddTextToChat(f_ProtocolChat, "-!- " + msg + ex.Message);
 
180
                }
 
181
            });
 
182
            ThreadPool.QueueUserWorkItem(delegate {
 
183
                try {
 
184
                    msg =_("Fetching friends from Twitter, please wait...");
 
185
                    Session.AddTextToChat(f_ProtocolChat, "-!- " + msg);
 
186
 
 
187
                    UpdateFriends();
 
188
 
 
189
                    msg =_("Finished fetching friends.");
 
190
                    Session.AddTextToChat(f_ProtocolChat, "-!- " + msg);
 
191
                } catch (Exception ex) {
 
192
                    msg =_("Failed to fetch friends from Twitter. Reason: ");
 
193
#if LOG4NET
 
194
                    f_Logger.Error("Connect(): " + msg, ex);
 
195
#endif
 
196
                    Session.AddTextToChat(f_ProtocolChat, "-!- " + msg + ex.Message);
 
197
                }
 
198
            });
 
199
 
 
200
            OpenFriendsTimelineChat();
 
201
            OpenRepliesChat();
 
202
            OpenDirectMessagesChat();
 
203
        }
 
204
 
 
205
        public override void Reconnect(FrontendManager fm)
 
206
        {
 
207
            Trace.Call(fm);
 
208
        }
 
209
 
 
210
        public override void Disconnect(FrontendManager fm)
 
211
        {
 
212
            Trace.Call(fm);
 
213
 
 
214
            f_Listening = false;
 
215
            f_FriendsTimelineEvent.Set();
 
216
        }
 
217
 
 
218
        public override IList<GroupChatModel> FindGroupChats(GroupChatModel filter)
 
219
        {
 
220
            Trace.Call(filter);
 
221
 
 
222
            return f_GroupChats;
 
223
        }
 
224
 
 
225
        public override void OpenChat(FrontendManager fm, ChatModel chat)
 
226
        {
 
227
            Trace.Call(fm, chat);
 
228
 
 
229
            if (chat.ChatType == ChatType.Group) {
 
230
               TwitterChatType twitterChatType = (TwitterChatType)
 
231
                    Enum.Parse(typeof(TwitterChatType), chat.ID);
 
232
               switch (twitterChatType) {
 
233
                    case TwitterChatType.FriendsTimeline:
 
234
                        OpenFriendsTimelineChat();
 
235
                        break;
 
236
                    case TwitterChatType.Replies:
 
237
                        OpenRepliesChat();
 
238
                        break;
 
239
                    case TwitterChatType.DirectMessages:
 
240
                        OpenDirectMessagesChat();
 
241
                        break;
 
242
                }
 
243
                return;
 
244
            }
 
245
 
 
246
            OpenPrivateChat(chat.ID);
 
247
        }
 
248
 
 
249
        private void OpenFriendsTimelineChat()
 
250
        {
 
251
            ChatModel chat =  Session.GetChat(
 
252
                TwitterChatType.FriendsTimeline.ToString(),
 
253
                ChatType.Group,
 
254
                this
 
255
            );
 
256
 
 
257
            if (chat != null) {
 
258
                return;
 
259
            }
 
260
 
 
261
            // BUG: causes a race condition as the frontend syncs the
 
262
            // unpopulated chat! So only add it if it's ready
 
263
            //Session.AddChat(f_FriendsTimelineChat);
 
264
            f_UpdateFriendsTimelineThread = new Thread(
 
265
                new ThreadStart(UpdateFriendsTimelineThread)
 
266
            );
 
267
            f_UpdateFriendsTimelineThread.IsBackground = true;
 
268
            f_UpdateFriendsTimelineThread.Name =
 
269
                "TwitterProtocolManager friends timeline listener";
 
270
            f_UpdateFriendsTimelineThread.Start();
 
271
        }
 
272
 
 
273
        private void OpenRepliesChat()
 
274
        {
 
275
            ChatModel chat =  Session.GetChat(
 
276
                TwitterChatType.Replies.ToString(),
 
277
                ChatType.Group,
 
278
                this
 
279
            );
 
280
 
 
281
            if (chat != null) {
 
282
                return;
 
283
            }
 
284
 
 
285
            // BUG: causes a race condition as the frontend syncs the
 
286
            // unpopulated chat! So only add it if it's ready
 
287
            //Session.AddChat(f_RepliesChat);
 
288
            f_UpdateRepliesThread = new Thread(
 
289
                new ThreadStart(UpdateRepliesThread)
 
290
            );
 
291
            f_UpdateRepliesThread.IsBackground = true;
 
292
            f_UpdateRepliesThread.Name =
 
293
                "TwitterProtocolManager replies listener";
 
294
            f_UpdateRepliesThread.Start();
 
295
        }
 
296
 
 
297
        private void OpenDirectMessagesChat()
 
298
        {
 
299
            ChatModel chat =  Session.GetChat(
 
300
                TwitterChatType.DirectMessages.ToString(),
 
301
                ChatType.Group,
 
302
                this
 
303
            );
 
304
 
 
305
            if (chat != null) {
 
306
                return;
 
307
            }
 
308
 
 
309
            // BUG: causes a race condition as the frontend syncs the
 
310
            // unpopulated chat! So only add it if it's ready
 
311
            //Session.AddChat(f_DirectMessagesChat);
 
312
            f_UpdateDirectMessagesThread = new Thread(
 
313
                new ThreadStart(UpdateDirectMessagesThread)
 
314
            );
 
315
            f_UpdateDirectMessagesThread.IsBackground = true;
 
316
            f_UpdateDirectMessagesThread.Name =
 
317
                "TwitterProtocolManager direct messages listener";
 
318
            f_UpdateDirectMessagesThread.Start();
 
319
        }
 
320
 
 
321
        private void OpenPrivateChat(int userId)
 
322
        {
 
323
            OpenPrivateChat(userId.ToString());
 
324
        }
 
325
 
 
326
        private void OpenPrivateChat(string userId)
 
327
        {
 
328
            ChatModel chat =  Session.GetChat(
 
329
                userId,
 
330
                ChatType.Person,
 
331
                this
 
332
            );
 
333
 
 
334
            if (chat != null) {
 
335
                return;
 
336
            }
 
337
 
 
338
            TwitterUser user = f_Twitter.User.Show(userId);
 
339
            PersonModel person = new PersonModel(
 
340
                user.ID.ToString(),
 
341
                user.ScreenName,
 
342
                NetworkID,
 
343
                Protocol,
 
344
                this
 
345
            );
 
346
            PersonChatModel personChat = new PersonChatModel(
 
347
                person,
 
348
                user.ID.ToString(),
 
349
                user.ScreenName,
 
350
                this
 
351
            );
 
352
            Session.AddChat(personChat);
 
353
            Session.SyncChat(personChat);
 
354
        }
 
355
 
 
356
        public override void CloseChat(FrontendManager fm, ChatModel chat)
 
357
        {
 
358
            Trace.Call(fm, chat);
 
359
 
 
360
            Session.RemoveChat(chat);
 
361
        }
 
362
 
 
363
        public override bool Command(CommandModel command)
 
364
        {
 
365
            bool handled = false;
 
366
            if (command.IsCommand) {
 
367
                if (f_IsConnected) {
 
368
                    switch (command.Command) {
 
369
                        case "msg":
 
370
                        case "query":
 
371
                            CommandMessage(command);
 
372
                            handled = true;
 
373
                            break;
 
374
                    }
 
375
                }
 
376
                switch (command.Command) {
 
377
                    case "help":
 
378
                        CommandHelp(command);
 
379
                        handled = true;
 
380
                        break;
 
381
                    case "connect":
 
382
                        CommandConnect(command);
 
383
                        handled = true;
 
384
                        break;
 
385
                }
 
386
            } else {
 
387
                if (f_IsConnected) {
 
388
                    CommandSay(command);
 
389
                    handled = true;
 
390
                } else {
 
391
                    NotConnected(command);
 
392
                    handled = true;
 
393
                }
 
394
            }
 
395
 
 
396
            return handled;
 
397
        }
 
398
 
 
399
        public override string ToString()
 
400
        {
 
401
            return NetworkID;
 
402
        }
 
403
 
 
404
        public void CommandHelp(CommandModel cd)
 
405
        {
 
406
            MessageModel fmsg = new MessageModel();
 
407
            TextMessagePartModel fmsgti;
 
408
 
 
409
            fmsgti = new TextMessagePartModel();
 
410
            // TRANSLATOR: this line is used as a label / category for a
 
411
            // list of commands below
 
412
            fmsgti.Text = "[" + _("Twitter Commands") + "]";
 
413
            fmsgti.Bold = true;
 
414
            fmsg.MessageParts.Add(fmsgti);
 
415
 
 
416
            Session.AddMessageToChat(cd.Chat, fmsg);
 
417
 
 
418
            string[] help = {
 
419
                "help",
 
420
                "connect twitter username password",
 
421
            };
 
422
 
 
423
            foreach (string line in help) {
 
424
                cd.FrontendManager.AddTextToChat(cd.Chat, "-!- " + line);
 
425
            }
 
426
        }
 
427
 
 
428
        public void CommandConnect(CommandModel cd)
 
429
        {
 
430
            string user;
 
431
            if (cd.DataArray.Length >= 3) {
 
432
                user = cd.DataArray[2];
 
433
            } else {
 
434
                NotEnoughParameters(cd);
 
435
                return;
 
436
            }
 
437
 
 
438
            string pass;
 
439
            if (cd.DataArray.Length >= 4) {
 
440
                pass = cd.DataArray[3];
 
441
            } else {
 
442
                NotEnoughParameters(cd);
 
443
                return;
 
444
            }
 
445
 
 
446
            Connect(cd.FrontendManager, null, 0, user, pass);
 
447
        }
 
448
 
 
449
        public void CommandSay(CommandModel cmd)
 
450
        {
 
451
            FrontendManager fm = cmd.FrontendManager;
 
452
            if (cmd.Chat.ChatType == ChatType.Group) {
 
453
                TwitterChatType twitterChatType = (TwitterChatType)
 
454
                    Enum.Parse(typeof(TwitterChatType), cmd.Chat.ID);
 
455
                switch (twitterChatType) {
 
456
                    case TwitterChatType.FriendsTimeline:
 
457
                    case TwitterChatType.Replies:
 
458
                        PostUpdate(cmd.Data);
 
459
                        break;
 
460
                    case TwitterChatType.DirectMessages:
 
461
                        fm.AddTextToChat(
 
462
                            cmd.Chat,
 
463
                            "-!- " +
 
464
                            _("Cannot send message - no target specified. "+
 
465
                              "Use: /msg $nick message")
 
466
                        );
 
467
                        break;
 
468
                }
 
469
            } else if (cmd.Chat.ChatType == ChatType.Person) {
 
470
                SendMessage(cmd.Chat.ID, cmd.Data);
 
471
            } else {
 
472
                // ignore protocol chat
 
473
            }
 
474
        }
 
475
 
 
476
        public void CommandMessage(CommandModel cmd)
 
477
        {
 
478
            FrontendManager fm = cmd.FrontendManager;
 
479
            string nickname;
 
480
            if (cmd.DataArray.Length >= 2) {
 
481
                nickname = cmd.DataArray[1];
 
482
            } else {
 
483
                NotEnoughParameters(cmd);
 
484
                return;
 
485
            }
 
486
 
 
487
            TwitterUser user = null;
 
488
            try {
 
489
                user = f_Twitter.User.Show(nickname);
 
490
            } catch (NullReferenceException) {
 
491
                // HACK: User.Show() might throw an NRE if the user does not exist
 
492
                // trying to handle this gracefully
 
493
            }
 
494
            if (user == null) {
 
495
                fm.AddTextToChat(cmd.Chat, "-!- " +
 
496
                    _("Could not send message - the specified user does not exist.")
 
497
                );
 
498
                return;
 
499
            }
 
500
 
 
501
            OpenPrivateChat(user.ID);
 
502
 
 
503
            if (cmd.DataArray.Length >= 3) {
 
504
                string message = String.Join(" ", cmd.DataArray, 2, cmd.DataArray.Length-2);
 
505
                SendMessage(user.ID.ToString(), message);
 
506
            }
 
507
         }
 
508
 
 
509
        private List<TwitterStatus> SortTimeline(TwitterStatusCollection timeline)
 
510
        {
 
511
            List<TwitterStatus> sortedTimeline =
 
512
                new List<TwitterStatus>(
 
513
                    timeline.Count
 
514
                );
 
515
            foreach (TwitterStatus status in timeline) {
 
516
                sortedTimeline.Add(status);
 
517
            }
 
518
            sortedTimeline.Sort(
 
519
                (a, b) => (a.Created.CompareTo(b.Created))
 
520
            );
 
521
            return sortedTimeline;
 
522
        }
 
523
 
 
524
        private void UpdateFriendsTimelineThread()
 
525
        {
 
526
            Trace.Call();
 
527
 
 
528
            try {
 
529
                // query the timeline only after we have fetched the user and friends
 
530
                while (f_TwitterUser == null || f_Friends == null) {
 
531
                    Thread.Sleep(1000);
 
532
                }
 
533
 
 
534
                // populate friend list
 
535
                lock (f_Friends) {
 
536
                    foreach (PersonModel friend in f_Friends) {
 
537
                        f_FriendsTimelineChat.UnsafePersons.Add(friend.ID, friend);
 
538
                    }
 
539
                }
 
540
                Session.AddChat(f_FriendsTimelineChat);
 
541
                Session.SyncChat(f_FriendsTimelineChat);
 
542
 
 
543
                while (f_Listening) {
 
544
                    try {
 
545
                        UpdateFriendsTimeline();
 
546
                    } catch (TwitterizerException ex) {
 
547
                        if (ex.Message.Contains("rate limited")) {
 
548
                            // ignore and keep trying
 
549
                        } else {
 
550
                            throw;
 
551
                        }
 
552
                    }
 
553
 
 
554
                    // only poll once per interval or when we get fired
 
555
                    f_FriendsTimelineEvent.WaitOne(
 
556
                        f_UpdateFriendsTimelineInterval * 1000
 
557
                    );
 
558
                }
 
559
            } catch (ThreadAbortException) {
 
560
#if LOG4NET
 
561
                f_Logger.Debug("UpdateFriendsTimelineThread(): thread aborted");
 
562
#endif
 
563
            } catch (Exception ex) {
 
564
#if LOG4NET
 
565
                f_Logger.Error("UpdateFriendsTimelineThread(): Exception", ex);
 
566
#endif
 
567
                string msg =_("An error occurred while fetching the friends timeline from Twitter. Reason: ");
 
568
                Session.AddTextToChat(f_ProtocolChat, "-!- " + msg + ex.Message);
 
569
            }
 
570
#if LOG4NET
 
571
            f_Logger.Debug("UpdateFriendsTimelineThread(): finishing thread.");
 
572
#endif
 
573
        }
 
574
 
 
575
        private void UpdateFriendsTimeline()
 
576
        {
 
577
            Trace.Call();
 
578
 
 
579
#if LOG4NET
 
580
            f_Logger.Debug("UpdateFriendsTimeline(): getting friend timeline from twitter...");
 
581
#endif
 
582
            TwitterParameters parameters = new TwitterParameters();
 
583
            parameters.Add(TwitterParameterNames.Count, 50);
 
584
            if (f_LastFriendsTimelineStatusID != null) {
 
585
                parameters.Add(TwitterParameterNames.SinceID,
 
586
                               f_LastFriendsTimelineStatusID);
 
587
            }
 
588
            TwitterStatusCollection timeline =
 
589
                f_Twitter.Status.FriendsTimeline(parameters);
 
590
#if LOG4NET
 
591
            f_Logger.Debug("UpdateFriendsTimeline(): done. New tweets: " +
 
592
                (timeline == null ? 0 : timeline.Count));
 
593
#endif
 
594
            if (timeline == null || timeline.Count == 0) {
 
595
                return;
 
596
            }
 
597
 
 
598
            List<TwitterStatus> sortedTimeline = SortTimeline(timeline);
 
599
            foreach (TwitterStatus status in sortedTimeline) {
 
600
                MessageModel msg = CreateMessage(
 
601
                    status.Created,
 
602
                    status.TwitterUser.ScreenName,
 
603
                    status.Text
 
604
                );
 
605
                Session.AddMessageToChat(f_FriendsTimelineChat, msg);
 
606
 
 
607
                f_LastFriendsTimelineStatusID = status.ID;
 
608
            }
 
609
        }
 
610
 
 
611
        private void UpdateRepliesThread()
 
612
        {
 
613
            Trace.Call();
 
614
 
 
615
            try {
 
616
                // query the replies only after we have fetched the user and friends
 
617
                while (f_TwitterUser == null || f_Friends == null) {
 
618
                    Thread.Sleep(1000);
 
619
                }
 
620
 
 
621
                // populate friend list
 
622
                lock (f_Friends) {
 
623
                    foreach (PersonModel friend in f_Friends) {
 
624
                        f_RepliesChat.UnsafePersons.Add(friend.ID, friend);
 
625
                    }
 
626
                }
 
627
                Session.AddChat(f_RepliesChat);
 
628
                Session.SyncChat(f_RepliesChat);
 
629
 
 
630
                while (f_Listening) {
 
631
                    try {
 
632
                        UpdateReplies();
 
633
                    } catch (TwitterizerException ex) {
 
634
                        if (ex.Message.Contains("rate limited")) {
 
635
                            // ignore and keep trying
 
636
                        } else {
 
637
                            throw;
 
638
                        }
 
639
                    }
 
640
 
 
641
                    // only poll once per interval
 
642
                    Thread.Sleep(f_UpdateRepliesInterval * 1000);
 
643
                }
 
644
            } catch (ThreadAbortException) {
 
645
#if LOG4NET
 
646
                f_Logger.Debug("UpdateRepliesThread(): thread aborted");
 
647
#endif
 
648
            } catch (Exception ex) {
 
649
#if LOG4NET
 
650
                f_Logger.Error("UpdateRepliesThread(): Exception", ex);
 
651
#endif
 
652
                string msg =_("An error occurred while fetching the replies from Twitter. Reason: ");
 
653
                Session.AddTextToChat(f_ProtocolChat, "-!- " + msg + ex.Message);
 
654
            }
 
655
#if LOG4NET
 
656
            f_Logger.Debug("UpdateRepliesThread(): finishing thread.");
 
657
#endif
 
658
        }
 
659
 
 
660
        private void UpdateReplies()
 
661
        {
 
662
            Trace.Call();
 
663
 
 
664
#if LOG4NET
 
665
            f_Logger.Debug("UpdateReplies(): getting replies from twitter...");
 
666
#endif
 
667
            TwitterParameters parameters = new TwitterParameters();
 
668
            parameters.Add(TwitterParameterNames.Count, 50);
 
669
            if (f_LastReplyStatusID != null) {
 
670
                parameters.Add(TwitterParameterNames.SinceID,
 
671
                               f_LastReplyStatusID);
 
672
            }
 
673
            TwitterStatusCollection timeline =
 
674
                f_Twitter.Status.Replies(parameters);
 
675
#if LOG4NET
 
676
            f_Logger.Debug("UpdateReplies(): done. New replies: " +
 
677
                (timeline == null ? 0 : timeline.Count));
 
678
#endif
 
679
            if (timeline == null || timeline.Count == 0) {
 
680
                return;
 
681
            }
 
682
 
 
683
            // if this isn't the first time we receive replies, this is new!
 
684
            bool highlight = f_LastReplyStatusID != null;
 
685
            List<TwitterStatus> sortedTimeline = SortTimeline(timeline);
 
686
            foreach (TwitterStatus status in sortedTimeline) {
 
687
                MessageModel msg = CreateMessage(
 
688
                    status.Created,
 
689
                    status.TwitterUser.ScreenName,
 
690
                    status.Text,
 
691
                    highlight
 
692
                );
 
693
                Session.AddMessageToChat(f_RepliesChat, msg);
 
694
 
 
695
                f_LastReplyStatusID = status.ID;
 
696
            }
 
697
        }
 
698
 
 
699
        private void UpdateDirectMessagesThread()
 
700
        {
 
701
            Trace.Call();
 
702
 
 
703
            try {
 
704
                // query the messages only after we have fetched the user and friends
 
705
                while (f_TwitterUser == null || f_Friends == null) {
 
706
                    Thread.Sleep(1000);
 
707
                }
 
708
 
 
709
                // populate friend list
 
710
                lock (f_Friends) {
 
711
                    foreach (PersonModel friend in f_Friends) {
 
712
                        f_DirectMessagesChat.UnsafePersons.Add(friend.ID, friend);
 
713
                    }
 
714
                }
 
715
                Session.AddChat(f_DirectMessagesChat);
 
716
                Session.SyncChat(f_DirectMessagesChat);
 
717
 
 
718
                while (f_Listening) {
 
719
                    try {
 
720
                        UpdateDirectMessages();
 
721
                    } catch (TwitterizerException ex) {
 
722
                        if (ex.Message.Contains("rate limited")) {
 
723
                            // ignore and keep trying
 
724
                        } else {
 
725
                            throw;
 
726
                        }
 
727
                    }
 
728
 
 
729
                    // only poll once per interval or when we get fired
 
730
                    f_DirectMessageEvent.WaitOne(
 
731
                        f_UpdateDirectMessagesInterval * 1000
 
732
                    );
 
733
                }
 
734
            } catch (ThreadAbortException) {
 
735
#if LOG4NET
 
736
                f_Logger.Debug("UpdateDirectMessagesThread(): thread aborted");
 
737
#endif
 
738
            } catch (Exception ex) {
 
739
#if LOG4NET
 
740
                f_Logger.Error("UpdateDirectMessagesThread(): Exception", ex);
 
741
#endif
 
742
                string msg =_("An error occurred while fetching direct messages from Twitter. Reason: ");
 
743
                Session.AddTextToChat(f_ProtocolChat, "-!- " + msg + ex.Message);
 
744
            }
 
745
#if LOG4NET
 
746
            f_Logger.Debug("UpdateDirectMessagesThread(): finishing thread.");
 
747
#endif
 
748
        }
 
749
 
 
750
        private void UpdateDirectMessages()
 
751
        {
 
752
            Trace.Call();
 
753
 
 
754
            TwitterParameters parameters;
 
755
#if LOG4NET
 
756
            f_Logger.Debug("UpdateDirectMessages(): getting received direct messages from twitter...");
 
757
#endif
 
758
            parameters = new TwitterParameters();
 
759
            parameters.Add(TwitterParameterNames.Count, 50);
 
760
            if (f_LastDirectMessageReceivedStatusID != null) {
 
761
                parameters.Add(TwitterParameterNames.SinceID,
 
762
                               f_LastDirectMessageReceivedStatusID);
 
763
            }
 
764
            TwitterStatusCollection receivedTimeline =
 
765
                f_Twitter.DirectMessages.DirectMessages(parameters);
 
766
#if LOG4NET
 
767
            f_Logger.Debug("UpdateDirectMessages(): done. New messages: " +
 
768
                (receivedTimeline == null ? 0 : receivedTimeline.Count));
 
769
#endif
 
770
 
 
771
#if LOG4NET
 
772
            f_Logger.Debug("UpdateDirectMessages(): getting sent direct messages from twitter...");
 
773
#endif
 
774
            parameters = new TwitterParameters();
 
775
            parameters.Add(TwitterParameterNames.Count, 50);
 
776
            if (f_LastDirectMessageSentStatusID != null) {
 
777
                parameters.Add(TwitterParameterNames.SinceID,
 
778
                               f_LastDirectMessageSentStatusID);
 
779
            }
 
780
            TwitterStatusCollection sentTimeline =
 
781
                f_Twitter.DirectMessages.DirectMessagesSent(parameters);
 
782
#if LOG4NET
 
783
            f_Logger.Debug("UpdateDirectMessages(): done. New messages: " +
 
784
                (sentTimeline == null ? 0 : sentTimeline.Count));
 
785
#endif
 
786
 
 
787
            TwitterStatusCollection timeline = new TwitterStatusCollection();
 
788
            if (receivedTimeline != null) {
 
789
                foreach (TwitterStatus status in receivedTimeline) {
 
790
                    timeline.Add(status);
 
791
                }
 
792
            }
 
793
            if (sentTimeline != null) {
 
794
                foreach (TwitterStatus status in sentTimeline) {
 
795
                    timeline.Add(status);
 
796
                }
 
797
            }
 
798
 
 
799
            if (timeline.Count == 0) {
 
800
                // nothing to do
 
801
                return;
 
802
            }
 
803
 
 
804
            List<TwitterStatus> sortedTimeline = SortTimeline(timeline);
 
805
            foreach (TwitterStatus status in sortedTimeline) {
 
806
                // if this isn't the first time a receive a direct message,
 
807
                // this is a new one!
 
808
                bool highlight = receivedTimeline.Contains(status) &&
 
809
                                 f_LastDirectMessageReceivedStatusID != null;
 
810
                MessageModel msg = CreateMessage(
 
811
                    status.Created,
 
812
                    status.TwitterUser.ScreenName,
 
813
                    status.Text,
 
814
                    highlight
 
815
                );
 
816
                Session.AddMessageToChat(f_DirectMessagesChat, msg);
 
817
 
 
818
                // if there is a tab open for this user put the message there too
 
819
                string userId;
 
820
                if (receivedTimeline.Contains(status)) {
 
821
                    // this is a received message
 
822
                    userId =  status.TwitterUser.ID.ToString();
 
823
                } else {
 
824
                    // this is a sent message
 
825
                    userId = status.Recipient.ID.ToString();
 
826
                }
 
827
                ChatModel chat =  Session.GetChat(
 
828
                    userId,
 
829
                    ChatType.Person,
 
830
                    this
 
831
                );
 
832
                if (chat != null) {
 
833
                    Session.AddMessageToChat(chat, msg);
 
834
                }
 
835
            }
 
836
 
 
837
            if (receivedTimeline != null) {
 
838
                // first one is the newest
 
839
                foreach (TwitterStatus status in receivedTimeline) {
 
840
                    f_LastDirectMessageReceivedStatusID = status.ID;
 
841
                    break;
 
842
                }
 
843
            }
 
844
            if (sentTimeline != null) {
 
845
                // first one is the newest
 
846
                foreach (TwitterStatus status in sentTimeline) {
 
847
                    f_LastDirectMessageSentStatusID = status.ID;
 
848
                    break;
 
849
                }
 
850
            }
 
851
        }
 
852
 
 
853
        private void UpdateFriends()
 
854
        {
 
855
            Trace.Call();
 
856
 
 
857
            if (f_Friends != null) {
 
858
                return;
 
859
            }
 
860
 
 
861
#if LOG4NET
 
862
            f_Logger.Debug("UpdateFriends(): getting friends from twitter...");
 
863
#endif
 
864
            TwitterUserCollection friends = f_Twitter.User.Friends();
 
865
#if LOG4NET
 
866
            f_Logger.Debug("UpdateFriends(): done. Friends: " +
 
867
                (friends == null ? 0 : friends.Count));
 
868
#endif
 
869
            if (friends == null || friends.Count == 0) {
 
870
                return;
 
871
            }
 
872
 
 
873
            List<PersonModel> persons = new List<PersonModel>(friends.Count);
 
874
            foreach (TwitterUser friend in friends) {
 
875
                PersonModel person = new PersonModel(
 
876
                    friend.ID.ToString(),
 
877
                    friend.ScreenName,
 
878
                    NetworkID,
 
879
                    Protocol,
 
880
                    this
 
881
                );
 
882
                persons.Add(person);
 
883
            }
 
884
            f_Friends = persons;
 
885
        }
 
886
 
 
887
        private void UpdateUser()
 
888
        {
 
889
#if LOG4NET
 
890
            f_Logger.Debug("UpdateUser(): getting user details from twitter...");
 
891
#endif
 
892
            f_TwitterUser = f_Twitter.User.Show(f_Username);
 
893
#if LOG4NET
 
894
            f_Logger.Debug("UpdateUser(): done.");
 
895
#endif
 
896
        }
 
897
 
 
898
        protected override TextColor GetIdentityNameColor(string identityName)
 
899
        {
 
900
            if (identityName == f_TwitterUser.ScreenName) {
 
901
                return f_BlueTextColor;
 
902
            }
 
903
 
 
904
            return base.GetIdentityNameColor(identityName);
 
905
        }
 
906
 
 
907
        private MessageModel CreateMessage(DateTime when, string from,
 
908
                                           string message)
 
909
        {
 
910
            return CreateMessage(when, from, message, false);
 
911
        }
 
912
 
 
913
        private MessageModel CreateMessage(DateTime when, string from,
 
914
                                           string message, bool highlight)
 
915
        {
 
916
            MessageModel msg = new MessageModel();
 
917
            TextMessagePartModel msgPart;
 
918
            msg.TimeStamp = when;
 
919
 
 
920
            msgPart = new TextMessagePartModel();
 
921
            msgPart.Text = "<";
 
922
            msg.MessageParts.Add(msgPart);
 
923
 
 
924
            msgPart = new TextMessagePartModel();
 
925
            msgPart.Text = from;
 
926
            msgPart.ForegroundColor = GetIdentityNameColor(from);
 
927
            msg.MessageParts.Add(msgPart);
 
928
 
 
929
            msgPart = new TextMessagePartModel();
 
930
            msgPart.Text = "> ";
 
931
            msg.MessageParts.Add(msgPart);
 
932
 
 
933
            msgPart = new TextMessagePartModel(message);
 
934
            msgPart.IsHighlight = highlight;
 
935
            msg.MessageParts.Add(msgPart);
 
936
 
 
937
            ParseUrls(msg);
 
938
 
 
939
            return msg;
 
940
        }
 
941
 
 
942
        private void PostUpdate(string text)
 
943
        {
 
944
            f_Twitter.Status.Update(text);
 
945
            f_FriendsTimelineEvent.Set();
 
946
        }
 
947
 
 
948
        private void SendMessage(string target, string text)
 
949
        {
 
950
            f_Twitter.DirectMessages.New(target, text);
 
951
            f_DirectMessageEvent.Set();
 
952
        }
 
953
 
 
954
        private static string _(string msg)
 
955
        {
 
956
            return LibraryCatalog.GetString(msg, f_LibraryTextDomain);
 
957
        }
 
958
    }
 
959
}