コード例 #1
0
        /// <summary>
        /// Parse streamed twitter event
        /// </summary>
        /// <param name="ev">event name</param>
        /// <param name="graph">JSON object graph</param>
        /// <param name="handler">result handler</param>
        private static void ParseStreamEvent(string ev, JsonValue graph, IStreamHandler handler)
        {
            try
            {
                var source    = new TwitterUser(graph[EventSourceKey]);
                var target    = new TwitterUser(graph[EventTargetKey]);
                var timestamp = graph[EventCreatedAtKey].AsString().ParseTwitterDateTime();
                switch (ev)
                {
                case StreamStatusEvent.FavoriteEventKey:
                case StreamStatusEvent.UnfavoriteEventKey:
                case StreamStatusEvent.QuotedTweetEventKey:
                case StreamStatusEvent.FavoritedRetweetEventKey:
                case StreamStatusEvent.RetweetedRetweetEventKey:
                    handler.OnMessage(new StreamStatusEvent(source, target,
                                                            new TwitterStatus(graph[EventTargetObjectKey]), ev, timestamp));
                    break;

                case StreamUserEvent.BlockEventKey:
                case StreamUserEvent.UnblockEventKey:
                case StreamUserEvent.FollowEventKey:
                case StreamUserEvent.UnfollowEventKey:
                case StreamUserEvent.UserMuteEventKey:
                case StreamUserEvent.UserUnmuteEventKey:
                case StreamUserEvent.UserUpdateEventKey:
                case StreamUserEvent.UserDeleteEventKey:
                case StreamUserEvent.UserSuspendEventKey:
                    handler.OnMessage(new StreamUserEvent(source, target,
                                                          ev, timestamp));
                    break;

                case StreamListEvent.ListCreatedEventKey:
                case StreamListEvent.ListDestroyedEventKey:
                case StreamListEvent.ListUpdatedEventKey:
                case StreamListEvent.ListMemberAddedEventKey:
                case StreamListEvent.ListMemberRemovedEventKey:
                case StreamListEvent.ListUserSubscribedEventKey:
                case StreamListEvent.ListUserUnsubscribedEventKey:
                    handler.OnMessage(new StreamListEvent(source, target,
                                                          new TwitterList(graph[EventTargetObjectKey]), ev, timestamp));
                    break;

                case StreamAccessInformationEvent.AccessRevokedEventKey:
                case StreamAccessInformationEvent.AccessUnrevokedEventKey:
                    handler.OnMessage(new StreamAccessInformationEvent(source, target,
                                                                       new AccessInformation(graph[EventTargetObjectKey]), ev, timestamp));
                    break;

                default:
                    Debug.WriteLine("unknown event: " + ev);
                    break;
                }
            }
            catch (Exception ex)
            {
                handler.OnException(new StreamParseException(
                                        "Event parse failed:" + ev, graph.ToString(), ex));
            }
        }
コード例 #2
0
        /// <summary>
        /// Parse streamed twitter event
        /// </summary>
        /// <param name="ev">event name</param>
        /// <param name="graph">JSON object graph</param>
        /// <param name="handler">result handler</param>
        private static void ParseStreamEvent(string ev, dynamic graph, IStreamHandler handler)
        {
            try
            {
                var source    = new TwitterUser(graph.source);
                var target    = new TwitterUser(graph.target);
                var timestamp = ((string)graph.created_at).ParseTwitterDateTime();
                switch (ev)
                {
                case "favorite":
                case "unfavorite":
                case "quoted_tweet":
                case "favorited_retweet":
                case "retweeted_retweet":
                    handler.OnMessage(new StreamStatusEvent(source, target,
                                                            new TwitterStatus(graph.target_object), ev, timestamp));
                    break;

                case "block":
                case "unblock":
                case "follow":
                case "unfollow":
                case "user_update":
                case "mute":
                case "unmute":
                    handler.OnMessage(new StreamUserEvent(source, target,
                                                          ev, timestamp));
                    break;

                case "list_created":
                case "list_destroyed":
                case "list_updated":
                case "list_member_added":
                case "list_member_removed":
                case "list_user_subscribed":
                case "list_user_unsubscribed":
                    handler.OnMessage(new StreamListEvent(source, target,
                                                          new TwitterList(graph.target_object), ev, timestamp));
                    break;

                default:
                    break;
                }
            }
            catch (Exception ex)
            {
                handler.OnException(new StreamParseException(
                                        "Event parse failed:" + ev, graph.ToString(), ex));
            }
        }
