From 99d7c51b5f0cedd6f80b9aa466c88b3e8bbbb7fc Mon Sep 17 00:00:00 2001 From: Nordup Date: Sun, 17 Aug 2025 16:47:53 +0700 Subject: [PATCH] afk manager --- app/project.godot | 1 + app/scenes/app.tscn | 6 +- app/scripts/afk_manager.gd | 60 +++++++++++++++++++ app/scripts/api/analytics/analytics_events.gd | 12 ++++ .../api/analytics/analytics_sender_afk.gd | 19 ++++++ .../api/analytics/analytics_sender_app.gd | 15 ++++- app/scripts/ui/onboarding/onboarding.gd | 4 +- 7 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 app/scripts/afk_manager.gd create mode 100644 app/scripts/api/analytics/analytics_sender_afk.gd diff --git a/app/project.godot b/app/project.godot index 91d4f45..2ab96e1 100644 --- a/app/project.godot +++ b/app/project.godot @@ -35,6 +35,7 @@ Debug="*res://scripts/debug_log/debug.gd" AnalyticsEvents="*res://scripts/api/analytics/analytics_events.gd" Backend="*res://scripts/api/backend.gd" Navigation="*res://scenes/autoloads/navigation.tscn" +AfkManager="*res://scripts/afk_manager.gd" [debug] diff --git a/app/scenes/app.tscn b/app/scenes/app.tscn index b327b05..5a65fda 100644 --- a/app/scenes/app.tscn +++ b/app/scenes/app.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=20 format=3 uid="uid://ct8gsph3wnepl"] +[gd_scene load_steps=21 format=3 uid="uid://ct8gsph3wnepl"] [ext_resource type="Script" path="res://scripts/app.gd" id="1_skc7d"] [ext_resource type="Resource" uid="uid://b1xvdym0qh6td" path="res://resources/gate_events.res" id="2_cdryv"] @@ -19,6 +19,7 @@ [ext_resource type="Script" path="res://scripts/api/analytics/analytics_sender_onboarding.gd" id="15_a11br"] [ext_resource type="Script" path="res://scripts/api/featured_gates.gd" id="15_c1fxl"] [ext_resource type="Resource" uid="uid://crjhix0osmtnf" path="res://resources/ui_events.res" id="16_6u24i"] +[ext_resource type="Script" path="res://scripts/api/analytics/analytics_sender_afk.gd" id="17_xfh2c"] [node name="App" type="Node" node_paths=PackedStringArray("scenes_root")] script = ExtResource("1_skc7d") @@ -63,6 +64,9 @@ bookmarks = ExtResource("6_rupvx") script = ExtResource("15_a11br") ui_events = ExtResource("16_6u24i") +[node name="SenderAfk" type="Node" parent="Api/Analytics"] +script = ExtResource("17_xfh2c") + [node name="DiscoverGate" type="Node" parent="Api"] script = ExtResource("13_3xhql") api = ExtResource("10_04o5h") diff --git a/app/scripts/afk_manager.gd b/app/scripts/afk_manager.gd new file mode 100644 index 0000000..ce41440 --- /dev/null +++ b/app/scripts/afk_manager.gd @@ -0,0 +1,60 @@ +extends Node +#class_name AfkManager + +signal state_changed(is_afk: bool) + +const AFK_TIMEOUT_SEC = 180 + +var afk_check_timer: Timer + +var session_start_tick: int +var last_key_tick: int +var cumulative_afk_msec: int +var afk_start_tick: int + + +func _ready() -> void: + session_start_tick = Time.get_ticks_msec() + last_key_tick = session_start_tick + + afk_check_timer = Timer.new() + afk_check_timer.one_shot = false + afk_check_timer.wait_time = 1.0 + add_child(afk_check_timer) + + afk_check_timer.timeout.connect(check_afk) + afk_check_timer.start() + + +func _input(_event: InputEvent) -> void: + var now := Time.get_ticks_msec() + last_key_tick = now + if afk_start_tick != 0: + leave_afk(now) + + +func check_afk() -> void: + var now := Time.get_ticks_msec() + if afk_start_tick == 0 and now - last_key_tick >= AFK_TIMEOUT_SEC * 1000: + enter_afk(now) + + +func enter_afk(now: int) -> void: + afk_start_tick = now + state_changed.emit(true) + + +func leave_afk(now: int) -> void: + if afk_start_tick == 0: + return + + cumulative_afk_msec += now - afk_start_tick + afk_start_tick = 0 + state_changed.emit(false) + + +func get_active_sec() -> float: + var now := Time.get_ticks_msec() + var afk_current := (now - afk_start_tick) if afk_start_tick != 0 else 0 + var active_msec := (now - session_start_tick) - cumulative_afk_msec - afk_current + return max(0.0, float(active_msec) / 1000.0) diff --git a/app/scripts/api/analytics/analytics_events.gd b/app/scripts/api/analytics/analytics_events.gd index 9a56cbe..071d788 100644 --- a/app/scripts/api/analytics/analytics_events.gd +++ b/app/scripts/api/analytics/analytics_events.gd @@ -100,3 +100,15 @@ func onboarding_finished(time_spend: float) -> Dictionary: var event = base("onboarding_finished") event.time_spend = time_spend return event + + +# AFK + +func enter_afk() -> Dictionary: + return base("enter_afk") + + +func leave_afk(time_spend: float) -> Dictionary: + var event = base("leave_afk") + event.time_spend = time_spend + return event diff --git a/app/scripts/api/analytics/analytics_sender_afk.gd b/app/scripts/api/analytics/analytics_sender_afk.gd new file mode 100644 index 0000000..8cda735 --- /dev/null +++ b/app/scripts/api/analytics/analytics_sender_afk.gd @@ -0,0 +1,19 @@ +extends AnalyticsSender +class_name AnalyticsSenderAfk + +var afk_started_tick: int + + +func start() -> void: + super.start() + + AfkManager.state_changed.connect(send_afk_state_changed) + + +func send_afk_state_changed(is_afk: bool) -> void: + if is_afk: + afk_started_tick = Time.get_ticks_msec() + analytics.send_event(AnalyticsEvents.enter_afk()) + else: + var time_spend = Analytics.get_delta_sec_from_tick(afk_started_tick) + analytics.send_event(AnalyticsEvents.leave_afk(time_spend)) diff --git a/app/scripts/api/analytics/analytics_sender_app.gd b/app/scripts/api/analytics/analytics_sender_app.gd index 782ebe4..4582933 100644 --- a/app/scripts/api/analytics/analytics_sender_app.gd +++ b/app/scripts/api/analytics/analytics_sender_app.gd @@ -2,6 +2,7 @@ extends AnalyticsSender class_name AnalyticsSenderApp const HEARTBEAT_DELAY = 60 + var heartbeat_timer: Timer @@ -9,7 +10,9 @@ func start() -> void: super.start() analytics.send_event(AnalyticsEvents.app_open()) + start_heartbeat() + AfkManager.state_changed.connect(on_state_changed) # Send latest exit event var json: String = DataSaver.get_string("analytics", "app_exit") @@ -26,12 +29,20 @@ func start_heartbeat() -> void: func send_hearbeat() -> void: - var time_spend = Analytics.get_delta_sec_from_tick(0) + var time_spend = AfkManager.get_active_sec() analytics.send_event(AnalyticsEvents.heartbeat(time_spend)) +func on_state_changed(is_afk: bool) -> void: + if is_afk: + heartbeat_timer.stop() + return + + heartbeat_timer.start(HEARTBEAT_DELAY) + + func _exit_tree() -> void: # Save to send on open - var time_spend = Analytics.get_delta_sec_from_tick(0) + var time_spend = AfkManager.get_active_sec() var event = AnalyticsEvents.app_exit(time_spend) DataSaver.set_value("analytics", "app_exit", JSON.stringify(event)) diff --git a/app/scripts/ui/onboarding/onboarding.gd b/app/scripts/ui/onboarding/onboarding.gd index 68886ea..da30cfb 100644 --- a/app/scripts/ui/onboarding/onboarding.gd +++ b/app/scripts/ui/onboarding/onboarding.gd @@ -1,7 +1,7 @@ extends Control -const SECTION: String = "onboarding" -const KEY: String = "shown" +const SECTION = "onboarding" +const KEY = "shown" const INITIAL_DELAY = 1.0 const SHOWN = Color(1, 1, 1, 1)