Skip to content

Instantly share code, notes, and snippets.

@ossareh
Created February 19, 2026 22:32
Show Gist options
  • Select an option

  • Save ossareh/1f3fdd17afba845f625108842e8081d2 to your computer and use it in GitHub Desktop.

Select an option

Save ossareh/1f3fdd17afba845f625108842e8081d2 to your computer and use it in GitHub Desktop.
#!/usr/bin/env swift
import Foundation
// MARK: - API Fetch
struct ProviderData {
let id: String
let displayName: String
let latestSentiment: Double
let avgSentiment: Double
let latestPostCount: Int
let latestCommentCount: Int
}
func sentimentPercent(_ value: Double) -> String {
return "\(Int(round(value * 100)))%"
}
func trendArrow(latest: Double, avg: Double) -> String {
let threshold = 0.03
if latest > avg + threshold { return "▲" }
if latest < avg - threshold { return "▼" }
return "▶"
}
func shortLabel(_ id: String) -> String {
switch id {
case "claude": return "C"
case "chatgpt": return "O"
case "gemini": return "G"
default: return "?"
}
}
func fetchSentiment() -> [ProviderData]? {
let url = URL(string: "https://api.claudometer.app/current-sentiment")!
var request = URLRequest(url: url, timeoutInterval: 10)
request.httpMethod = "GET"
let semaphore = DispatchSemaphore(value: 0)
var result: [ProviderData]?
let task = URLSession.shared.dataTask(with: request) { data, response, error in
defer { semaphore.signal() }
guard let data = data, error == nil,
let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200,
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
return
}
let order = ["claude", "chatgpt", "gemini"]
var providers: [ProviderData] = []
for key in order {
guard let provider = json[key] as? [String: Any],
let displayName = provider["display_name"] as? String,
let latest = provider["latest_sentiment"] as? Double,
let avg = provider["avg_sentiment"] as? Double,
let postCount = provider["latest_post_count"] as? Int,
let commentCount = provider["latest_comment_count"] as? Int else {
continue
}
providers.append(ProviderData(
id: key,
displayName: displayName,
latestSentiment: latest,
avgSentiment: avg,
latestPostCount: postCount,
latestCommentCount: commentCount
))
}
result = providers
}
task.resume()
if semaphore.wait(timeout: .now() + 15) == .timedOut {
return nil
}
return result
}
// MARK: - Output
func printError(_ message: String) {
print("C:? O:? G:?")
print("---")
print("⚠ \(message) | color=red")
print("---")
print("Open Claudometer | href=https://claudometer.app")
print("Refresh | refresh=true")
}
guard let providers = fetchSentiment(), !providers.isEmpty else {
printError("Failed to fetch data")
exit(0)
}
// Menu bar: all three providers
let menuBarParts = providers.map { p in
"\(shortLabel(p.id)):\(sentimentPercent(p.avgSentiment))"
}
print(menuBarParts.joined(separator: " "))
// Dropdown
print("---")
for p in providers {
let arrow = trendArrow(latest: p.latestSentiment, avg: p.avgSentiment)
print("\(p.displayName) | size=14")
print(" Avg: \(sentimentPercent(p.avgSentiment)) \(arrow) (Latest: \(sentimentPercent(p.latestSentiment))) | font=Monaco size=12")
print(" \(p.latestPostCount) posts, \(p.latestCommentCount) comments | font=Monaco size=11 color=gray")
print("---")
}
print("Open Claudometer | href=https://claudometer.app")
print("Refresh | refresh=true")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment