Initial commit
This commit is contained in:
48
extension/background.js
Normal file
48
extension/background.js
Normal file
@@ -0,0 +1,48 @@
|
||||
// Background script for service worker
|
||||
// Currently minimal, can be extended for additional functionality
|
||||
|
||||
chrome.runtime.onInstalled.addListener(() => {
|
||||
console.log('Facebook Post Tracker extension installed');
|
||||
|
||||
// Set default profile if not set
|
||||
chrome.storage.sync.get(['profileNumber'], (result) => {
|
||||
if (!result.profileNumber) {
|
||||
chrome.storage.sync.set({ profileNumber: 1 });
|
||||
}
|
||||
});
|
||||
|
||||
// Create context menu for manual post parsing
|
||||
chrome.contextMenus.create({
|
||||
id: 'fb-tracker-reparse',
|
||||
title: 'FB Tracker: Post neu parsen',
|
||||
contexts: ['all'],
|
||||
documentUrlPatterns: ['*://*.facebook.com/*']
|
||||
});
|
||||
});
|
||||
|
||||
chrome.contextMenus.onClicked.addListener((info, tab) => {
|
||||
if (info.menuItemId === 'fb-tracker-reparse') {
|
||||
chrome.tabs.sendMessage(tab.id, {
|
||||
type: 'reparsePost',
|
||||
x: info.pageX || 0,
|
||||
y: info.pageY || 0
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
if (message && message.type === 'captureScreenshot') {
|
||||
const windowId = sender && sender.tab ? sender.tab.windowId : chrome.windows.WINDOW_ID_CURRENT;
|
||||
|
||||
chrome.tabs.captureVisibleTab(windowId, { format: 'jpeg', quality: 80 }, (imageData) => {
|
||||
if (chrome.runtime.lastError) {
|
||||
sendResponse({ error: chrome.runtime.lastError.message });
|
||||
return;
|
||||
}
|
||||
|
||||
sendResponse({ imageData });
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
3
extension/config.js
Normal file
3
extension/config.js
Normal file
@@ -0,0 +1,3 @@
|
||||
// API Configuration
|
||||
// Passe die URL an deine Deployment-Domain an
|
||||
const API_BASE_URL = 'https://fb.srv.medeba-media.de';
|
||||
64
extension/content.css
Normal file
64
extension/content.css
Normal file
@@ -0,0 +1,64 @@
|
||||
.fb-tracker-ui {
|
||||
margin: 8px 0;
|
||||
padding: 6px 12px;
|
||||
background: #f0f2f5;
|
||||
border-radius: 6px;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.fb-tracker-status,
|
||||
.fb-tracker-add {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.fb-tracker-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.fb-tracker-text {
|
||||
flex: 1;
|
||||
font-size: 13px;
|
||||
color: #050505;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.fb-tracker-status.complete .fb-tracker-text {
|
||||
color: #059669;
|
||||
}
|
||||
|
||||
.fb-tracker-count {
|
||||
padding: 4px 6px;
|
||||
border: 1px solid #ccd0d5;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.fb-tracker-add-btn,
|
||||
.fb-tracker-check-btn {
|
||||
padding: 4px 12px;
|
||||
background: #1877f2;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.fb-tracker-add-btn:hover,
|
||||
.fb-tracker-check-btn:hover {
|
||||
background: #166fe5;
|
||||
}
|
||||
|
||||
.fb-tracker-check-btn {
|
||||
background: #059669;
|
||||
}
|
||||
|
||||
.fb-tracker-check-btn:hover {
|
||||
background: #047857;
|
||||
}
|
||||
3608
extension/content.js
Normal file
3608
extension/content.js
Normal file
File diff suppressed because it is too large
Load Diff
BIN
extension/icons/icon-128.png
Normal file
BIN
extension/icons/icon-128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
extension/icons/icon-16.png
Normal file
BIN
extension/icons/icon-16.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 209 B |
BIN
extension/icons/icon-32.png
Normal file
BIN
extension/icons/icon-32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 340 B |
BIN
extension/icons/icon-48.png
Normal file
BIN
extension/icons/icon-48.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 465 B |
BIN
extension/icons/icon-512.png
Normal file
BIN
extension/icons/icon-512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.8 KiB |
4
extension/icons/icon.svg
Normal file
4
extension/icons/icon.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
|
||||
<rect width="128" height="128" rx="20" fill="#1877f2"/>
|
||||
<text x="64" y="90" font-size="80" text-anchor="middle" fill="white">📋</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 210 B |
47
extension/manifest.json
Normal file
47
extension/manifest.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "Facebook Post Tracker",
|
||||
"version": "1.1.0",
|
||||
"description": "Track Facebook posts across multiple profiles",
|
||||
"permissions": [
|
||||
"storage",
|
||||
"activeTab",
|
||||
"tabs",
|
||||
"contextMenus"
|
||||
],
|
||||
"host_permissions": [
|
||||
"<all_urls>",
|
||||
"https://www.facebook.com/*",
|
||||
"https://facebook.com/*",
|
||||
"http://localhost:3001/*",
|
||||
"https://fb.srv.medeba-media.de/*"
|
||||
],
|
||||
"icons": {
|
||||
"16": "icons/icon-16.png",
|
||||
"32": "icons/icon-32.png",
|
||||
"48": "icons/icon-48.png",
|
||||
"128": "icons/icon-128.png"
|
||||
},
|
||||
"action": {
|
||||
"default_popup": "popup.html",
|
||||
"default_icon": {
|
||||
"16": "icons/icon-16.png",
|
||||
"32": "icons/icon-32.png",
|
||||
"48": "icons/icon-48.png"
|
||||
}
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": [
|
||||
"https://www.facebook.com/*",
|
||||
"https://facebook.com/*"
|
||||
],
|
||||
"js": ["config.js", "content.js"],
|
||||
"css": ["content.css"],
|
||||
"run_at": "document_idle"
|
||||
}
|
||||
],
|
||||
"background": {
|
||||
"service_worker": "background.js"
|
||||
}
|
||||
}
|
||||
119
extension/popup.html
Normal file
119
extension/popup.html
Normal file
@@ -0,0 +1,119 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Facebook Post Tracker</title>
|
||||
<style>
|
||||
body {
|
||||
width: 300px;
|
||||
padding: 20px;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 18px;
|
||||
margin: 0 0 16px 0;
|
||||
color: #050505;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
color: #050505;
|
||||
}
|
||||
|
||||
select {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border: 1px solid #ccd0d5;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.status {
|
||||
padding: 12px;
|
||||
background: #f0f2f5;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
color: #65676b;
|
||||
}
|
||||
|
||||
.status.saved {
|
||||
background: #d1fae5;
|
||||
color: #065f46;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
button {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
background: #1877f2;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #166fe5;
|
||||
}
|
||||
|
||||
button.secondary {
|
||||
background: #e4e6eb;
|
||||
color: #050505;
|
||||
}
|
||||
|
||||
button.secondary:hover {
|
||||
background: #d8dadf;
|
||||
}
|
||||
|
||||
.info {
|
||||
font-size: 12px;
|
||||
color: #65676b;
|
||||
margin-top: 8px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>📋 Facebook Post Tracker</h1>
|
||||
|
||||
<div class="section">
|
||||
<label for="profileSelect">Aktuelles Profil:</label>
|
||||
<select id="profileSelect">
|
||||
<option value="1">Profil 1</option>
|
||||
<option value="2">Profil 2</option>
|
||||
<option value="3">Profil 3</option>
|
||||
<option value="4">Profil 4</option>
|
||||
<option value="5">Profil 5</option>
|
||||
</select>
|
||||
<div class="info">Wähle das Browser-Profil aus, mit dem du aktuell arbeitest.</div>
|
||||
</div>
|
||||
|
||||
<div id="status" class="status"></div>
|
||||
|
||||
<div class="btn-group">
|
||||
<button id="saveBtn">Speichern</button>
|
||||
<button id="webInterfaceBtn" class="secondary">Web-Interface</button>
|
||||
</div>
|
||||
|
||||
<script src="config.js"></script>
|
||||
<script src="popup.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
99
extension/popup.js
Normal file
99
extension/popup.js
Normal file
@@ -0,0 +1,99 @@
|
||||
const profileSelect = document.getElementById('profileSelect');
|
||||
const statusEl = document.getElementById('status');
|
||||
|
||||
function apiFetch(url, options = {}) {
|
||||
const config = {
|
||||
...options,
|
||||
credentials: 'include'
|
||||
};
|
||||
|
||||
if (options && options.headers) {
|
||||
config.headers = { ...options.headers };
|
||||
}
|
||||
|
||||
return fetch(url, config);
|
||||
}
|
||||
|
||||
async function fetchProfileState() {
|
||||
try {
|
||||
const response = await apiFetch(`${API_BASE_URL}/api/profile-state`);
|
||||
if (!response.ok) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
if (data && typeof data.profile_number !== 'undefined') {
|
||||
const parsed = parseInt(data.profile_number, 10);
|
||||
if (!Number.isNaN(parsed)) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.warn('Profile state fetch failed:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function updateProfileState(profileNumber) {
|
||||
try {
|
||||
const response = await apiFetch(`${API_BASE_URL}/api/profile-state`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ profile_number: profileNumber })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to update profile state:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function updateStatus(message, saved = false) {
|
||||
statusEl.textContent = message;
|
||||
statusEl.className = saved ? 'status saved' : 'status';
|
||||
}
|
||||
|
||||
async function initProfileSelect() {
|
||||
const backendProfile = await fetchProfileState();
|
||||
if (backendProfile) {
|
||||
profileSelect.value = String(backendProfile);
|
||||
chrome.storage.sync.set({ profileNumber: backendProfile });
|
||||
updateStatus(`Profil ${backendProfile} ausgewählt`);
|
||||
return;
|
||||
}
|
||||
|
||||
chrome.storage.sync.get(['profileNumber'], (result) => {
|
||||
const profileNumber = result.profileNumber || 1;
|
||||
profileSelect.value = String(profileNumber);
|
||||
updateStatus(`Profil ${profileNumber} ausgewählt (lokal)`);
|
||||
});
|
||||
}
|
||||
|
||||
initProfileSelect();
|
||||
|
||||
function reloadFacebookTabs() {
|
||||
chrome.tabs.query({ url: ['https://www.facebook.com/*', 'https://facebook.com/*'] }, (tabs) => {
|
||||
tabs.forEach(tab => {
|
||||
chrome.tabs.reload(tab.id);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById('saveBtn').addEventListener('click', async () => {
|
||||
const profileNumber = parseInt(profileSelect.value, 10);
|
||||
|
||||
chrome.storage.sync.set({ profileNumber }, async () => {
|
||||
updateStatus(`Profil ${profileNumber} gespeichert!`, true);
|
||||
await updateProfileState(profileNumber);
|
||||
reloadFacebookTabs();
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('webInterfaceBtn').addEventListener('click', () => {
|
||||
chrome.tabs.create({ url: API_BASE_URL });
|
||||
});
|
||||
Reference in New Issue
Block a user