コード例 #3
0
        /// <summary>
        /// Parse streamed JSON line (which is not a status)
        /// </summary>
        /// <param name="graph">JSON object graph</param>
        /// <param name="handler">result handler</param>
        internal static void ParseNotStatusStreamLine(dynamic graph, IStreamHandler handler)
        {
            try
            {
                // element.foo() -> element.IsDefined("foo")

                // direct message
                if (graph.direct_message())
                {
                    handler.OnStatus(new TwitterStatus(graph.direct_message));
                    return;
                }

                // delete
                if (graph.delete())
                {
                    if (graph.delete.status())
                    {
                        handler.OnMessage(new StreamDelete(
                                              Int64.Parse(graph.delete.status.id_str),
                                              Int64.Parse(graph.delete.status.user_id_str),
                                              graph.delete.timestamp_ms));
                        return;
                    }
                    if (graph.delete.direct_message())
                    {
                        handler.OnMessage(new StreamDelete(
                                              Int64.Parse(graph.delete.status.id_str),
                                              Int64.Parse(graph.delete.direct_message.user_id.ToString()),
                                              graph.delete.timestamp_ms));
                        return;
                    }
                }

                // scrub_geo
                if (graph.scrub_geo())
                {
                    handler.OnMessage(new StreamScrubGeo(
                                          Int64.Parse(graph.scrub_geo.user_id_str),
                                          Int64.Parse(graph.scrub_geo.up_to_status_id_str),
                                          graph.scrub_geo.timestamp_ms));
                    return;
                }

                // limit
                if (graph.limit())
                {
                    handler.OnMessage(new StreamLimit(
                                          (long)graph.limit.track,
                                          graph.limit.timestamp_ms));
                    return;
                }

                // withheld
                if (graph.status_withheld())
                {
                    handler.OnMessage(new StreamWithheld(
                                          Int64.Parse(graph.status_withheld.user_id),
                                          Int64.Parse(graph.status_withheld.id),
                                          graph.status_withheld.withheld_in_countries,
                                          graph.status_withheld.timestamp_ms));
                    return;
                }
                if (graph.user_withheld())
                {
                    handler.OnMessage(new StreamWithheld(
                                          Int64.Parse(graph.user_withheld.id),
                                          graph.user_withheld.withheld_in_countries,
                                          graph.user_withheld.timestamp_ms));
                    return;
                }

                // disconnect
                if (graph.disconnect())
                {
                    handler.OnMessage(new StreamDisconnect(
                                          (DisconnectCode)graph.disconnect.code,
                                          graph.disconnect.stream_name, graph.disconnect.reason,
                                          graph.disconnect.timestamp_ms));
                    return;
                }

                // stall warning
                if (graph.warning())
                {
                    if (graph.warning.code == "FALLING_BEHIND")
                    {
                        handler.OnMessage(new StreamStallWarning(
                                              graph.warning.code,
                                              graph.warning.message,
                                              graph.warning.percent_full,
                                              graph.warning.timestamp_ms));
                        return;
                    }
                }

                // user update
                if (graph.IsDefined("event")) // 'event' is the reserved word...
                {
                    var ev = ((string)graph["event"]).ToLower();
                    if (ev == "user_update")
                    {
                        // parse user_update only in generic streams.
                        handler.OnMessage(new StreamUserEvent(
                                              new TwitterUser(graph.source),
                                              new TwitterUser(graph.target), ev,
                                              ((string)graph.created_at).ParseTwitterDateTime()));
                        return;
                    }
                    // unknown event...
                    handler.OnMessage(new StreamUnknownMessage("event: " + ev, graph.ToString()));
                }

                if (graph.IsObject())
                {
                    // unknown...
                    foreach (KeyValuePair <string, dynamic> item in graph)
                    {
                        handler.OnMessage(new StreamUnknownMessage(item.Key, item.Value.ToString()));
                        return;
                    }
                }
                // unknown event-type...
                handler.OnMessage(new StreamUnknownMessage(null, graph.Value.ToString()));
            }
            catch (Exception ex)
            {
                handler.OnException(new StreamParseException(
                                        "Stream graph parse failed.", graph.ToString(), ex));
            }
        }
コード例 #4
0
        /// <summary>
        /// Parse streamed JSON line (which is not a status)
        /// </summary>
        /// <param name="graph">JSON object graph</param>
        /// <param name="handler">result handler</param>
        internal static void ParseNotStatusStreamLine(JsonValue graph, IStreamHandler handler)
        {
            try
            {
                // element.foo() -> element.IsDefined("foo")

                // direct message
                if (graph.TryGetValue("direct_message", out var directMessage))
                {
                    handler.OnStatus(new TwitterStatus(directMessage));
                    return;
                }

                // delete
                if (graph.TryGetValue("delete", out var delete))
                {
                    var timestamp = GetTimestamp(delete);
                    if (delete.TryGetValue("status", out var delstatus))
                    {
                        handler.OnMessage(new StreamDelete(
                                              Int64.Parse(delstatus["id_str"].AsString()),
                                              Int64.Parse(delstatus["user_id_str"].AsString()),
                                              timestamp));
                        return;
                    }
                    if (delete.TryGetValue("direct_message", out var delmsg))
                    {
                        handler.OnMessage(new StreamDelete(
                                              Int64.Parse(delmsg["id_str"].AsString()),
                                              Int64.Parse(delmsg["user_id"].AsString()),
                                              timestamp));
                        return;
                    }
                }

                // scrub_geo
                if (graph.TryGetValue("scrub_geo", out var scrubGeo))
                {
                    handler.OnMessage(new StreamScrubGeo(
                                          Int64.Parse(scrubGeo["user_id_str"].AsString()),
                                          Int64.Parse(scrubGeo["up_to_status_id_str"].AsString()),
                                          GetTimestamp(scrubGeo)));
                    return;
                }

                // limit
                if (graph.TryGetValue("limit", out var limit))
                {
                    handler.OnMessage(new StreamLimit(
                                          limit["track"].AsLong(),
                                          GetTimestamp(limit)));
                    return;
                }

                // withheld
                if (graph.TryGetValue("status_withheld", out var statusWithheld))
                {
                    handler.OnMessage(new StreamWithheld(
                                          statusWithheld["user_id"].AsLong(),
                                          statusWithheld["id"].AsLong(),
                                          ((JsonArray)statusWithheld["withheld_in_countries"]).Select(s => s.AsString()).ToArray(),
                                          GetTimestamp(statusWithheld)));
                    return;
                }
                if (graph.TryGetValue("user_withheld", out var userWithheld))
                {
                    handler.OnMessage(new StreamWithheld(
                                          userWithheld["id"].AsLong(),
                                          ((JsonArray)statusWithheld["withheld_in_countries"]).Select(s => s.AsString()).ToArray(),
                                          GetTimestamp(statusWithheld)));
                    return;
                }

                // disconnect
                if (graph.TryGetValue("disconnect", out var disconnect))
                {
                    handler.OnMessage(new StreamDisconnect(
                                          (DisconnectCode)disconnect["code"].AsLong(),
                                          disconnect["stream_name"].AsString(),
                                          disconnect["reason"].AsString(),
                                          GetTimestamp(disconnect)));
                    return;
                }

                // stall warning
                if (graph.TryGetValue("warning", out var warning))
                {
                    var timestamp = GetTimestamp(warning);
                    var code      = warning["code"].AsString();
                    if (code == "FALLING_BEHIND")
                    {
                        handler.OnMessage(new StreamStallWarning(
                                              code,
                                              warning["message"].AsString(),
                                              (int)warning["percent_full"].AsLong(),
                                              timestamp));
                        return;
                    }
                }

                // user update
                if (graph.TryGetValue("event", out var @event))
                {
                    var ev = @event.AsString().ToLower();
                    if (ev == StreamUserEvent.UserUpdateEventKey)
                    {
                        // parse user_update only in generic streams.
                        handler.OnMessage(new StreamUserEvent(
                                              new TwitterUser(graph["source"]),
                                              new TwitterUser(graph["target"]),
                                              ev, graph["created_at"].AsString().ParseTwitterDateTime()));
                        return;
                    }
                    // unknown event...
                    handler.OnMessage(new StreamUnknownMessage("event: " + ev, graph.ToString()));
                }

                // unknown event-type...
                handler.OnMessage(new StreamUnknownMessage(null, graph.ToString()));
            }
            catch (Exception ex)
            {
                handler.OnException(new StreamParseException(
                                        "Stream graph parse failed.", graph.ToString(), ex));
            }
        }
