-
Notifications
You must be signed in to change notification settings - Fork 0
feat: working matomo analytics (test environment) #41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import { trackEvent } from "./matomo"; | ||
import { getLocalOption } from "./localstorage"; | ||
|
||
// Track when a user clicks on an archived page to view it | ||
export async function onPageClicked(pageUrl: string): Promise<void> { | ||
console.log("onPageClicked called with URL:", pageUrl); | ||
await trackEvent("Packrat Chrome Extension: Archive", "ViewPage", pageUrl); | ||
} | ||
|
||
// Track when a torrent is created for sharing | ||
export async function onTorrentCreated(numPages: number): Promise<void> { | ||
console.log("onTorrentCreated called with pages:", numPages); | ||
await trackEvent( | ||
"Packrat Chrome Extension: Sharing", | ||
"TorrentCreated", | ||
`${numPages} pages`, | ||
); | ||
} | ||
|
||
// Track when a page is successfully archived | ||
export async function onPageArchived( | ||
pageUrl: string, | ||
pageSize?: number, | ||
): Promise<void> { | ||
console.log("onPageArchived called:", pageUrl, pageSize); | ||
await trackEvent( | ||
"Packrat Chrome Extension: Archive", | ||
"PageArchived", | ||
pageUrl, | ||
); | ||
|
||
// If page size is provided, track it separately | ||
if (pageSize !== undefined) { | ||
await trackEvent( | ||
"Packrat Chrome Extension: Archive", | ||
"PageSize", | ||
`${Math.round(pageSize / 1024)}KB`, | ||
); | ||
} | ||
} | ||
|
||
// Track settings changes | ||
export async function onSettingsChanged( | ||
settingName: string, | ||
value: string | boolean | number, | ||
): Promise<void> { | ||
console.log("onSettingsChanged:", settingName, value); | ||
await trackEvent( | ||
"Packrat Chrome Extension: Settings", | ||
settingName, | ||
String(value), | ||
); | ||
} | ||
|
||
// Track total archive size | ||
export async function trackArchiveSize(totalSizeBytes: number): Promise<void> { | ||
const sizeMB = Math.round(totalSizeBytes / (1024 * 1024)); | ||
console.log("trackArchiveSize:", sizeMB, "MB"); | ||
await trackEvent( | ||
"Packrat Chrome Extension: Archive", | ||
"TotalSize", | ||
`${sizeMB}MB`, | ||
); | ||
} | ||
|
||
// Track when archiving starts | ||
export async function onArchivingStarted(pageUrl: string): Promise<void> { | ||
console.log("onArchivingStarted:", pageUrl); | ||
await trackEvent("Packrat Chrome Extension: Archive", "Started", pageUrl); | ||
} | ||
|
||
// Track when archiving stops | ||
export async function onArchivingStopped( | ||
reason: string = "manual", | ||
): Promise<void> { | ||
console.log("onArchivingStopped:", reason); | ||
await trackEvent("Packrat Chrome Extension: Archive", "Stopped", reason); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
// matomo.ts - Matomo tracking with opt-out and persistent user ID | ||
|
||
import { getLocalOption, setLocalOption } from "./localstorage"; | ||
|
||
const MATOMO_URL = "https://analytics.vaporware.network/matomo.php"; | ||
const SITE_ID = "1"; | ||
const USER_ID_KEY = "matomoUserId"; | ||
|
||
/** | ||
* Ensure there is a persistent user ID in local storage. | ||
* If one doesn't exist, generate a random hex string, store it, and return it. | ||
*/ | ||
async function getOrCreateUserId(): Promise<string> { | ||
let stored = await getLocalOption(USER_ID_KEY); | ||
if (stored && typeof stored === "string") { | ||
return stored; | ||
} | ||
|
||
// Generate a 16-byte (128-bit) hex string | ||
const randomId = Array.from({ length: 16 }) | ||
.map(() => | ||
Math.floor(Math.random() * 256) | ||
.toString(16) | ||
.padStart(2, "0"), | ||
) | ||
.join(""); | ||
|
||
await setLocalOption(USER_ID_KEY, randomId); | ||
return randomId; | ||
} | ||
|
||
/** | ||
* Reads the "analyticsEnabled" key via getLocalOption. | ||
* We expect it to be stored as "1" or "0". | ||
* Returns true only if the stored value is exactly "1". | ||
*/ | ||
async function checkAnalyticsEnabled(): Promise<boolean> { | ||
const stored = await getLocalOption("analyticsEnabled"); | ||
return stored === "1"; | ||
} | ||
|
||
/** | ||
* Check if we're in the background/service worker context | ||
*/ | ||
function isBackgroundContext(): boolean { | ||
// Check if we have access to chrome.tabs (only available in background) | ||
return typeof chrome !== "undefined" && chrome.tabs !== undefined; | ||
} | ||
|
||
/** | ||
* Send a simple event to Matomo, but only if analyticsEnabled === "1". | ||
* Includes a persistent user ID (uid) in every request. | ||
*/ | ||
export async function trackEvent( | ||
category: string, | ||
action: string, | ||
name?: string, | ||
): Promise<void> { | ||
try { | ||
const isEnabled = await checkAnalyticsEnabled(); | ||
if (!isEnabled) { | ||
console.log("Matomo tracking is disabled; skipping event:", { | ||
category, | ||
action, | ||
name, | ||
}); | ||
return; | ||
} | ||
|
||
const userId = await getOrCreateUserId(); | ||
const params = new URLSearchParams({ | ||
// Required | ||
idsite: SITE_ID, | ||
rec: "1", | ||
|
||
// Event parameters | ||
e_c: category, | ||
e_a: action, | ||
e_n: name || "", | ||
|
||
// Basic info | ||
url: "chrome-extension://" + chrome.runtime.id, | ||
_id: Math.random().toString(16).substr(2, 16), | ||
rand: Date.now().toString(), | ||
apiv: "1", | ||
|
||
// Don't return image | ||
send_image: "0", | ||
|
||
// Persistent user ID | ||
uid: userId, | ||
}); | ||
|
||
const url = `${MATOMO_URL}?${params.toString()}`; | ||
console.log("Sending Matomo event:", { | ||
category, | ||
action, | ||
name, | ||
userId, | ||
url, | ||
}); | ||
|
||
// If we're in the background context, use fetch directly | ||
if (isBackgroundContext()) { | ||
await fetch(url, { | ||
method: "GET", | ||
mode: "no-cors", | ||
}); | ||
console.log("Matomo event sent directly from background"); | ||
} else { | ||
// Otherwise, try to send via message to background | ||
try { | ||
await chrome.runtime.sendMessage({ | ||
type: "matomoTrack", | ||
url: url, | ||
}); | ||
console.log("Matomo event sent via message"); | ||
} catch (error) { | ||
// Fallback to image beacon if messaging fails | ||
const img = new Image(); | ||
img.src = url; | ||
console.log("Matomo event sent via image beacon"); | ||
} | ||
} | ||
|
||
nikitalokhmachev-ai marked this conversation as resolved.
Show resolved
Hide resolved
|
||
console.log("Matomo event sent successfully"); | ||
} catch (error) { | ||
console.error("Matomo tracking error:", error); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.