From 54e80ca3418523b6380971b25f5b4029be5a81cc Mon Sep 17 00:00:00 2001
From: PianoMan0 <180116946+PianoMan0@users.noreply.github.com>
Date: Mon, 7 Jul 2025 20:57:33 -0400
Subject: [PATCH 1/5] Update flavor_text.rb
---
lib/flavor_text.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/flavor_text.rb b/lib/flavor_text.rb
index 1fd614d..61c80ca 100644
--- a/lib/flavor_text.rb
+++ b/lib/flavor_text.rb
@@ -220,7 +220,7 @@ class FlavorText
"#{%w[est created inited].sample} #{Time.now.to_i - Time.parse("Sun Feb 16 03:21:30 2025 -0500").to_i} seconds ago!".html_safe,
"uptime: #{Time.now.to_i - Rails.application.config.server_start_time.to_i} seconds!".html_safe,
"It takes a long time to build something good:
".html_safe,
- "If you're seeing this, the page is currently
.".html_safe,
+ "If you're seeing this, the page is currently
".html_safe,
"time is money!",
"in soviet russia, time tracks you!",
"tick tock!",
From 5cac80567bbfd9323c07c55b03b6268ce85cb1d6 Mon Sep 17 00:00:00 2001
From: Echo
Date: Mon, 21 Jul 2025 12:19:56 -0400
Subject: [PATCH 2/5] filtering by project dedupe
---
app/controllers/api/v1/stats_controller.rb | 26 ++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/app/controllers/api/v1/stats_controller.rb b/app/controllers/api/v1/stats_controller.rb
index c35482c..7d11b63 100644
--- a/app/controllers/api/v1/stats_controller.rb
+++ b/app/controllers/api/v1/stats_controller.rb
@@ -77,6 +77,16 @@ class Api::V1::StatsController < ApplicationController
summary = WakatimeService.new(**service_params).generate_summary
+ if params[:features]&.include?("projects") && params[:filter_by_project].present?
+ filter_by_project = params[:filter_by_project].split(",")
+ heartbeats = @user.heartbeats
+ .coding_only
+ .with_valid_timestamps
+ .where(time: start_date..end_date, project: filter_by_project)
+ unique_seconds = unique_heartbeat_seconds(heartbeats)
+ summary[:unique_total_seconds] = unique_seconds
+ end
+
trust_level = @user.trust_level
trust_level = "blue" if trust_level == "yellow"
trust_value = User.trust_levels[trust_level]
@@ -283,4 +293,20 @@ class Api::V1::StatsController < ApplicationController
data.sort_by { |project| -project[:total_seconds] }
end
+
+ def unique_heartbeat_seconds(heartbeats)
+ timestamps = heartbeats.order(:time).pluck(:time)
+ intervals = timestamps.each_cons(2).map { |a, b| [ a, b ] }
+ return 0 if intervals.empty?
+ merged = [ intervals.first ]
+ intervals[1..].each do |current|
+ last = merged.last
+ if current.first <= last.last
+ merged[-1] = [ last.first, [ last.last, current.last ].max ]
+ else
+ merged << current
+ end
+ end
+ merged.sum { |a, b| b - a }
+ end
end
From 810bf1de9a7f928c2bc5d8a3c738295d7b1b2398 Mon Sep 17 00:00:00 2001
From: Echo
Date: Mon, 21 Jul 2025 17:17:34 -0400
Subject: [PATCH 3/5] fix range issues
---
app/controllers/api/v1/stats_controller.rb | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/app/controllers/api/v1/stats_controller.rb b/app/controllers/api/v1/stats_controller.rb
index 7d11b63..e062454 100644
--- a/app/controllers/api/v1/stats_controller.rb
+++ b/app/controllers/api/v1/stats_controller.rb
@@ -296,17 +296,16 @@ class Api::V1::StatsController < ApplicationController
def unique_heartbeat_seconds(heartbeats)
timestamps = heartbeats.order(:time).pluck(:time)
- intervals = timestamps.each_cons(2).map { |a, b| [ a, b ] }
- return 0 if intervals.empty?
- merged = [ intervals.first ]
- intervals[1..].each do |current|
- last = merged.last
- if current.first <= last.last
- merged[-1] = [ last.first, [ last.last, current.last ].max ]
- else
- merged << current
+ return 0 if timestamps.empty?
+
+ total_seconds = 0
+ timestamps.each_cons(2) do |prev_time, curr_time|
+ gap = curr_time - prev_time
+ if gap > 0 && gap <= 2.minutes
+ total_seconds += gap
end
end
- merged.sum { |a, b| b - a }
+
+ total_seconds.to_i
end
end
From cee240c1fa4de68e61648762386f0aec4f82bba6 Mon Sep 17 00:00:00 2001
From: "Tom (Deployor)" <129990841+deployor@users.noreply.github.com>
Date: Tue, 22 Jul 2025 16:43:07 +0200
Subject: [PATCH 4/5] Update roblox-studio.md
The other plugin only works with Wakatime; it only connects to Wakatime, so it doesn't work. Also, the version I made during High Seas is much more advanced; it tracks literally anything a person does, not just scripting and playtesting.
---
docs/editors/roblox-studio.md | 76 ++++++++++++++++++++++++++++-------
1 file changed, 62 insertions(+), 14 deletions(-)
diff --git a/docs/editors/roblox-studio.md b/docs/editors/roblox-studio.md
index 0dc8440..74d4fc8 100644
--- a/docs/editors/roblox-studio.md
+++ b/docs/editors/roblox-studio.md
@@ -1,29 +1,77 @@
-# Roblox Studio Setup Guide
+# Set Up Hackatime with Roblox Studio