コード例 #5
0
        /// <summary>
        /// Parse streamed JSON line
        /// </summary>
        /// <param name="graph">JSON object graph</param>
        /// <param name="handler">result handler</param>
        public static void ParseStreamLine(dynamic graph, IStreamHandler handler)
        {
            try
            {
                // element.foo() -> element.IsDefined("foo")

                //
                // fast path: first, identify standard status payload
                ////////////////////////////////////////////////////////////////////////////////////
                if (TwitterStreamParser.ParseStreamLineAsStatus(graph, handler))
                {
                    return;
                }

                //
                // parse stream-specific elements
                //

                // friends lists
                if (graph.friends())
                {
                    // friends enumeration
                    handler.OnMessage(new StreamEnumeration((long[])graph.friends));
                    return;
                }
                if (graph.friends_str())
                {
                    // friends enumeration(stringified)
                    handler.OnMessage(new StreamEnumeration(
                                          ((string[])graph.friends).Select(s => s.ParseLong()).ToArray()));
                    return;
                }

                if (graph.IsDefined("event")) // graph.event()
                {
                    ParseStreamEvent(((string)graph["event"]).ToLower(), graph, handler);
                    return;
                }


                // too many follows warning
                if (graph.warning())
                {
                    if (graph.warning.code == "FOLLOWS_OVER_LIMIT")
                    {
                        handler.OnMessage(new StreamTooManyFollowsWarning(
                                              graph.warning.code,
                                              graph.warning.message,
                                              graph.warning.user_id,
                                              graph.warning.timestamp_ms));
                        return;
                    }
                }

                // fallback to default stream handler
                TwitterStreamParser.ParseNotStatusStreamLine(graph, handler);
            }
            catch (Exception ex)
            {
                handler.OnException(new StreamParseException(
                                        "Stream graph parse failed.", graph.ToString(), ex));
            }
        }
コード例 #6
0
        private async Task <bool> HandleException(Exception ex)
        {
            Log("Exception on User Stream Receiver: " + Environment.NewLine + ex);
            var tx = ex as TwitterApiException;

            if (tx != null)
            {
                // protocol error
                Log($"Twitter API Exception: [status-code: {tx.StatusCode} twitter-code: {tx.TwitterErrorCode}]");
                _stateUpdater.UpdateState(_account.UnreliableScreenName +
                                          ReceivingResources.UserStreamDisconnectedFormat.SafeFormat(
                                              (int)tx.StatusCode, tx.TwitterErrorCode));
                _handler.OnMessage(new StreamErrorMessage(_account, tx.StatusCode, tx.TwitterErrorCode));
                switch (tx.StatusCode)
                {
                case HttpStatusCode.Unauthorized:
                    Log("Authorization failed.");
                    if (_hardErrorCount > MaxHardErrorCount)
                    {
                        return(false);
                    }
                    break;

                case HttpStatusCode.Forbidden:
                case HttpStatusCode.NotFound:
                    Log("Endpoint not found / not accessible.");
                    if (_hardErrorCount > MaxHardErrorCount)
                    {
                        return(false);
                    }
                    break;

                case HttpStatusCode.NotAcceptable:
                case HttpStatusCode.RequestEntityTooLarge:
                    Log("Specified argument could not be accepted.");
                    return(false);

                case HttpStatusCode.RequestedRangeNotSatisfiable:
                    Log("Permission denied / Parameter out of range");
                    return(false);

                case (HttpStatusCode)420:     // Too many connections
                    Log("Too many connections are established.");
                    return(false);
                }
                // general protocol error
                if (_backoffMode == BackoffMode.ProtocolError)
                {
                    // exponential backoff
                    _backoffWait *= 2;
                }
                else
                {
                    _backoffWait = ProtocolErrorInitialWait;
                    _backoffMode = BackoffMode.ProtocolError;
                }
                if (_backoffWait >= ProtocolErrorMaxWait)
                {
                    Log("Protocol backoff limit exceeded.");
                    _stateUpdater.UpdateState(_account.UnreliableScreenName + ": " +
                                              ReceivingResources.ConnectFailedByProtocol);
                    return(false);
                }
            }
            else
            {
                // network error
                if (_backoffMode == BackoffMode.NetworkError)
                {
                    // linear backoff
                    _backoffWait += NetworkErrorInitialWait;
                }
                else
                {
                    _backoffWait = NetworkErrorInitialWait;
                    _backoffMode = BackoffMode.NetworkError;
                }
                if (_backoffWait >= NetworkErrorMaxWait)
                {
                    Log("Network backoff limit exceeded.");
                    _stateUpdater.UpdateState(_account.UnreliableScreenName + ": " +
                                              ReceivingResources.ConnectFailedByNetwork);
                    return(false);
                }
            }
            Log($"Waiting reconnection... [{_backoffWait} ms]");
            _stateUpdater.UpdateState(_account.UnreliableScreenName + ": " +
                                      ReceivingResources.ReconnectingFormat.SafeFormat(_backoffWait));
            _handler.OnMessage(new StreamWaitMessage(_account, _backoffWait));
            await Task.Delay(TimeSpan.FromMilliseconds(_backoffWait)).ConfigureAwait(false);

            return(true);
        }
