/// <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)); } }
/// <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)); } }
/// <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)); } }
/// <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)); } }
/// <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)); } }
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); }
/// <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)); } }
/// <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)); } }
/// <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)); } }
/// <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)); } }