Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,19 @@
"lint-staged": {
"*.{js,jsx}": "eslint --cache --fix",
"*.{js,css,md}": "prettier --write"
},
"pkg": {
"assets": [
"./node_modules/**/*.node",
"./packages/**/.babel/*.env",
"./packages/server/.babel/static/",
"./packages/desktop/node_modules/active-win/main"
],
"targets": [
"node14-win-x64",
"node14-linux-x64",
"node14-macos"
],
"outputPath": "build"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 26 additions & 0 deletions packages/Activity-Tracker-browser-extension/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "Browser Activity Tracker",
"description": "Shows you your digital time based on your browser activity",
"version": "1.0.0",
"manifest_version": 3,

"action": {
"default_icon": {
"16": "images/icon16.png",
"32": "images/icon32.png",
"48": "images/icon48.png",
"128": "images/icon128.png"
},
"default_popup": "popup.html"
},

"background": {
"service_worker": "src/background.js",
"type": "module"
},

"author": "",
"homepage_url": "https://github.com/OpenLake/Activity-Tracker#readme",

"permissions": ["tabs", "activeTab", "storage"]
}
19 changes: 19 additions & 0 deletions packages/Activity-Tracker-browser-extension/popup.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="style/popup.css" />
<title>Document</title>
</head>
<body>
<script src="src/background.js"></script>
<h1 class="textBody">Activity</h1>
<div>
<a class="textBody" id="activityTitle">Title: Loading ...</a>
</div>
<a class="textBody" href="" id="activityUrl">URL: Loading ...</a>
<div><img id="activityFavicon" src="" alt="Favicon" /></div>
</body>
</html>
138 changes: 138 additions & 0 deletions packages/Activity-Tracker-browser-extension/src/background.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
let title = null;
let url = null;
let favicon = null;
class ActiveBrowserWatcher {
/**
* @param {number} interval Polling interval
* @param {(activity) => void} changeCallback
*/
constructor(interval = 1000, changeCallback) {
this.startTime = null;
this.title = null; //Title
this.url = null;
this.favicon = null;
this.changeCallback = changeCallback;
this.interval = interval;
}

/**
* Storing the start time of the active window
* Collecting data of the window which will be active
*/
storeTime() {
const endTime = Date.now();
const startTime = this.startTime;

const title = this.title;
const url = this.url;
const favicon = this.favicon;

const data = {
title,
url,
favicon,
startTime,
endTime,
};

fetch('http://localhost:32768/api/browseractivities', {
method: 'POST',
body: data,
})
.then(console.log('done store'))
.catch(err => {
console.log(err);
});
this.changeCallback(data);
console.log(data);
}

/**
* Checks the active window is specific time interval
* and whenever the active window changes stores the time difference by calling {@link ActiveWindowWatcher.storeTime} function
*/
tracker() {
setInterval(() => {
let queryOptions = { active: true, currentWindow: true }; // to get current active tab from the current window
// eslint-disable-next-line no-undef
chrome.tabs.query(queryOptions, function currentTab(tabs) {
let currentTab = tabs[0]; // take the object from the returned promise
let currentTitle = currentTab.title; // take object title
let currentUrl = currentTab.url; // take object URL
let currentFavIcons = currentTab.favIconUrl;
title = currentTitle;
url = currentUrl;
favicon = currentFavIcons;

// Title
const activityTitle = document.getElementById('activityTitle');
const activityTitleUrl = document.getElementById('activityTitle');
activityTitle.innerHTML = 'Title: ' + currentTitle; //format it in html
activityTitleUrl.setAttribute('href', currentUrl);
console.log(activityTitle);

// URl
const activityUrl = document.getElementById('activityUrl');
const activityLink = document.getElementById('activityUrl');
activityUrl.innerHTML = 'URL: ' + currentUrl; //format it in html
activityLink.setAttribute('href', currentUrl);

// Favicon
const activityFavicon = document.getElementById('activityFavicon');
activityFavicon.setAttribute('src', currentFavIcons); //format Favicon in html
});

if (title === undefined) {
this.title = null;
this.url = null;
this.favicon = null;
return;
}

if (!this.title) {
this.startTime = Date.now();
this.title = title;
this.url = url;
this.favicon = favicon;
}

//If the active window is changed store the used time data.
if (title !== this.title) {
this.storeTime();
this.title = null;
this.url = null;
this.favicon = null;
}
console.log(title, url, favicon, this.startTime);
}, this.interval);
}

initialize() {
this.tracker();
}
}

// const activityTracker = new ActiveBrowserWatcher(1000);

// const activityTracker = new ActiveBrowserWatcher(1000, activity => {
// saveActivities(activity);
// });

// fetch('http://localhost:32768/api/browseractivities',{
// method: 'POST',
// body: activityTracker
// )
// });

const activityTracker = new ActiveBrowserWatcher(1000, activity => {
fetch('http://localhost:32768/api/browseractivities', {
method: 'POST',
body: activity,
})
.then(console.log('done'))
.catch(err => {
console.log(err);
});
});

activityTracker.initialize();
20 changes: 20 additions & 0 deletions packages/Activity-Tracker-browser-extension/style/popup.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
body {
width: 300px;
height: 300px;
text-align: center;
background-color: rgb(18, 31, 48);
}