コード例 #7
0
        /// <summary>
        /// Parse streamed JSON line
        /// </summary>
        /// <param name="graph">JSON object graph</param>
        /// <param name="handler">result handler</param>
        public static void ParseStreamLine(JsonValue graph, IStreamHandler handler)
        {
            try
            {
                // element.foo() -> element.IsDefined("foo")

                //
                // fast path: first, identify standard status payload
                ////////////////////////////////////////////////////////////////////////////////////
                if (TwitterStreamParser.ParseStreamLineAsStatus(graph, handler))
                {
                    return;
                }

                //
                // parse stream-specific elements
                //

                // friends lists
                var friends = graph["friends"].AsArrayOrNull();
                if (friends != null)
                {
                    // friends enumeration
                    var friendsIds = friends.Select(v => v.AsLong()).ToArray();
                    handler.OnMessage(new StreamEnumeration(friendsIds));
                    return;
                }
                friends = graph["friends_str"].AsArrayOrNull();
                if (friends != null)
                {
                    // friends enumeration(stringified)
                    var friendsIds = friends.Select(v => v.AsString().ParseLong()).ToArray();
                    handler.OnMessage(new StreamEnumeration(friendsIds));
                    return;
                }

                var @event = graph["event"].AsStringOrNull();
                if (@event != null)
                {
                    ParseStreamEvent(@event.ToLower(), graph, handler);
                    return;
                }

                // too many follows warning
                var warning = graph["warning"].AsObjectOrNull();
                if (warning != null)
                {
                    var code = warning["code"].AsString();
                    if (code == "FOLLOWS_OVER_LIMIT")
                    {
                        handler.OnMessage(new StreamTooManyFollowsWarning(
                                              code,
                                              warning["message"].AsString(),
                                              warning["user_id"].AsLong(),
                                              TwitterStreamParser.GetTimestamp(warning)));
                        return;
                    }
                }

                // fallback to default stream handler
                TwitterStreamParser.ParseNotStatusStreamLine(graph, handler);
            }
            catch (Exception ex)
            {
                handler.OnException(new StreamParseException(
                                        "Stream graph parse failed.", graph.ToString(), ex));
            }
        }
