From 561d0e0339efeb6d0d2eec670057bb5708e7dbdd Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Sat, 8 Feb 2020 21:13:28 +0100 Subject: [PATCH] Move /t_ms delta admin text type parsing to separate file --- fbchat-sharp/API/Client.cs | 708 +----------------- .../API/{ => Events}/ClientPayload.cs | 7 +- fbchat-sharp/API/{ => Events}/DeltaClass.cs | 0 fbchat-sharp/API/Events/DeltaType.cs | 457 +++++++++++ fbchat-sharp/API/{ => Events}/Event.cs | 0 fbchat-sharp/API/{ => Models}/Attachment.cs | 0 fbchat-sharp/API/{ => Models}/File.cs | 0 fbchat-sharp/API/{ => Models}/Image.cs | 0 fbchat-sharp/API/{ => Models}/Location.cs | 0 fbchat-sharp/API/{ => Models}/Message.cs | 11 +- fbchat-sharp/API/{ => Models}/Plan.cs | 0 fbchat-sharp/API/{ => Models}/Poll.cs | 0 fbchat-sharp/API/{ => Models}/QuickReply.cs | 0 fbchat-sharp/API/{ => Models}/Sticker.cs | 0 fbchat-sharp/API/{ => Threads}/Group.cs | 0 fbchat-sharp/API/{ => Threads}/Marketplace.cs | 0 fbchat-sharp/API/{ => Threads}/Page.cs | 0 fbchat-sharp/API/{ => Threads}/Thread.cs | 0 fbchat-sharp/API/{ => Threads}/User.cs | 0 fbchat-sharp/Utils/Extensions.cs | 5 - fbchat-sharp/fbchat-sharp.csproj | 35 +- 21 files changed, 497 insertions(+), 726 deletions(-) rename fbchat-sharp/API/{ => Events}/ClientPayload.cs (96%) rename fbchat-sharp/API/{ => Events}/DeltaClass.cs (100%) create mode 100644 fbchat-sharp/API/Events/DeltaType.cs rename fbchat-sharp/API/{ => Events}/Event.cs (100%) rename fbchat-sharp/API/{ => Models}/Attachment.cs (100%) rename fbchat-sharp/API/{ => Models}/File.cs (100%) rename fbchat-sharp/API/{ => Models}/Image.cs (100%) rename fbchat-sharp/API/{ => Models}/Location.cs (100%) rename fbchat-sharp/API/{ => Models}/Message.cs (98%) rename fbchat-sharp/API/{ => Models}/Plan.cs (100%) rename fbchat-sharp/API/{ => Models}/Poll.cs (100%) rename fbchat-sharp/API/{ => Models}/QuickReply.cs (100%) rename fbchat-sharp/API/{ => Models}/Sticker.cs (100%) rename fbchat-sharp/API/{ => Threads}/Group.cs (100%) rename fbchat-sharp/API/{ => Threads}/Marketplace.cs (100%) rename fbchat-sharp/API/{ => Threads}/Page.cs (100%) rename fbchat-sharp/API/{ => Threads}/Thread.cs (100%) rename fbchat-sharp/API/{ => Threads}/User.cs (100%) diff --git a/fbchat-sharp/API/Client.cs b/fbchat-sharp/API/Client.cs index a09c0a3..b620440 100644 --- a/fbchat-sharp/API/Client.cs +++ b/fbchat-sharp/API/Client.cs @@ -1053,287 +1053,28 @@ public async Task deleteMessages(List message_ids) private async Task _parseDelta(JToken m) { - var delta = m.get("delta"); - var delta_type = delta.get("type")?.Value(); - var metadata = delta.get("messageMetadata"); - - var mid = metadata?.get("messageId")?.Value(); - var author_id = metadata?.get("actorFbId")?.Value(); - long.TryParse(metadata?.get("timestamp")?.Value(), out long ts); - - // Color change - if (delta.get("change_thread_theme") != null) - { - var new_color = ThreadColor._from_graphql(delta.get("untypedData")?.get("theme_color")); - var thread = FB_Thread._from_metadata(metadata, _session); - await this.onColorChange( - mid: mid, - author_id: author_id, - new_color: new_color, - thread: thread, - ts: ts, - metadata: metadata, - msg: m - ); - } - // Emoji change - else if (delta_type == "change_thread_icon") - { - var new_emoji = delta.get("untypedData")?.get("thread_icon")?.Value(); - var thread = FB_Thread._from_metadata(metadata, _session); - await this.onEmojiChange( - mid: mid, - author_id: author_id, - new_emoji: new_emoji, - thread: thread, - ts: ts, - metadata: metadata, - msg: m - ); - } - // Nickname change - else if (delta_type == "change_thread_nickname") - { - var changed_for = delta.get("untypedData")?.get("participant_id")?.Value(); - var new_nickname = delta.get("untypedData")?.get("nickname")?.Value(); - var thread = FB_Thread._from_metadata(metadata, _session); - await this.onNicknameChange( - mid: mid, - author_id: author_id, - changed_for: changed_for, - new_nickname: new_nickname, - thread: thread, - ts: ts, - metadata: metadata, - msg: m - ); - } - // Admin added or removed in a group thread - else if (delta_type == "change_thread_admins") - { - var thread = FB_Thread._from_metadata(metadata, _session); - var target_id = delta.get("untypedData")?.get("TARGET_ID")?.Value(); - var admin_event = delta.get("untypedData")?.get("ADMIN_EVENT")?.Value(); - if (admin_event == "add_admin") - await this.onAdminAdded( - mid: mid, - added_id: target_id, - author_id: author_id, - thread: thread, - ts: ts, - msg: m - ); - else if (admin_event == "remove_admin") - await this.onAdminRemoved( - mid: mid, - removed_id: target_id, - author_id: author_id, - thread: thread, - ts: ts, - msg: m - ); - } - // Group approval mode change - else if (delta_type == "change_thread_approval_mode") - { - var thread = FB_Thread._from_metadata(metadata, _session); - var approval_mode = long.Parse(delta.get("untypedData")?.get("APPROVAL_MODE")?.Value()) != 0; - await this.onApprovalModeChange( - mid: mid, - approval_mode: approval_mode, - author_id: author_id, - thread: thread, - ts: ts, - msg: m - ); - } - // Game played - else if (delta_type == "instant_game_update") - { - var game_id = delta.get("untypedData")?.get("game_id"); - var game_name = delta.get("untypedData")?.get("game_name"); - var score = delta.get("untypedData")?.get("score") != null ? (int?)long.Parse(delta.get("untypedData")?.get("score")?.Value()) : null; - var leaderboard = delta.get("untypedData")?.get("leaderboard") != null ? JToken.Parse(delta.get("untypedData")?.get("leaderboard")?.Value()).get("scores") : null; - var thread = FB_Thread._from_metadata(metadata, _session); - await this.onGamePlayed( - mid: mid, - author_id: author_id, - game_id: game_id, - game_name: game_name, - score: score, - leaderboard: leaderboard, - thread: thread, - ts: ts, - metadata: metadata, - msg: m - ); - } - // Group call started/ended - else if (delta_type == "rtc_call_log") - { - var thread = FB_Thread._from_metadata(metadata, _session); - var call_status = delta.get("untypedData")?.get("event")?.Value(); - int call_duration = int.Parse(delta.get("untypedData")?.get("call_duration")?.Value()); - var is_video_call = int.Parse(delta.get("untypedData")?.get("is_video_call")?.Value()) != 0; - if (call_status == "call_started") - await this.onCallStarted( - mid: mid, - caller_id: author_id, - is_video_call: is_video_call, - thread: thread, - ts: ts, - metadata: metadata, - msg: m - ); - else if (call_status == "call_ended") - await this.onCallEnded( - mid: mid, - caller_id: author_id, - is_video_call: is_video_call, - call_duration: call_duration, - thread: thread, - ts: ts, - metadata: metadata, - msg: m - ); - } - // User joined to group call - else if (delta_type == "participant_joined_group_call") - { - var thread = FB_Thread._from_metadata(metadata, _session); - var is_video_call = long.Parse(delta.get("untypedData")?.get("group_call_type")?.Value()) != 0; - await this.onUserJoinedCall( - mid: mid, - joined_id: author_id, - is_video_call: is_video_call, - thread: thread, - ts: ts, - metadata: metadata, - msg: m - ); - } - // Group poll event - else if (delta_type == "group_poll") - { - var thread = FB_Thread._from_metadata(metadata, _session); - var event_type = delta.get("untypedData")?.get("event_type")?.Value(); - var poll_json = JToken.Parse(delta.get("untypedData")?.get("question_json")?.Value()); - var poll = FB_Poll._from_graphql(poll_json, _session); - if (event_type == "question_creation") - // User created group poll - await this.onPollCreated( - mid: mid, - poll: poll, - author_id: author_id, - thread: thread, - ts: ts, - metadata: metadata, - msg: m - ); - else if (event_type == "update_vote") - { - // User voted on group poll - var added_options = JToken.Parse(delta.get("untypedData")?.get("added_option_ids")?.Value()); - var removed_options = JToken.Parse(delta.get("untypedData")?.get("removed_option_ids")?.Value()); - await this.onPollVoted( - mid: mid, - poll: poll, - added_options: added_options, - removed_options: removed_options, - author_id: author_id, - thread: thread, - ts: ts, - metadata: metadata, - msg: m - ); - } - } - // Plan created - else if (delta_type == "lightweight_event_create") - { - var thread = FB_Thread._from_metadata(metadata, _session); - await this.onPlanCreated( - mid: mid, - plan: FB_Plan._from_pull(delta.get("untypedData"), _session), - author_id: author_id, - thread: thread, - ts: ts, - metadata: metadata, - msg: m - ); - } - // Plan ended - else if (delta_type == "lightweight_event_notify") - { - var thread = FB_Thread._from_metadata(metadata, _session); - await this.onPlanEnded( - mid: mid, - plan: FB_Plan._from_pull(delta.get("untypedData"), _session), - thread: thread, - ts: ts, - metadata: metadata, - msg: m - ); - } - // Plan edited - else if (delta_type == "lightweight_event_update") - { - var thread = FB_Thread._from_metadata(metadata, _session); - await this.onPlanEdited( - mid: mid, - plan: FB_Plan._from_pull(delta.get("untypedData"), _session), - author_id: author_id, - thread: thread, - ts: ts, - metadata: metadata, - msg: m - ); - } - // Plan deleted - else if (delta_type == "lightweight_event_delete") - { - var thread = FB_Thread._from_metadata(metadata, _session); - await this.onPlanDeleted( - mid: mid, - plan: FB_Plan._from_pull(delta.get("untypedData"), _session), - author_id: author_id, - thread: thread, - ts: ts, - metadata: metadata, - msg: m - ); - } - // Plan participation change - else if (delta_type == "lightweight_event_rsvp") - { - var thread = FB_Thread._from_metadata(metadata, _session); - var take_part = delta.get("untypedData")?.get("guest_status")?.Value() == "GOING"; - await this.onPlanParticipation( - mid: mid, - plan: FB_Plan._from_pull(delta.get("untypedData"), _session), - take_part: take_part, - author_id: author_id, - thread: thread, - ts: ts, - metadata: metadata, - msg: m - ); - } + var delta = m.get("delta"); + // Client payload (that weird numbers) - else if (delta.get("class")?.Value() == "ClientPayload") + if (delta.get("class")?.Value() == "ClientPayload") { - foreach (var ev in ClientPayload.parse_client_payloads(this._session, delta).OrEmptyIfNull()) + foreach (var ev in ClientPayload.parse_client_payloads(this._session, delta) ?? Enumerable.Empty()) { await this.onEvent(ev); } } - // Client payload (that weird numbers) else if (delta.get("class")?.Value() != null) { var ev = DeltaClass.parse_delta(_session, delta); if (ev != null) await this.onEvent(ev); } + else if (delta.get("type")?.Value() != null) + { + var ev = DeltaType.parse_delta(_session, delta); + if (ev != null) + await this.onEvent(ev); + } // Unknown message type else await this.onUnknownMesssageType(msg: m); @@ -1704,7 +1445,7 @@ private async Task _parse_mqtt(string event_type, JToken event_data) // event_data.get("lastIssuedSeqId")?.Value() ?? event_data.get("deltas")?.LastOrDefault()?.get("irisSeqId")?.Value() ?? _mqtt_sequence_id); } - foreach (var delta in event_data.get("deltas").OrEmptyIfNull()) + foreach (var delta in event_data.get("deltas") ?? Enumerable.Empty()) await this._parseDelta(new JObject() { { "delta", delta } }); } } @@ -1948,157 +1689,6 @@ protected virtual async Task onPendingMessage(FB_Thread thread = null, JToken me await Task.Yield(); } - /// - /// Called when the client is listening, and somebody changes a thread's color - /// - /// The action ID - /// The ID of the person who changed the color - /// The new color - /// Thread that the action was sent to - /// A timestamp of the action - /// Extra metadata about the action - /// A full set of the data received - protected virtual async Task onColorChange(string mid = null, string author_id = null, string new_color = null, FB_Thread thread = null, long ts = 0, JToken metadata = null, JToken msg = null) - { - /* - * Called when the client is listening, and somebody changes a thread's color - * :param mid: The action ID - * : param author_id: The ID of the person who changed the color - * : param new_color: The new color - * :param thread: Thread that the action was sent to. See: ref:`intro_threads` - * :param ts: A timestamp of the action - * : param metadata: Extra metadata about the action - * : param msg: A full set of the data received - * : type new_color: ThreadColor - * */ - Debug.WriteLine(string.Format("Color change from {0} in {1}: {2}", author_id, thread.uid, new_color)); - await Task.Yield(); - } - - /// - /// Called when the client is listening, and somebody changes a thread's emoji - /// - /// The action ID - /// The ID of the person who changed the emoji - /// The new emoji - /// Thread that the action was sent to - /// A timestamp of the action - /// Extra metadata about the action - /// A full set of the data received - protected virtual async Task onEmojiChange(string mid = null, string author_id = null, string new_emoji = null, FB_Thread thread = null, long ts = 0, JToken metadata = null, JToken msg = null) - { - /* - * Called when the client is listening, and somebody changes a thread's emoji - * :param mid: The action ID - * : param author_id: The ID of the person who changed the emoji - * : param new_emoji: The new emoji - * :param thread: Thread that the action was sent to. See: ref:`intro_threads` - * :param ts: A timestamp of the action - * : param metadata: Extra metadata about the action - * : param msg: A full set of the data received - * */ - Debug.WriteLine(string.Format("Emoji change from {0} in {1}: {2}", author_id, thread.uid, new_emoji)); - await Task.Yield(); - } - - /// - /// Called when the client is listening, and somebody changes the nickname of a person - /// - /// The action ID - /// The ID of the person who changed the nickname - /// The ID of the person whom got their nickname changed - /// The new nickname - /// Thread that the action was sent to - /// A timestamp of the action - /// Extra metadata about the action - /// A full set of the data received - protected virtual async Task onNicknameChange(string mid = null, string author_id = null, string changed_for = null, string new_nickname = null, FB_Thread thread = null, long ts = 0, JToken metadata = null, JToken msg = null) - { - /* - * Called when the client is listening, and somebody changes the nickname of a person - * :param mid: The action ID - * : param author_id: The ID of the person who changed the nickname - * : param changed_for: The ID of the person whom got their nickname changed - * :param new_nickname: The new nickname - * :param thread: Thread that the action was sent to. See: ref:`intro_threads` - * :param ts: A timestamp of the action - * : param metadata: Extra metadata about the action - * : param msg: A full set of the data received - * */ - Debug.WriteLine(string.Format("Nickname change from {0} in {1} for {2}: {3}", author_id, thread.uid, changed_for, new_nickname)); - await Task.Yield(); - } - - /// - /// Called when the client is listening, and somebody adds an admin to a group thread - /// - /// The action ID - /// The ID of the admin who got added - /// The ID of the person who added the admins - /// Thread that the action was sent to. See :ref:`intro_threads` - /// A timestamp of the action - /// A full set of the data received - protected virtual async Task onAdminAdded( - string mid = null, - string added_id = null, - string author_id = null, - FB_Thread thread = null, - long ts = 0, - JToken msg = null) - { - Debug.WriteLine(string.Format("{0} added admin: {1} in {2}", author_id, added_id, thread.uid)); - await Task.Yield(); - } - - /// - /// Called when the client is listening, and somebody removes an admin from a group thread - /// - /// The action ID - /// The ID of the admin who got removed - /// The ID of the person who removed the admins - /// Thread that the action was sent to. See :ref:`intro_threads` - /// A timestamp of the action - /// A full set of the data received - protected virtual async Task onAdminRemoved( - string mid = null, - string removed_id = null, - string author_id = null, - FB_Thread thread = null, - long ts = 0, - JToken msg = null) - { - Debug.WriteLine(string.Format("{0} removed admin: {1} in {2}", author_id, removed_id, thread.uid)); - await Task.Yield(); - } - - /// - /// Called when the client is listening, and somebody changes approval mode in a group thread - /// - /// The action ID - /// True if approval mode is activated - /// The ID of the person who changed approval mode - /// Thread that the action was sent to. See :ref:`intro_threads` - /// A timestamp of the action - /// A full set of the data received - protected virtual async Task onApprovalModeChange( - string mid = null, - bool approval_mode = false, - string author_id = null, - FB_Thread thread = null, - long ts = 0, - JToken msg = null) - { - if (approval_mode) - { - Debug.WriteLine(string.Format("{0} activated approval mode in {1}", author_id, thread.uid)); - } - else - { - Debug.WriteLine(string.Format("{0} disabled approval mode in {1}", author_id, thread.uid)); - } - await Task.Yield(); - } - /// /// Called when the client is listening, and somebody sends a friend request /// @@ -2144,282 +1734,6 @@ protected virtual async Task onTyping( await Task.Yield(); } - /// - /// Called when the client is listening, and somebody plays a game - /// - /// The action ID - /// The ID of the person who played the game - /// The ID of the game - /// Name of the game - /// Score obtained in the game - /// Actual leaderboard of the game in the thread - /// Thread that the action was sent to. See :ref:`intro_threads` - /// A timestamp of the action - /// Extra metadata about the action - /// A full set of the data received - protected virtual async Task onGamePlayed( - string mid = null, - string author_id = null, - object game_id = null, - object game_name = null, - object score = null, - object leaderboard = null, - FB_Thread thread = null, - long ts = 0, - JToken metadata = null, - JToken msg = null) - { - Debug.WriteLine(string.Format("{0} played \"{1}\" in {2}", author_id, game_name, thread.uid)); - await Task.Yield(); - } - - /// - /// .. todo:: - /// Make this work with private calls - /// Called when the client is listening, and somebody starts a call in a group - /// - /// The action ID - /// The ID of the person who started the call - /// True if it's video call - /// Thread that the action was sent to. See :ref:`intro_threads` - /// A timestamp of the action - /// Extra metadata about the action - /// A full set of the data received - protected virtual async Task onCallStarted( - string mid = null, - object caller_id = null, - object is_video_call = null, - FB_Thread thread = null, - long ts = 0, - JToken metadata = null, - JToken msg = null) - { - Debug.WriteLine(string.Format("{0} started call in {1}", caller_id, thread.uid)); - await Task.Yield(); - } - - /// - /// .. todo:: - /// Make this work with private calls - /// Called when the client is listening, and somebody ends a call in a group - /// - /// The action ID - /// The ID of the person who ended the call - /// True if it was video call - /// Call duration in seconds - /// Thread that the action was sent to. See :ref:`intro_threads` - /// A timestamp of the action - /// Extra metadata about the action - /// A full set of the data received - protected virtual async Task onCallEnded( - string mid = null, - object caller_id = null, - object is_video_call = null, - object call_duration = null, - FB_Thread thread = null, - long ts = 0, - JToken metadata = null, - JToken msg = null) - { - Debug.WriteLine(string.Format("{0} ended call in {1}", caller_id, thread.uid)); - await Task.Yield(); - } - - /// - /// Called when the client is listening, and somebody joins a group call - /// - /// The action ID - /// The ID of the person who joined the call - /// True if it's video call - /// Thread that the action was sent to. See :ref:`intro_threads` - /// A timestamp of the action - /// Extra metadata about the action - /// A full set of the data received - protected virtual async Task onUserJoinedCall( - string mid = null, - object joined_id = null, - object is_video_call = null, - FB_Thread thread = null, - long ts = 0, - JToken metadata = null, - JToken msg = null) - { - Debug.WriteLine(string.Format("{0} joined call in {1}", joined_id, thread.uid)); - await Task.Yield(); - } - - /// - /// Called when the client is listening, and somebody creates a group poll - /// - /// The action ID - /// Created poll - /// The ID of the person who created the poll - /// Thread that the action was sent to. See :ref:`intro_threads` - /// A timestamp of the action - /// Extra metadata about the action - /// A full set of the data received - protected virtual async Task onPollCreated( - string mid = null, - object poll = null, - string author_id = null, - FB_Thread thread = null, - long ts = 0, - JToken metadata = null, - JToken msg = null) - { - Debug.WriteLine(string.Format("{0} created poll {1} in {2}", author_id, poll, thread.uid)); - await Task.Yield(); - } - - /// - /// Called when the client is listening, and somebody votes in a group poll - /// - /// The action ID - /// Poll, that user voted in - /// - /// - /// The ID of the person who voted in the poll - /// Thread that the action was sent to. See :ref:`intro_threads` - /// A timestamp of the action - /// Extra metadata about the action - /// A full set of the data received - protected virtual async Task onPollVoted( - string mid = null, - object poll = null, - object added_options = null, - object removed_options = null, - string author_id = null, - FB_Thread thread = null, - long ts = 0, - JToken metadata = null, - JToken msg = null) - { - Debug.WriteLine(string.Format("{0} voted in poll {1} in {2}", author_id, poll, thread.uid)); - await Task.Yield(); - } - - /// - /// Called when the client is listening, and somebody creates a plan - /// - /// The action ID - /// Created plan - /// The ID of the person who created the plan - /// Thread that the action was sent to. See :ref:`intro_threads` - /// A timestamp of the action - /// Extra metadata about the action - /// A full set of the data received - protected virtual async Task onPlanCreated( - string mid = null, - object plan = null, - string author_id = null, - FB_Thread thread = null, - long ts = 0, - JToken metadata = null, - JToken msg = null) - { - Debug.WriteLine(string.Format("{0} created plan {1} in {2}", author_id, plan, thread.uid)); - await Task.Yield(); - } - - /// - /// Called when the client is listening, and a plan ends - /// - /// The action ID - /// Ended plan - /// Thread that the action was sent to. See :ref:`intro_threads` - /// A timestamp of the action - /// Extra metadata about the action - /// A full set of the data received - protected virtual async Task onPlanEnded( - string mid = null, - object plan = null, - FB_Thread thread = null, - long ts = 0, - JToken metadata = null, - JToken msg = null) - { - Debug.WriteLine(string.Format("Plan {0} has ended in {1}", plan, thread.uid)); - await Task.Yield(); - } - - /// - /// Called when the client is listening, and somebody edits a plan - /// - /// The action ID - /// Edited plan - /// The ID of the person who edited the plan - /// Thread that the action was sent to. See :ref:`intro_threads` - /// A timestamp of the action - /// Extra metadata about the action - /// A full set of the data received - protected virtual async Task onPlanEdited( - string mid = null, - object plan = null, - string author_id = null, - FB_Thread thread = null, - long ts = 0, - JToken metadata = null, - JToken msg = null) - { - Debug.WriteLine(string.Format("{0} edited plan {1} in {2}", author_id, plan, thread.uid)); - await Task.Yield(); - } - - /// - /// Called when the client is listening, and somebody deletes a plan - /// - /// The action ID - /// Deleted plan - /// The ID of the person who deleted the plan - /// Thread that the action was sent to. See :ref:`intro_threads` - /// A timestamp of the action - /// Extra metadata about the action - /// A full set of the data received - protected virtual async Task onPlanDeleted( - string mid = null, - object plan = null, - string author_id = null, - FB_Thread thread = null, - long ts = 0, - JToken metadata = null, - JToken msg = null) - { - Debug.WriteLine(string.Format("{0} deleted plan {1} in {2}", author_id, plan, thread.uid)); - await Task.Yield(); - } - - /// - /// Called when the client is listening, and somebody takes part in a plan or not - /// - /// The action ID - /// Plan - /// Whether the person takes part in the plan or not - /// The ID of the person who will participate in the plan or not - /// Thread that the action was sent to. See :ref:`intro_threads` - /// A timestamp of the action - /// Extra metadata about the action - /// A full set of the data received - protected virtual async Task onPlanParticipation( - string mid = null, - FB_Plan plan = null, - bool take_part = false, - string author_id = null, - FB_Thread thread = null, - long ts = 0, - JToken metadata = null, - JToken msg = null) - { - if (take_part) - { - Debug.WriteLine(string.Format("{0} will take part in {1} in {2}", author_id, plan, thread.uid)); - } - else - { - Debug.WriteLine(string.Format("{0} won't take part in {1} in {2}", author_id, plan, thread.uid)); - } - await Task.Yield(); - } - /// /// Called when the client just started listening /// diff --git a/fbchat-sharp/API/ClientPayload.cs b/fbchat-sharp/API/Events/ClientPayload.cs similarity index 96% rename from fbchat-sharp/API/ClientPayload.cs rename to fbchat-sharp/API/Events/ClientPayload.cs index 4547b77..3066be2 100644 --- a/fbchat-sharp/API/ClientPayload.cs +++ b/fbchat-sharp/API/Events/ClientPayload.cs @@ -1,5 +1,6 @@ using Newtonsoft.Json.Linq; using System.Collections.Generic; +using System.Linq; namespace fbchat_sharp.API { @@ -58,7 +59,7 @@ public class FB_LiveLocationEvent : FB_ThreadEvent { var thread = FB_LiveLocationEvent._get_thread(session, data); - foreach (var location_data in data?.get("messageLiveLocations").OrEmptyIfNull()) + foreach (var location_data in data?.get("messageLiveLocations") ?? Enumerable.Empty()) { var message = new FB_Message(session: session, thread_id: thread.uid, uid: data?.get("messageId")?.Value()); var author = new FB_User(session: session, uid: location_data?.get("senderId")?.Value()); @@ -123,9 +124,9 @@ internal class ClientPayload { public static IEnumerable parse_client_payloads(Session session, JToken data) { - var payload = JToken.Parse(string.Join("", data.get("payload")?.Value())); + var payload = JToken.Parse(string.Join("", data.get("payload")?.Select(x => x?.Value()?.ToString()))); - foreach (var d in payload.get("deltas").OrEmptyIfNull()) + foreach (var d in payload?.get("deltas") ?? Enumerable.Empty()) { yield return parse_client_delta(session, d); } diff --git a/fbchat-sharp/API/DeltaClass.cs b/fbchat-sharp/API/Events/DeltaClass.cs similarity index 100% rename from fbchat-sharp/API/DeltaClass.cs rename to fbchat-sharp/API/Events/DeltaClass.cs diff --git a/fbchat-sharp/API/Events/DeltaType.cs b/fbchat-sharp/API/Events/DeltaType.cs new file mode 100644 index 0000000..db5b373 --- /dev/null +++ b/fbchat-sharp/API/Events/DeltaType.cs @@ -0,0 +1,457 @@ +using Newtonsoft.Json.Linq; +using System.Collections.Generic; +using System.Linq; + +namespace fbchat_sharp.API +{ + internal class DeltaType + { + /// + /// Somebody set the color in a thread. + /// + public class FB_ColorSet : FB_ThreadEvent + { + /// The new color. Not limited to the ones in `ThreadABC.set_color` + public string color { get; set; } + /// When the color was set + public long at { get; set; } + + public static new FB_ColorSet _parse(Session session, JToken data) + { + (FB_User author, FB_Thread thread, long at) = FB_ColorSet._parse_metadata(session, data); + var color = ThreadColor._from_graphql(data?.get("untypedData")?.get("theme_color")); + return new FB_ColorSet() + { + author = author, + thread = thread, + color = color, + at = at + }; + } + } + + /// + /// Somebody set the emoji in a thread. + /// + public class FB_EmojiSet : FB_ThreadEvent + { + /// The new color. Not limited to the ones in `ThreadABC.set_color` + public string emoji { get; set; } + /// When the color was set + public long at { get; set; } + + public static new FB_EmojiSet _parse(Session session, JToken data) + { + (FB_User author, FB_Thread thread, long at) = FB_EmojiSet._parse_metadata(session, data); + var emoji = data?.get("untypedData")?.get("thread_icon")?.Value(); + return new FB_EmojiSet() + { + author = author, + thread = thread, + emoji = emoji, + at = at + }; + } + } + + /// + /// Somebody set the nickname of a person in a thread. + /// + public class FB_NicknameSet : FB_ThreadEvent + { + /// The person whose nickname was set + public FB_User subject { get; set; } + /// The new nickname. If ``None``, the nickname was cleared + public string nickname { get; set; } + /// When the nickname was set + public long at { get; set; } + + public static new FB_NicknameSet _parse(Session session, JToken data) + { + (FB_User author, FB_Thread thread, long at) = FB_NicknameSet._parse_metadata(session, data); + var subject = new FB_User(data?.get("untypedData")?.get("participant_id")?.Value(), session); + var nickname = data?.get("untypedData")?.get("nickname")?.Value(); + return new FB_NicknameSet() + { + author = author, + thread = thread, + subject = subject, + nickname = nickname, + at = at + }; + } + } + + /// + /// Somebody added admins to a group. + /// + public class FB_AdminsAdded : FB_ThreadEvent + { + /// The people that were set as admins + public List added { get; set; } + /// When the admins were added + public long at { get; set; } + + public static new FB_AdminsAdded _parse(Session session, JToken data) + { + (FB_User author, FB_Thread thread, long at) = FB_AdminsAdded._parse_metadata(session, data); + var target = new FB_User(data?.get("untypedData")?.get("TARGET_ID")?.Value(), session); + return new FB_AdminsAdded() + { + author = author, + thread = thread, + added = new List() { target }, + at = at + }; + } + } + + /// + /// Somebody removed admins from a group. + /// + public class FB_AdminsRemoved : FB_ThreadEvent + { + /// The people that were removed as admins + public List removed { get; set; } + /// When the admins were removed + public long at { get; set; } + + public static new FB_AdminsRemoved _parse(Session session, JToken data) + { + (FB_User author, FB_Thread thread, long at) = FB_AdminsRemoved._parse_metadata(session, data); + var target = new FB_User(data?.get("untypedData")?.get("TARGET_ID")?.Value(), session); + return new FB_AdminsRemoved() + { + author = author, + thread = thread, + removed = new List() { target }, + at = at + }; + } + } + + /// + /// Somebody changed the approval mode in a group. + /// + public class FB_ApprovalModeSet : FB_ThreadEvent + { + /// The new approval mode + public bool require_admin_approval { get; set; } + /// When the approval mode was set + public long at { get; set; } + + public static new FB_ApprovalModeSet _parse(Session session, JToken data) + { + (FB_User author, FB_Thread thread, long at) = FB_ApprovalModeSet._parse_metadata(session, data); + var approval_mode = long.Parse(data?.get("untypedData")?.get("APPROVAL_MODE")?.Value()) != 0; + return new FB_ApprovalModeSet() + { + author = author, + thread = thread, + require_admin_approval = approval_mode, + at = at + }; + } + } + + /// + /// Somebody started a call. + /// + public class FB_CallStarted : FB_ThreadEvent + { + /// When the call was started + public long at { get; set; } + + public static new FB_CallStarted _parse(Session session, JToken data) + { + (FB_User author, FB_Thread thread, long at) = FB_CallStarted._parse_metadata(session, data); + return new FB_CallStarted() + { + author = author, + thread = thread, + at = at + }; + } + } + + /// + /// Somebody ended a call. + /// + public class FB_CallEnded : FB_ThreadEvent + { + /// How long the call took + public long duration { get; set; } + /// When the call ended + public long at { get; set; } + + public static new FB_CallEnded _parse(Session session, JToken data) + { + (FB_User author, FB_Thread thread, long at) = FB_CallEnded._parse_metadata(session, data); + long call_duration = long.Parse(data?.get("untypedData")?.get("call_duration")?.Value()); + return new FB_CallEnded() + { + author = author, + thread = thread, + duration = call_duration, + at = at + }; + } + } + + /// + /// Somebody joined a call. + /// + public class FB_CallJoined : FB_ThreadEvent + { + /// When the call was joined + public long at { get; set; } + + public static new FB_CallJoined _parse(Session session, JToken data) + { + (FB_User author, FB_Thread thread, long at) = FB_CallJoined._parse_metadata(session, data); + return new FB_CallJoined() + { + author = author, + thread = thread, + at = at + }; + } + } + + /// + /// Somebody created a group poll. + /// + public class FB_PollCreated : FB_ThreadEvent + { + /// The new poll + public FB_Poll poll { get; set; } + /// When the pall was created + public long at { get; set; } + + public static new FB_PollCreated _parse(Session session, JToken data) + { + (FB_User author, FB_Thread thread, long at) = FB_PollCreated._parse_metadata(session, data); + var poll_json = JToken.Parse(data?.get("untypedData")?.get("question_json")?.Value()); + var poll = FB_Poll._from_graphql(poll_json, session); + return new FB_PollCreated() + { + author = author, + thread = thread, + poll = poll, + at = at + }; + } + } + + /// + /// Somebody voited in a group poll. + /// + public class FB_PollVoted : FB_ThreadEvent + { + /// The updated poll + public FB_Poll poll { get; set; } + /// Ids of the voted options + public List added_ids { get; set; } + /// Ids of the un-voted options + public List removed_ids { get; set; } + /// When the pall was voted in + public long at { get; set; } + + public static new FB_PollVoted _parse(Session session, JToken data) + { + (FB_User author, FB_Thread thread, long at) = FB_PollVoted._parse_metadata(session, data); + var poll_json = JToken.Parse(data?.get("untypedData")?.get("question_json")?.Value()); + var poll = FB_Poll._from_graphql(poll_json, session); + var added_options = JToken.Parse(data?.get("untypedData")?.get("added_option_ids")?.Value()); + var removed_options = JToken.Parse(data?.get("untypedData")?.get("removed_option_ids")?.Value()); + return new FB_PollVoted() + { + author = author, + thread = thread, + poll = poll, + added_ids = added_options.Select(x => x.ToString()).ToList(), + removed_ids = removed_options.Select(x => x.ToString()).ToList(), + at = at + }; + } + } + + /// + /// Somebody created plan in a group. + /// + public class FB_PlanCreated : FB_ThreadEvent + { + /// The new plan + public FB_Plan plan { get; set; } + /// When the plan was created + public long at { get; set; } + + public static new FB_PlanCreated _parse(Session session, JToken data) + { + (FB_User author, FB_Thread thread, long at) = FB_PlanCreated._parse_metadata(session, data); + var plan = FB_Plan._from_pull(data?.get("untypedData"), session); + return new FB_PlanCreated() + { + author = author, + thread = thread, + plan = plan, + at = at + }; + } + } + + /// + /// A plan ended. + /// + public class FB_PlanEnded : FB_ThreadEvent + { + /// The ended plan + public FB_Plan plan { get; set; } + /// When the plan ended + public long at { get; set; } + + public static new FB_PlanEnded _parse(Session session, JToken data) + { + (FB_User author, FB_Thread thread, long at) = FB_PlanEnded._parse_metadata(session, data); + var plan = FB_Plan._from_pull(data?.get("untypedData"), session); + return new FB_PlanEnded() + { + author = author, + thread = thread, + plan = plan, + at = at + }; + } + } + + /// + /// A plan was updated. + /// + public class FB_PlanEdited : FB_ThreadEvent + { + /// The updated plan + public FB_Plan plan { get; set; } + /// When the plan was updated + public long at { get; set; } + + public static new FB_PlanEdited _parse(Session session, JToken data) + { + (FB_User author, FB_Thread thread, long at) = FB_PlanEdited._parse_metadata(session, data); + var plan = FB_Plan._from_pull(data?.get("untypedData"), session); + return new FB_PlanEdited() + { + author = author, + thread = thread, + plan = plan, + at = at + }; + } + } + + /// + /// Somebody removed a plan in a group. + /// + public class FB_PlanDeleted : FB_ThreadEvent + { + /// The removed plan + public FB_Plan plan { get; set; } + /// When the plan was removed + public long at { get; set; } + + public static new FB_PlanDeleted _parse(Session session, JToken data) + { + (FB_User author, FB_Thread thread, long at) = FB_PlanDeleted._parse_metadata(session, data); + var plan = FB_Plan._from_pull(data?.get("untypedData"), session); + return new FB_PlanDeleted() + { + author = author, + thread = thread, + plan = plan, + at = at + }; + } + } + + /// + /// Somebody responded to a plan in a group. + /// + public class FB_PlanResponded : FB_ThreadEvent + { + /// The plan that was responded to + public FB_Plan plan { get; set; } + /// Whether the author will go to the plan or not + public bool take_part { get; set; } + /// When the user responded + public long at { get; set; } + + public static new FB_PlanResponded _parse(Session session, JToken data) + { + (FB_User author, FB_Thread thread, long at) = FB_PlanResponded._parse_metadata(session, data); + var plan = FB_Plan._from_pull(data?.get("untypedData"), session); + var take_part = data.get("untypedData")?.get("guest_status")?.Value() == "GOING"; + return new FB_PlanResponded() + { + author = author, + thread = thread, + plan = plan, + take_part = take_part, + at = at + }; + } + } + + public static FB_Event parse_delta(Session session, JToken data) + { + var type_ = data?.get("type")?.Value(); + if (type_ == "change_thread_theme") + return FB_ColorSet._parse(session, data); + else if (type_ == "change_thread_icon") + return FB_EmojiSet._parse(session, data); + else if (type_ == "change_thread_nickname") + return FB_NicknameSet._parse(session, data); + else if (type_ == "change_thread_admins") + { + var event_type = data?.get("untypedData")?.get("ADMIN_EVENT")?.Value(); + if (event_type == "add_admin") + return FB_AdminsAdded._parse(session, data); + else if (event_type == "remove_admin") + return FB_AdminsRemoved._parse(session, data); + } + else if (type_ == "change_thread_approval_mode") + return FB_ApprovalModeSet._parse(session, data); + else if (type_ == "instant_game_update") + { + // TODO: This + } + else if (type_ == "messenger_call_log") // Previously "rtc_call_log" + { + var event_type = data?.get("untypedData")?.get("event")?.Value(); + if (event_type == "group_call_started") + return FB_CallStarted._parse(session, data); + else if (new string[] { "group_call_ended", "one_on_one_call_ended" }.Contains(event_type)) + return FB_CallEnded._parse(session, data); + } + else if (type_ == "participant_joined_group_call") + return FB_CallJoined._parse(session, data); + else if (type_ == "group_poll") + { + var event_type = data?.get("untypedData")?.get("event_type")?.Value(); + if (event_type == "question_creation") + return FB_PollCreated._parse(session, data); + else if (event_type == "update_vote") + return FB_PollVoted._parse(session, data); + } + else if (type_ == "lightweight_event_create") + return FB_PlanCreated._parse(session, data); + else if (type_ == "lightweight_event_notify") + return FB_PlanEnded._parse(session, data); + else if (type_ == "lightweight_event_update") + return FB_PlanEdited._parse(session, data); + else if (type_ == "lightweight_event_delete") + return FB_PlanDeleted._parse(session, data); + else if (type_ == "lightweight_event_rsvp") + return FB_PlanResponded._parse(session, data); + return new FB_UnknownEvent() { data = data }; + } + } +} diff --git a/fbchat-sharp/API/Event.cs b/fbchat-sharp/API/Events/Event.cs similarity index 100% rename from fbchat-sharp/API/Event.cs rename to fbchat-sharp/API/Events/Event.cs diff --git a/fbchat-sharp/API/Attachment.cs b/fbchat-sharp/API/Models/Attachment.cs similarity index 100% rename from fbchat-sharp/API/Attachment.cs rename to fbchat-sharp/API/Models/Attachment.cs diff --git a/fbchat-sharp/API/File.cs b/fbchat-sharp/API/Models/File.cs similarity index 100% rename from fbchat-sharp/API/File.cs rename to fbchat-sharp/API/Models/File.cs diff --git a/fbchat-sharp/API/Image.cs b/fbchat-sharp/API/Models/Image.cs similarity index 100% rename from fbchat-sharp/API/Image.cs rename to fbchat-sharp/API/Models/Image.cs diff --git a/fbchat-sharp/API/Location.cs b/fbchat-sharp/API/Models/Location.cs similarity index 100% rename from fbchat-sharp/API/Location.cs rename to fbchat-sharp/API/Models/Location.cs diff --git a/fbchat-sharp/API/Message.cs b/fbchat-sharp/API/Models/Message.cs similarity index 98% rename from fbchat-sharp/API/Message.cs rename to fbchat-sharp/API/Models/Message.cs index 1084236..ec70449 100644 --- a/fbchat-sharp/API/Message.cs +++ b/fbchat-sharp/API/Models/Message.cs @@ -249,9 +249,12 @@ public Dictionary _to_send_data() if (this.text != null) data["body"] = this.text; - foreach (var item in this.mentions.Select((mention, i) => new { i, mention })) + if (this.mentions != null) { - data.update(item.mention._to_send_data(item.i)); + foreach (var item in this.mentions.Select((mention, i) => new { i, mention })) + { + data.update(item.mention._to_send_data(item.i)); + } } if (this.emoji_size != null) @@ -423,7 +426,7 @@ public static FB_Message _from_reply(JToken data, FB_Thread thread) else rtn.quick_replies = new List() { FB_QuickReply.graphql_to_quick_reply(quick_replies) }; } - foreach (var atc in data.get("attachments").OrEmptyIfNull()) + foreach (var atc in data.get("attachments") ?? Enumerable.Empty()) { var attachment = JToken.Parse(atc.get("mercuryJSON")?.Value()); if (attachment.get("blob_attachment") != null) @@ -459,7 +462,7 @@ public static FB_Message _from_pull(JToken data, FB_Thread thread, string author rtn.session = thread.session; rtn.thread_id = thread.uid; rtn.author = author; - rtn.timestamp = timestamp; + rtn.timestamp = timestamp; rtn.mentions = JToken.Parse(data.get("data")?.get("prng")?.Value() ?? "{}")?.Select((m) => FB_Mention._from_prng(m) ).ToList(); diff --git a/fbchat-sharp/API/Plan.cs b/fbchat-sharp/API/Models/Plan.cs similarity index 100% rename from fbchat-sharp/API/Plan.cs rename to fbchat-sharp/API/Models/Plan.cs diff --git a/fbchat-sharp/API/Poll.cs b/fbchat-sharp/API/Models/Poll.cs similarity index 100% rename from fbchat-sharp/API/Poll.cs rename to fbchat-sharp/API/Models/Poll.cs diff --git a/fbchat-sharp/API/QuickReply.cs b/fbchat-sharp/API/Models/QuickReply.cs similarity index 100% rename from fbchat-sharp/API/QuickReply.cs rename to fbchat-sharp/API/Models/QuickReply.cs diff --git a/fbchat-sharp/API/Sticker.cs b/fbchat-sharp/API/Models/Sticker.cs similarity index 100% rename from fbchat-sharp/API/Sticker.cs rename to fbchat-sharp/API/Models/Sticker.cs diff --git a/fbchat-sharp/API/Group.cs b/fbchat-sharp/API/Threads/Group.cs similarity index 100% rename from fbchat-sharp/API/Group.cs rename to fbchat-sharp/API/Threads/Group.cs diff --git a/fbchat-sharp/API/Marketplace.cs b/fbchat-sharp/API/Threads/Marketplace.cs similarity index 100% rename from fbchat-sharp/API/Marketplace.cs rename to fbchat-sharp/API/Threads/Marketplace.cs diff --git a/fbchat-sharp/API/Page.cs b/fbchat-sharp/API/Threads/Page.cs similarity index 100% rename from fbchat-sharp/API/Page.cs rename to fbchat-sharp/API/Threads/Page.cs diff --git a/fbchat-sharp/API/Thread.cs b/fbchat-sharp/API/Threads/Thread.cs similarity index 100% rename from fbchat-sharp/API/Thread.cs rename to fbchat-sharp/API/Threads/Thread.cs diff --git a/fbchat-sharp/API/User.cs b/fbchat-sharp/API/Threads/User.cs similarity index 100% rename from fbchat-sharp/API/User.cs rename to fbchat-sharp/API/Threads/User.cs diff --git a/fbchat-sharp/Utils/Extensions.cs b/fbchat-sharp/Utils/Extensions.cs index 1663749..1bb37ab 100644 --- a/fbchat-sharp/Utils/Extensions.cs +++ b/fbchat-sharp/Utils/Extensions.cs @@ -10,11 +10,6 @@ namespace fbchat_sharp { internal static class Extensions { - public static IEnumerable OrEmptyIfNull(this IEnumerable source) - { - return source ?? Enumerable.Empty(); - } - public static void update(this Dictionary dict, Dictionary mergeDict) { if (mergeDict != null) diff --git a/fbchat-sharp/fbchat-sharp.csproj b/fbchat-sharp/fbchat-sharp.csproj index cd7e6ae..813b68c 100644 --- a/fbchat-sharp/fbchat-sharp.csproj +++ b/fbchat-sharp/fbchat-sharp.csproj @@ -52,27 +52,28 @@ - + - - - + + + + - + - - - - - - - - - + + + + + + + + + - - - + + +