-Follow these steps to start tracking your game development in Roblox Studio with Hackatime.
+This guide will walk you through setting up **Hackatime** to automatically track your game development time in **Roblox Studio**.
-## Step 1: Log into Hackatime
+---
-Make sure you have a [Hackatime account](https://hackatime.hackclub.com) and are logged in.
+## Step 1: Log In to Your Hackatime Account
-## Step 2: Run the Setup Script
+First, make sure you have a **Hackatime account** and are logged in. If you don't have an account, you can create one at [hackatime.hackclub.com](https://hackatime.hackclub.com).
-Visit the [setup page](https://hackatime.hackclub.com/my/wakatime_setup) to automatically configure your API key and endpoint. This ensures everything works perfectly with Hackatime.
+---
-## Step 3: Install Roblox Studio Plugin
+## Step 2: Install the Hackatime Roblox Studio Plugin
-Follow the detailed plugin installation instructions on the [WakaTime Roblox Studio page](https://wakatime.com/roblox-studio).
+Next, you'll need to install the Hackatime plugin directly within Roblox Studio:
-The WakaTime plugin will automatically use your Hackatime configuration after running the setup script.
+1. Open **Roblox Studio**.
+2. Navigate to the **Toolbox**.
+3. In the Toolbox search bar, select **"Plugins"** from the dropdown filter.
+4. Search for **"HackaTime Roblox"**.
+5. Install the plugin published by **"ThisWhity"**.
+
+ 
+ *Filter the Toolbox by "Plugins"*
+
+ 
+ *Install the "HackaTime Roblox" plugin by ThisWhity*
+
+---
+
+## Step 3: Configure the Plugin with Your API Key
+
+Now, you'll connect the plugin to your Hackatime account using your unique API key:
+
+1. Get your API key by visiting [hackatime.hackclub.com/my/wakatime_setup](https://hackatime.hackclub.com/my/wakatime_setup). It will look something like this: `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`.
+
+ 
+ *Your API key from the Hackatime website*
+
+2. In Roblox Studio, open the **Hackatime plugin**. You'll usually find it under the "Plugins" tab in the Ribbon bar.
+
+ 
+ *Open the Plugin*
+
+4. Paste your API key into the API key box. And hit "Save API Key"
+
+---
## Troubleshooting
-- **Not seeing your time?** Make sure you completed the [setup page](https://hackatime.hackclub.com/my/wakatime_setup) first
-- **Plugin not working?** Try restarting Roblox Studio after installation
-- **Still stuck?** Ask for help in [Hack Club Slack](https://hackclub.slack.com) (#hackatime-v2 channel)
+### ERR\_NETWORK: Plugin Cannot Connect to Hackatime
-## Next Steps
+If you see an **ERR\_NETWORK** message, it means the plugin can't connect to Hackatime. This is likely due to you not allowing HTTP request from the plugin:
-Once configured, your activity time will automatically appear on your [Hackatime dashboard](https://hackatime.hackclub.com). Happy game developing!
+1. Open the "Manage Plugins".
+2. Hit the edit icon.
+3. Ensure that **"hackatime.hackclub.com"** is enabled.
+
+ 
+ *Open Plugin Managment*
+
+ 
+ *Allow HTTP requests*
+
+### Still Stuck?
+
+If you're still experiencing issues, don't hesitate to ask for help in the **#hackatime-v2 channel** on the [Hack Club Slack](https://hackclub.slack.com).
+
+---
+
+## What's Next?
+
+Once the plugin is successfully configured, your Roblox Studio activity time will automatically start appearing on your [Hackatime dashboard](https://hackatime.hackclub.com).
+
+Happy game developing!
From eb60d1d82ccdd1b3e60c714a2bb538401d50c9a2 Mon Sep 17 00:00:00 2001
From: Echo
Date: Tue, 22 Jul 2025 20:55:26 -0400
Subject: [PATCH 5/5] =?UTF-8?q?Revert=20"fix:=20use=20WakatimeService=20wi?=
=?UTF-8?q?thout=20filters=20and=20consistent=20time=20ranges=20f=E2=80=A6?=
=?UTF-8?q?"?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/controllers/api/v1/stats_controller.rb | 21 ++++++++++++++++++---
lib/wakatime_service.rb | 19 +++----------------
2 files changed, 21 insertions(+), 19 deletions(-)
diff --git a/app/controllers/api/v1/stats_controller.rb b/app/controllers/api/v1/stats_controller.rb
index fd36846..6b9d5f8 100644
--- a/app/controllers/api/v1/stats_controller.rb
+++ b/app/controllers/api/v1/stats_controller.rb
@@ -61,10 +61,25 @@ class Api::V1::StatsController < ApplicationController
service_params[:scope] = scope if scope.present?
if params[:total_seconds] == "true"
- service_params[:boundary_aware] = params[:boundary_aware] == "true"
+ query = @user.heartbeats
+ .coding_only
+ .with_valid_timestamps
+ .where(time: start_date..end_date)
- summary = WakatimeService.new(**service_params).generate_summary
- return render json: { total_seconds: summary[:total_seconds] }
+ if params[:filter_by_project].present?
+ filter_by_project = params[:filter_by_project].split(",")
+ query = query.where(project: filter_by_project)
+ end
+
+ # do the boundary thingie if requested
+ use_boundary_aware = params[:boundary_aware] == "true"
+ total_seconds = if use_boundary_aware
+ Heartbeat.duration_seconds_boundary_aware(query, start_date.to_f, end_date.to_f) || 0
+ else
+ query.duration_seconds || 0
+ end
+
+ return render json: { total_seconds: total_seconds }
end
summary = WakatimeService.new(**service_params).generate_summary
diff --git a/lib/wakatime_service.rb b/lib/wakatime_service.rb
index 928d178..908de9b 100644
--- a/lib/wakatime_service.rb
+++ b/lib/wakatime_service.rb
@@ -1,24 +1,18 @@
include ApplicationHelper
class WakatimeService
- def initialize(user: nil, specific_filters: [], allow_cache: true, limit: 10, start_date: nil, end_date: nil, scope: nil, boundary_aware: false)
+ def initialize(user: nil, specific_filters: [], allow_cache: true, limit: 10, start_date: nil, end_date: nil, scope: nil)
@scope = scope || Heartbeat.all
@user = user
- @boundary_aware = boundary_aware
@start_date = convert_to_unix_timestamp(start_date)
@end_date = convert_to_unix_timestamp(end_date)
- # apply with_valid_timestamps filter if no custom scope provided-- this is copied from query in stats_controller
- if scope.nil?
- @scope = @scope.with_valid_timestamps
- end
-
# Default to 1 year ago if no start_date provided or if no data exists
@start_date = @start_date || @scope.minimum(:time) || 1.year.ago.to_i
@end_date = @end_date || @scope.maximum(:time) || Time.current.to_i
- @scope = @scope.where(time: @start_date..@end_date)
+ @scope = @scope.where("time >= ? AND time < ?", @start_date, @end_date)
@limit = limit
@limit = nil if @limit&.zero?
@@ -47,14 +41,7 @@ class WakatimeService
summary[:range] = "all_time"
summary[:human_readable_range] = "All Time"
- @total_seconds = if @boundary_aware
- result = Heartbeat.duration_seconds_boundary_aware(@scope, @start_date, @end_date) || 0
- result
- else
- result = @scope.duration_seconds || 0
- result
- end
-
+ @total_seconds = @scope.duration_seconds || 0
summary[:total_seconds] = @total_seconds
@total_days = (@end_time - @start_time) / 86400