コード例 #8
0
ファイル: UserStreamParser.cs プロジェクト: karno/Cadena
        /// <summary>
        /// Parse streamed JSON line
        /// </summary>
        /// <param name="graph">JSON object graph</param>
        /// <param name="handler">result handler</param>
        public static void ParseStreamLine(JsonValue graph, IStreamHandler handler)
        {


            try
            {
                // element.foo() -> element.IsDefined("foo")

                //
                // fast path: first, identify standard status payload
                ////////////////////////////////////////////////////////////////////////////////////
                if (TwitterStreamParser.ParseStreamLineAsStatus(graph, handler))
                {
                    return;
                }

                //
                // parse stream-specific elements
                //

                // friends lists
                var friends = graph["friends"].AsArrayOrNull();
                if (friends != null)
                {
                    // friends enumeration
                    var friendsIds = friends.Select(v => v.AsLong()).ToArray();
                    handler.OnMessage(new StreamEnumeration(friendsIds));
                    return;
                }
                friends = graph["friends_str"].AsArrayOrNull();
                if (friends != null)
                {
                    // friends enumeration(stringified)
                    var friendsIds = friends.Select(v => v.AsString().ParseLong()).ToArray();
                    handler.OnMessage(new StreamEnumeration(friendsIds));
                    return;
                }

                var @event = graph["event"].AsString();
                if (@event != null)
                {
                    ParseStreamEvent(@event.ToLower(), graph, handler);
                    return;
                }

                // too many follows warning
                var warning = graph["warning"].AsObjectOrNull();
                if (warning != null)
                {
                    var code = warning["code"].AsString();
                    if (code == "FOLLOWS_OVER_LIMIT")
                    {
                        handler.OnMessage(new StreamTooManyFollowsWarning(
                            code,
                            warning["message"].AsString(),
                            warning["user_id"].AsLong(),
                            TwitterStreamParser.GetTimestamp(warning)));
                        return;
                    }
                }

                // fallback to default stream handler
                TwitterStreamParser.ParseNotStatusStreamLine(graph, handler);
            }
            catch (Exception ex)
            {
                handler.OnException(new StreamParseException(
                    "Stream graph parse failed.", graph.ToString(), ex));
            }
        }
コード例 #9
0
ファイル: UserStreamParser.cs プロジェクト: karno/Cadena
 /// <summary>
 /// Parse streamed twitter event
 /// </summary>
 /// <param name="ev">event name</param>
 /// <param name="graph">JSON object graph</param>
 /// <param name="handler">result handler</param>
 private static void ParseStreamEvent(string ev, JsonValue graph, IStreamHandler handler)
 {
     try
     {
         var source = new TwitterUser(graph[EventSourceKey]);
         var target = new TwitterUser(graph[EventTargetKey]);
         var timestamp = graph[EventCreatedAtKey].AsString().ParseTwitterDateTime();
         switch (ev)
         {
             case "favorite":
             case "unfavorite":
             case "quoted_tweet":
             case "favorited_retweet":
             case "retweeted_retweet":
                 handler.OnMessage(new StreamStatusEvent(source, target,
                     new TwitterStatus(graph[EventTargetObjectKey]), ev, timestamp));
                 break;
             case "block":
             case "unblock":
             case "follow":
             case "unfollow":
             case "mute":
             case "unmute":
             case "user_update":
             case "user_delete":
             case "user_suspend":
                 handler.OnMessage(new StreamUserEvent(source, target,
                     ev, timestamp));
                 break;
             case "list_created":
             case "list_destroyed":
             case "list_updated":
             case "list_member_added":
             case "list_member_removed":
             case "list_user_subscribed":
             case "list_user_unsubscribed":
                 handler.OnMessage(new StreamListEvent(source, target,
                     new TwitterList(graph[EventTargetObjectKey]), ev, timestamp));
                 break;
             case "access_revoked":
             case "access_unrevoked":
                 handler.OnMessage(new StreamAccessInformationEvent(source, target,
                     new AccessInformation(graph[EventTargetObjectKey]), ev, timestamp));
                 break;
         }
     }
     catch (Exception ex)
     {
         handler.OnException(new StreamParseException(
             "Event parse failed:" + ev, graph.ToString(), ex));
     }
 }