.textBody {
color: aliceblue;
}

#activityFavicon {
margin-top: 50px;
margin-bottom: 50px;
}

img {
width: 20%;
height: auto;
}
2 changes: 2 additions & 0 deletions packages/server/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import root from './routes/root.routes.js';
import user from './routes/user.routes.js';
import activity from './routes/activity.routes.js';
import app_usage from './routes/app.routes.js';
import browser_activity from './routes/browseractivity.routes.js';

const app = express();

Expand Down Expand Up @@ -41,6 +42,7 @@ app.use('/api/', root);
app.use('/api/users', user);
app.use('/api/activities', activity);
app.use('/api/apps', app_usage);
app.use('/api/browseractivities', browser_activity);

app.listen(port, hostname, function () {
console.log(`Nodejs server running at http://${hostname}:${port}/`);
Expand Down
6 changes: 6 additions & 0 deletions packages/server/controllers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import { useLocal } from '../config.js';

import * as json_activity_controller from './json/activity.controller.js';
import * as json_app_controller from './json/app.controller.js';
import * as json_browser_activity_controller from './json/browseractivity.controller.js';

import * as mongo_activity_controller from './mongo/activity.controller.js';
import * as mongo_app_controller from './mongo/app.controller.js';
import * as mongo_user_controller from './mongo/user.controller.js';
import * as mongo_browser_activity_controller from './mongo/browserTracker.controller.js';

export const activity_controller = useLocal
? json_activity_controller
Expand All @@ -16,3 +18,7 @@ export const app_controller = useLocal
: mongo_app_controller;

export const user_controller = mongo_user_controller;

export const browser_activity_controller = useLocal
? json_browser_activity_controller
: mongo_browser_activity_controller;
87 changes: 87 additions & 0 deletions packages/server/controllers/json/browseractivity.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import fs from 'fs';
import path from 'path';
import envPaths from 'env-paths';
import { extract } from './utils.js';

const dirname = envPaths('ActivityTracker').data;
const browserActivityDir = path.join(dirname, '/browser-activity');
fs.mkdirSync(browserActivityDir, { recursive: true });

/**
* Get date string of format YYYY-MM-DD
* @param {Date} date
*/
const getISODateString = date => date.toISOString().slice(0, 10);

/**
* @param {Date} date
*/
function getFilePath(date) {
const filename = `${getISODateString(date)}.json`;
return path.join(browserActivityDir, filename);
}

/**
* Get Activities for a given date
* @param {Date} date
* @returns {Object[]}
*/
function getActivities(date) {
let data = [];
try {
data = JSON.parse(fs.readFileSync(getFilePath(date), 'utf-8'));
} catch (error) {
if (error.code === 'ENOENT') {
data = [];
} else {
throw error;
}
}
return data;
}

const getDataFromJson = (start, end) => {
start = new Date(getISODateString(new Date(start)));
end = new Date(getISODateString(new Date(end)));

let result = [];
let date = start;

while (date <= end) {
try {
result.push(...JSON.parse(fs.readFileSync(getFilePath(date), 'utf-8')));
} catch (error) {
console.log(`No data for ${getISODateString(date)}`);
}
date.setDate(date.getDate() + 1);
}
return result;
};

export const activity_create = (req, res) => {
const packet = extract(req.body, [
'title',
'url',
'favicon',
'startTime',
'endTime',
]);
const filepath = getFilePath(new Date(packet.endTime));
const data = getActivities(new Date(packet.endTime));
data.push(packet);
fs.writeFileSync(filepath, JSON.stringify(data, null, 4));

res.send('Activity created successfully');
};

export const all_activities = (req, res) => {
const today = new Date();
const yesterday = new Date(today);
yesterday.setDate(yesterday.getDate() - 1);

const after = req.query.after ?? yesterday.toISOString();
const before = req.query.before ?? today.toISOString();

const activities = getDataFromJson(after, before);
res.json(activities);
};
3 changes: 3 additions & 0 deletions packages/server/controllers/json/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,6 @@ export const groupBy = (array, key) => {
return result;
}, {}); // empty object is the initial value for result object
};

export const extract = (obj, keys) =>
Object.fromEntries(keys.map(k => [k, obj[k]]));
36 changes: 36 additions & 0 deletions packages/server/controllers/mongo/browserTracker.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import browserActivity from '../../models/browserTracker.model.js';

export const activity_create = (req, res, next) => {
const { title, url, favicon, startTime, endTime } = req.body;
const browseractivity = new browserActivity({
title,
url,
favicon,
startTime,
endTime,
});
browseractivity.save(err => {
if (err) return next(err);
});

res.send('Activity created succecssfully');
};

export const all_activities = (req, res, next) => {
const today = new Date();
const yesterday = new Date(today);
yesterday.setDate(yesterday.getDate() - 1);

const after = req.query.after ?? yesterday.toISOString();
const before = req.query.before ?? today.toISOString();

browserActivity.find(
{
startTime: { $gte: after, $lt: before },
},
(err, activity) => {
if (err) return next(err);
res.json(activity);
},
);
};
Loading