コード例 #10
0
ファイル: TwitterStreamParser.cs プロジェクト: karno/Cadena
        /// <summary>
        /// Parse streamed JSON line (which is not a status)
        /// </summary>
        /// <param name="graph">JSON object graph</param>
        /// <param name="handler">result handler</param>
        internal static void ParseNotStatusStreamLine(JsonValue graph, IStreamHandler handler)
        {
            try
            {
                // element.foo() -> element.IsDefined("foo")

                // direct message
                JsonValue directMessage;
                if (graph.TryGetValue("direct_message", out directMessage))
                {
                    handler.OnStatus(new TwitterStatus(graph["direct_message"]));
                    return;
                }

                // delete
                JsonValue delete;
                if (graph.TryGetValue("delete", out delete))
                {
                    var timestamp = GetTimestamp(delete);
                    JsonValue status;
                    if (delete.TryGetValue("status", out status))
                    {
                        handler.OnMessage(new StreamDelete(
                            Int64.Parse(status["id_str"].AsString()),
                            Int64.Parse(status["user_id_str"].AsString()),
                            timestamp));
                        return;
                    }
                    if (delete.TryGetValue("direct_message", out directMessage))
                    {
                        handler.OnMessage(new StreamDelete(
                            Int64.Parse(directMessage["id_str"].AsString()),
                            Int64.Parse(directMessage["user_id"].AsString()),
                            timestamp));
                        return;
                    }
                }

                // scrub_geo
                JsonValue scrubGeo;
                if (graph.TryGetValue("scrub_geo", out scrubGeo))
                {
                    handler.OnMessage(new StreamScrubGeo(
                        Int64.Parse(scrubGeo["user_id_str"].AsString()),
                        Int64.Parse(scrubGeo["up_to_status_id_str"].AsString()),
                        GetTimestamp(scrubGeo)));
                    return;
                }

                // limit
                JsonValue limit;
                if (graph.TryGetValue("limit", out limit))
                {
                    handler.OnMessage(new StreamLimit(
                        limit["track"].AsLong(),
                        GetTimestamp(limit)));
                    return;
                }

                // withheld
                JsonValue statusWithheld;
                if (graph.TryGetValue("status_withheld", out statusWithheld))
                {
                    handler.OnMessage(new StreamWithheld(
                        statusWithheld["user_id"].AsLong(),
                        statusWithheld["id"].AsLong(),
                        ((JsonArray)statusWithheld["withheld_in_countries"]).Select(s => s.AsString()).ToArray(),
                        GetTimestamp(statusWithheld)));
                    return;
                }
                JsonValue userWithheld;
                if (graph.TryGetValue("user_withheld", out userWithheld))
                {
                    handler.OnMessage(new StreamWithheld(
                        userWithheld["id"].AsLong(),
                        ((JsonArray)statusWithheld["withheld_in_countries"]).Select(s => s.AsString()).ToArray(),
                        GetTimestamp(statusWithheld)));
                    return;
                }

                // disconnect
                JsonValue disconnect;
                if (graph.TryGetValue("disconnect", out disconnect))
                {
                    handler.OnMessage(new StreamDisconnect(
                        (DisconnectCode)disconnect["code"].AsLong(),
                        disconnect["stream_name"].AsString(),
                        disconnect["reason"].AsString(),
                        GetTimestamp(disconnect)));
                    return;
                }

                // stall warning
                JsonValue warning;
                if (graph.TryGetValue("warning", out warning))
                {
                    var timestamp = GetTimestamp(warning);
                    var code = warning["code"].AsString();
                    if (code == "FALLING_BEHIND")
                    {
                        handler.OnMessage(new StreamStallWarning(
                            code,
                            warning["message"].AsString(),
                            (int)warning["percent_full"].AsLong(),
                            timestamp));
                        return;
                    }
                }

                // user update
                JsonValue @event;
                if (graph.TryGetValue("event", out @event))
                {
                    var ev = @event.AsString().ToLower();
                    if (ev == "user_update")
                    {
                        // parse user_update only in generic streams.
                        handler.OnMessage(new StreamUserEvent(
                            new TwitterUser(graph["source"]),
                            new TwitterUser(graph["target"]),
                            ev, graph["created_at"].AsString().ParseTwitterDateTime()));
                        return;
                    }
                    // unknown event...
                    handler.OnMessage(new StreamUnknownMessage("event: " + ev, graph.ToString()));
                }

                // unknown event-type...
                handler.OnMessage(new StreamUnknownMessage(null, graph.ToString()));

            }
            catch (Exception ex)
            {
                handler.OnException(new StreamParseException(
                    "Stream graph parse failed.", graph.ToString(), ex));
            }
        }