Exempt repeat AI comments on same post
This commit is contained in:
@@ -1171,11 +1171,36 @@ function getAIAutoCommentOldestEventSince(profileNumber, sinceIso) {
|
|||||||
`).get(normalizedProfileNumber, sinceIso) || null;
|
`).get(normalizedProfileNumber, sinceIso) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildAIAutoCommentRateLimitStatus(profileNumber, settings = getAIAutoCommentRateLimitSettings(), now = new Date()) {
|
function sanitizeAIAutoCommentPostKey(value) {
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const trimmed = truncateString(value.trim(), 1000);
|
||||||
|
return trimmed || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasAIAutoCommentEventForPost(profileNumber, postKey) {
|
||||||
|
const normalizedProfileNumber = sanitizeProfileNumber(profileNumber);
|
||||||
|
const normalizedPostKey = sanitizeAIAutoCommentPostKey(postKey);
|
||||||
|
if (!normalizedProfileNumber || !normalizedPostKey) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const row = db.prepare(`
|
||||||
|
SELECT 1
|
||||||
|
FROM ai_auto_comment_rate_limit_events
|
||||||
|
WHERE profile_number = ?
|
||||||
|
AND post_key = ?
|
||||||
|
LIMIT 1
|
||||||
|
`).get(normalizedProfileNumber, normalizedPostKey);
|
||||||
|
return Boolean(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildAIAutoCommentRateLimitStatus(profileNumber, settings = getAIAutoCommentRateLimitSettings(), now = new Date(), options = {}) {
|
||||||
const normalizedProfileNumber = sanitizeProfileNumber(profileNumber);
|
const normalizedProfileNumber = sanitizeProfileNumber(profileNumber);
|
||||||
if (!normalizedProfileNumber) {
|
if (!normalizedProfileNumber) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
const normalizedPostKey = sanitizeAIAutoCommentPostKey(options.postKey);
|
||||||
|
|
||||||
clearExpiredAIAutoCommentProfileCooldown(normalizedProfileNumber, now);
|
clearExpiredAIAutoCommentProfileCooldown(normalizedProfileNumber, now);
|
||||||
|
|
||||||
@@ -1192,12 +1217,13 @@ function buildAIAutoCommentRateLimitStatus(profileNumber, settings = getAIAutoCo
|
|||||||
const lastEvent = getAIAutoCommentLatestEvent(normalizedProfileNumber);
|
const lastEvent = getAIAutoCommentLatestEvent(normalizedProfileNumber);
|
||||||
const profileState = getAIAutoCommentProfileState(normalizedProfileNumber);
|
const profileState = getAIAutoCommentProfileState(normalizedProfileNumber);
|
||||||
const activeHours = getAIAutoCommentActiveHoursState(settings, now);
|
const activeHours = getAIAutoCommentActiveHoursState(settings, now);
|
||||||
|
const samePostExempt = Boolean(normalizedPostKey && hasAIAutoCommentEventForPost(normalizedProfileNumber, normalizedPostKey));
|
||||||
|
|
||||||
let blocked = false;
|
let blocked = false;
|
||||||
let blockedReason = null;
|
let blockedReason = null;
|
||||||
let blockedUntil = null;
|
let blockedUntil = null;
|
||||||
|
|
||||||
if (settings.enabled) {
|
if (settings.enabled && !samePostExempt) {
|
||||||
const blockingCandidates = [];
|
const blockingCandidates = [];
|
||||||
const addBlockingCandidate = (reason, untilIso) => {
|
const addBlockingCandidate = (reason, untilIso) => {
|
||||||
if (!untilIso) {
|
if (!untilIso) {
|
||||||
@@ -1282,6 +1308,7 @@ function buildAIAutoCommentRateLimitStatus(profileNumber, settings = getAIAutoCo
|
|||||||
blocked,
|
blocked,
|
||||||
blocked_reason: blockedReason,
|
blocked_reason: blockedReason,
|
||||||
blocked_until: blockedUntil,
|
blocked_until: blockedUntil,
|
||||||
|
same_post_exempt: samePostExempt,
|
||||||
cooldown_until: profileState?.cooldown_until || null,
|
cooldown_until: profileState?.cooldown_until || null,
|
||||||
cooldown_reason: profileState?.cooldown_reason || null,
|
cooldown_reason: profileState?.cooldown_reason || null,
|
||||||
usage: {
|
usage: {
|
||||||
@@ -1313,15 +1340,18 @@ function listAIAutoCommentRateLimitStatuses(settings = getAIAutoCommentRateLimit
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkAIAutoCommentActionAvailability(profileNumber, settings = getAIAutoCommentRateLimitSettings()) {
|
function checkAIAutoCommentActionAvailability(profileNumber, settings = getAIAutoCommentRateLimitSettings(), postKey = null) {
|
||||||
const normalizedProfileNumber = sanitizeProfileNumber(profileNumber);
|
const normalizedProfileNumber = sanitizeProfileNumber(profileNumber);
|
||||||
if (!normalizedProfileNumber) {
|
if (!normalizedProfileNumber) {
|
||||||
return { ok: false, status: null };
|
return { ok: false, status: null };
|
||||||
}
|
}
|
||||||
|
const normalizedPostKey = sanitizeAIAutoCommentPostKey(postKey);
|
||||||
|
|
||||||
const check = db.transaction(() => {
|
const check = db.transaction(() => {
|
||||||
purgeOldAIAutoCommentRateLimitEvents();
|
purgeOldAIAutoCommentRateLimitEvents();
|
||||||
const currentStatus = buildAIAutoCommentRateLimitStatus(normalizedProfileNumber, settings, new Date());
|
const currentStatus = buildAIAutoCommentRateLimitStatus(normalizedProfileNumber, settings, new Date(), {
|
||||||
|
postKey: normalizedPostKey
|
||||||
|
});
|
||||||
if (!currentStatus || currentStatus.blocked) {
|
if (!currentStatus || currentStatus.blocked) {
|
||||||
return {
|
return {
|
||||||
ok: false,
|
ok: false,
|
||||||
@@ -1345,11 +1375,12 @@ function checkAIAutoCommentActionAvailability(profileNumber, settings = getAIAut
|
|||||||
return check();
|
return check();
|
||||||
}
|
}
|
||||||
|
|
||||||
function recordAIAutoCommentAction(profileNumber, settings = getAIAutoCommentRateLimitSettings(), occurredAt = new Date()) {
|
function recordAIAutoCommentAction(profileNumber, settings = getAIAutoCommentRateLimitSettings(), occurredAt = new Date(), postKey = null) {
|
||||||
const normalizedProfileNumber = sanitizeProfileNumber(profileNumber);
|
const normalizedProfileNumber = sanitizeProfileNumber(profileNumber);
|
||||||
if (!normalizedProfileNumber) {
|
if (!normalizedProfileNumber) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
const normalizedPostKey = sanitizeAIAutoCommentPostKey(postKey);
|
||||||
|
|
||||||
const eventDate = occurredAt instanceof Date && !Number.isNaN(occurredAt.getTime())
|
const eventDate = occurredAt instanceof Date && !Number.isNaN(occurredAt.getTime())
|
||||||
? occurredAt
|
? occurredAt
|
||||||
@@ -1357,12 +1388,19 @@ function recordAIAutoCommentAction(profileNumber, settings = getAIAutoCommentRat
|
|||||||
|
|
||||||
const record = db.transaction(() => {
|
const record = db.transaction(() => {
|
||||||
purgeOldAIAutoCommentRateLimitEvents(eventDate);
|
purgeOldAIAutoCommentRateLimitEvents(eventDate);
|
||||||
|
if (normalizedPostKey && hasAIAutoCommentEventForPost(normalizedProfileNumber, normalizedPostKey)) {
|
||||||
|
return buildAIAutoCommentRateLimitStatus(normalizedProfileNumber, settings, eventDate, {
|
||||||
|
postKey: normalizedPostKey
|
||||||
|
});
|
||||||
|
}
|
||||||
db.prepare(`
|
db.prepare(`
|
||||||
INSERT INTO ai_auto_comment_rate_limit_events (profile_number, created_at)
|
INSERT INTO ai_auto_comment_rate_limit_events (profile_number, post_key, created_at)
|
||||||
VALUES (?, ?)
|
VALUES (?, ?, ?)
|
||||||
`).run(normalizedProfileNumber, eventDate.toISOString());
|
`).run(normalizedProfileNumber, normalizedPostKey, eventDate.toISOString());
|
||||||
|
|
||||||
return buildAIAutoCommentRateLimitStatus(normalizedProfileNumber, settings, eventDate);
|
return buildAIAutoCommentRateLimitStatus(normalizedProfileNumber, settings, eventDate, {
|
||||||
|
postKey: normalizedPostKey
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return record();
|
return record();
|
||||||
@@ -2159,6 +2197,7 @@ db.exec(`
|
|||||||
CREATE TABLE IF NOT EXISTS ai_auto_comment_rate_limit_events (
|
CREATE TABLE IF NOT EXISTS ai_auto_comment_rate_limit_events (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
profile_number INTEGER NOT NULL,
|
profile_number INTEGER NOT NULL,
|
||||||
|
post_key TEXT,
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
`);
|
`);
|
||||||
@@ -2655,8 +2694,13 @@ ensureColumn('ai_auto_comment_rate_limit_settings', 'burst_limit', 'burst_limit
|
|||||||
ensureColumn('ai_auto_comment_rate_limit_settings', 'cooldown_minutes', 'cooldown_minutes INTEGER NOT NULL DEFAULT 15');
|
ensureColumn('ai_auto_comment_rate_limit_settings', 'cooldown_minutes', 'cooldown_minutes INTEGER NOT NULL DEFAULT 15');
|
||||||
ensureColumn('ai_auto_comment_rate_limit_settings', 'active_hours_start', 'active_hours_start TEXT');
|
ensureColumn('ai_auto_comment_rate_limit_settings', 'active_hours_start', 'active_hours_start TEXT');
|
||||||
ensureColumn('ai_auto_comment_rate_limit_settings', 'active_hours_end', 'active_hours_end TEXT');
|
ensureColumn('ai_auto_comment_rate_limit_settings', 'active_hours_end', 'active_hours_end TEXT');
|
||||||
|
ensureColumn('ai_auto_comment_rate_limit_events', 'post_key', 'post_key TEXT');
|
||||||
ensureColumn('ai_auto_comment_rate_limit_profile_state', 'cooldown_until', 'cooldown_until DATETIME');
|
ensureColumn('ai_auto_comment_rate_limit_profile_state', 'cooldown_until', 'cooldown_until DATETIME');
|
||||||
ensureColumn('ai_auto_comment_rate_limit_profile_state', 'cooldown_reason', 'cooldown_reason TEXT');
|
ensureColumn('ai_auto_comment_rate_limit_profile_state', 'cooldown_reason', 'cooldown_reason TEXT');
|
||||||
|
db.exec(`
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_ai_auto_comment_rate_limit_events_profile_post
|
||||||
|
ON ai_auto_comment_rate_limit_events(profile_number, post_key);
|
||||||
|
`);
|
||||||
ensureColumn('ai_credentials', 'is_active', 'is_active INTEGER DEFAULT 1');
|
ensureColumn('ai_credentials', 'is_active', 'is_active INTEGER DEFAULT 1');
|
||||||
ensureColumn('ai_credentials', 'priority', 'priority INTEGER DEFAULT 0');
|
ensureColumn('ai_credentials', 'priority', 'priority INTEGER DEFAULT 0');
|
||||||
ensureColumn('ai_credentials', 'base_url', 'base_url TEXT');
|
ensureColumn('ai_credentials', 'base_url', 'base_url TEXT');
|
||||||
@@ -7294,6 +7338,26 @@ app.get('/api/ai-settings', (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.get('/api/ai/auto-comment-rate-limit-status', (req, res) => {
|
||||||
|
try {
|
||||||
|
const profileNumber = sanitizeProfileNumber(req.query.profileNumber);
|
||||||
|
if (!profileNumber) {
|
||||||
|
return res.status(400).json({ error: 'profileNumber is required' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const status = buildAIAutoCommentRateLimitStatus(
|
||||||
|
profileNumber,
|
||||||
|
getAIAutoCommentRateLimitSettings(),
|
||||||
|
new Date(),
|
||||||
|
{ postKey: req.query.postKey }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json({ status });
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
app.put('/api/ai-settings', (req, res) => {
|
app.put('/api/ai-settings', (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { active_credential_id, prompt_prefix, enabled, rate_limit_settings } = req.body;
|
const { active_credential_id, prompt_prefix, enabled, rate_limit_settings } = req.body;
|
||||||
@@ -7933,10 +7997,12 @@ app.post('/api/ai/generate-comment', async (req, res) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let autoCommentRateLimitStatus = null;
|
let autoCommentRateLimitStatus = null;
|
||||||
|
let autoCommentRateLimitGlobalStatus = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { postText, profileNumber, preferredCredentialId } = requestBody;
|
const { postText, profileNumber, preferredCredentialId, postKey } = requestBody;
|
||||||
const normalizedProfileNumber = sanitizeProfileNumber(profileNumber);
|
const normalizedProfileNumber = sanitizeProfileNumber(profileNumber);
|
||||||
|
const normalizedPostKey = sanitizeAIAutoCommentPostKey(postKey);
|
||||||
|
|
||||||
if (!postText) {
|
if (!postText) {
|
||||||
return respondWithTrackedError(400, 'postText is required');
|
return respondWithTrackedError(400, 'postText is required');
|
||||||
@@ -7975,8 +8041,16 @@ app.post('/api/ai/generate-comment', async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const limitCheckStartedMs = timingStart();
|
const limitCheckStartedMs = timingStart();
|
||||||
const availability = checkAIAutoCommentActionAvailability(normalizedProfileNumber, autoCommentRateLimitSettings);
|
const availability = checkAIAutoCommentActionAvailability(
|
||||||
|
normalizedProfileNumber,
|
||||||
|
autoCommentRateLimitSettings,
|
||||||
|
normalizedPostKey
|
||||||
|
);
|
||||||
autoCommentRateLimitStatus = availability.status || null;
|
autoCommentRateLimitStatus = availability.status || null;
|
||||||
|
autoCommentRateLimitGlobalStatus = buildAIAutoCommentRateLimitStatus(
|
||||||
|
normalizedProfileNumber,
|
||||||
|
autoCommentRateLimitSettings
|
||||||
|
);
|
||||||
if (!availability.ok && autoCommentRateLimitStatus && autoCommentRateLimitStatus.blocked) {
|
if (!availability.ok && autoCommentRateLimitStatus && autoCommentRateLimitStatus.blocked) {
|
||||||
timingEnd('profileLimitCheckMs', limitCheckStartedMs);
|
timingEnd('profileLimitCheckMs', limitCheckStartedMs);
|
||||||
const blockedUntilText = autoCommentRateLimitStatus.blocked_until
|
const blockedUntilText = autoCommentRateLimitStatus.blocked_until
|
||||||
@@ -8079,7 +8153,12 @@ app.post('/api/ai/generate-comment', async (req, res) => {
|
|||||||
autoCommentRateLimitStatus = recordAIAutoCommentAction(
|
autoCommentRateLimitStatus = recordAIAutoCommentAction(
|
||||||
normalizedProfileNumber,
|
normalizedProfileNumber,
|
||||||
autoCommentRateLimitSettings,
|
autoCommentRateLimitSettings,
|
||||||
new Date()
|
new Date(),
|
||||||
|
normalizedPostKey
|
||||||
|
);
|
||||||
|
autoCommentRateLimitGlobalStatus = buildAIAutoCommentRateLimitStatus(
|
||||||
|
normalizedProfileNumber,
|
||||||
|
autoCommentRateLimitSettings
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8097,7 +8176,8 @@ app.post('/api/ai/generate-comment', async (req, res) => {
|
|||||||
usedCredential: credential.name,
|
usedCredential: credential.name,
|
||||||
usedCredentialId: credential.id,
|
usedCredentialId: credential.id,
|
||||||
attempts: attemptDetails,
|
attempts: attemptDetails,
|
||||||
autoCommentRateLimit: autoCommentRateLimitStatus
|
autoCommentRateLimit: autoCommentRateLimitStatus,
|
||||||
|
autoCommentRateLimitGlobal: autoCommentRateLimitGlobalStatus
|
||||||
},
|
},
|
||||||
totalDurationMs: backendTimings.totalMs
|
totalDurationMs: backendTimings.totalMs
|
||||||
});
|
});
|
||||||
@@ -8112,6 +8192,7 @@ app.post('/api/ai/generate-comment', async (req, res) => {
|
|||||||
attempts: attemptDetails,
|
attempts: attemptDetails,
|
||||||
rateLimitInfo: rateInfo || null,
|
rateLimitInfo: rateInfo || null,
|
||||||
autoCommentRateLimitStatus,
|
autoCommentRateLimitStatus,
|
||||||
|
autoCommentRateLimitGlobalStatus,
|
||||||
traceId,
|
traceId,
|
||||||
flowId,
|
flowId,
|
||||||
timings: {
|
timings: {
|
||||||
@@ -8129,6 +8210,7 @@ app.post('/api/ai/generate-comment', async (req, res) => {
|
|||||||
if (cooldownDecision) {
|
if (cooldownDecision) {
|
||||||
setAIAutoCommentProfileCooldown(normalizedProfileNumber, cooldownDecision);
|
setAIAutoCommentProfileCooldown(normalizedProfileNumber, cooldownDecision);
|
||||||
autoCommentRateLimitStatus = buildAIAutoCommentRateLimitStatus(normalizedProfileNumber, autoCommentRateLimitSettings);
|
autoCommentRateLimitStatus = buildAIAutoCommentRateLimitStatus(normalizedProfileNumber, autoCommentRateLimitSettings);
|
||||||
|
autoCommentRateLimitGlobalStatus = buildAIAutoCommentRateLimitStatus(normalizedProfileNumber, autoCommentRateLimitSettings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
credentialTimingDetails.push({
|
credentialTimingDetails.push({
|
||||||
@@ -8170,7 +8252,8 @@ app.post('/api/ai/generate-comment', async (req, res) => {
|
|||||||
attempts: error && error.attempts ? error.attempts : null,
|
attempts: error && error.attempts ? error.attempts : null,
|
||||||
responseMeta: {
|
responseMeta: {
|
||||||
attempts: error && error.attempts ? error.attempts : null,
|
attempts: error && error.attempts ? error.attempts : null,
|
||||||
autoCommentRateLimit: autoCommentRateLimitStatus
|
autoCommentRateLimit: autoCommentRateLimitStatus,
|
||||||
|
autoCommentRateLimitGlobal: autoCommentRateLimitGlobalStatus
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Facebook Post Tracker Extension
|
// Facebook Post Tracker Extension
|
||||||
// Uses API_BASE_URL from config.js
|
// Uses API_BASE_URL from config.js
|
||||||
|
|
||||||
const EXTENSION_VERSION = '1.2.2';
|
const EXTENSION_VERSION = '1.2.3';
|
||||||
const PROCESSED_ATTR = 'data-fb-tracker-processed';
|
const PROCESSED_ATTR = 'data-fb-tracker-processed';
|
||||||
const PENDING_ATTR = 'data-fb-tracker-pending';
|
const PENDING_ATTR = 'data-fb-tracker-pending';
|
||||||
const DIALOG_ROOT_SELECTOR = '[role="dialog"], [data-pagelet*="Modal"], [data-pagelet="StoriesRecentStoriesFeedSection"]';
|
const DIALOG_ROOT_SELECTOR = '[role="dialog"], [data-pagelet*="Modal"], [data-pagelet="StoriesRecentStoriesFeedSection"]';
|
||||||
@@ -6509,7 +6509,8 @@ async function generateAIComment(postText, profileNumber, options = {}) {
|
|||||||
maxAttempts = 3,
|
maxAttempts = 3,
|
||||||
flowId = null,
|
flowId = null,
|
||||||
source = 'extension-ai-button',
|
source = 'extension-ai-button',
|
||||||
returnMeta = false
|
returnMeta = false,
|
||||||
|
postKey = null
|
||||||
} = options;
|
} = options;
|
||||||
const normalizedFlowId = typeof flowId === 'string' && flowId.trim()
|
const normalizedFlowId = typeof flowId === 'string' && flowId.trim()
|
||||||
? flowId.trim()
|
? flowId.trim()
|
||||||
@@ -6524,6 +6525,9 @@ async function generateAIComment(postText, profileNumber, options = {}) {
|
|||||||
if (typeof preferredCredentialId === 'number' && !Number.isNaN(preferredCredentialId)) {
|
if (typeof preferredCredentialId === 'number' && !Number.isNaN(preferredCredentialId)) {
|
||||||
basePayload.preferredCredentialId = preferredCredentialId;
|
basePayload.preferredCredentialId = preferredCredentialId;
|
||||||
}
|
}
|
||||||
|
if (typeof postKey === 'string' && postKey.trim()) {
|
||||||
|
basePayload.postKey = postKey.trim();
|
||||||
|
}
|
||||||
|
|
||||||
const requestAttempts = [];
|
const requestAttempts = [];
|
||||||
let lastError = null;
|
let lastError = null;
|
||||||
@@ -6605,7 +6609,8 @@ async function generateAIComment(postText, profileNumber, options = {}) {
|
|||||||
flowId: effectiveFlowId,
|
flowId: effectiveFlowId,
|
||||||
requestAttempts,
|
requestAttempts,
|
||||||
backendTimings: data.timings && data.timings.backend ? data.timings.backend : null,
|
backendTimings: data.timings && data.timings.backend ? data.timings.backend : null,
|
||||||
autoCommentRateLimitStatus: data.autoCommentRateLimitStatus || null
|
autoCommentRateLimitStatus: data.autoCommentRateLimitStatus || null,
|
||||||
|
autoCommentRateLimitGlobalStatus: data.autoCommentRateLimitGlobalStatus || null
|
||||||
};
|
};
|
||||||
return returnMeta ? result : sanitizedComment;
|
return returnMeta ? result : sanitizedComment;
|
||||||
}
|
}
|
||||||
@@ -6664,6 +6669,27 @@ async function generateAIComment(postText, profileNumber, options = {}) {
|
|||||||
throw finalError;
|
throw finalError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fetchAIAutoCommentRateLimitStatus(profileNumber, options = {}) {
|
||||||
|
const normalizedProfile = parseInt(profileNumber, 10);
|
||||||
|
if (!normalizedProfile) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.set('profileNumber', String(normalizedProfile));
|
||||||
|
if (typeof options.postKey === 'string' && options.postKey.trim()) {
|
||||||
|
params.set('postKey', options.postKey.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await backendFetch(`${API_URL}/ai/auto-comment-rate-limit-status?${params.toString()}`);
|
||||||
|
const data = await response.json().catch(() => ({}));
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(data.error || 'Failed to fetch AI rate limit status');
|
||||||
|
}
|
||||||
|
|
||||||
|
return data && data.status ? data.status : null;
|
||||||
|
}
|
||||||
|
|
||||||
async function handleSelectionAIRequest(selectionText, sendResponse) {
|
async function handleSelectionAIRequest(selectionText, sendResponse) {
|
||||||
try {
|
try {
|
||||||
const normalizedSelection = normalizeSelectionText(selectionText);
|
const normalizedSelection = normalizeSelectionText(selectionText);
|
||||||
@@ -6869,6 +6895,20 @@ async function addAICommentButton(container, postElement) {
|
|||||||
: 'Generiere automatisch einen passenden Kommentar';
|
: 'Generiere automatisch einen passenden Kommentar';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getCurrentPostKey = () => {
|
||||||
|
const raw = encodedPostUrl || (container && container.getAttribute('data-post-url'));
|
||||||
|
if (!raw) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const decoded = decodeURIComponent(raw);
|
||||||
|
return normalizeFacebookPostUrl(decoded) || decoded;
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('[FB Tracker] Konnte Post-Key nicht lesen:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const buildBlockedButtonTitle = (status) => {
|
const buildBlockedButtonTitle = (status) => {
|
||||||
if (!status || !status.blocked) {
|
if (!status || !status.blocked) {
|
||||||
return getDefaultButtonTitle();
|
return getDefaultButtonTitle();
|
||||||
@@ -7024,8 +7064,10 @@ async function addAICommentButton(container, postElement) {
|
|||||||
applyAvailabilityState(null);
|
applyAvailabilityState(null);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const settings = await fetchAISettings(forceRefresh);
|
const status = await fetchAIAutoCommentRateLimitStatus(profileNumber, {
|
||||||
const status = getAIAutoCommentRateLimitStatusFromSettings(settings, profileNumber);
|
forceRefresh,
|
||||||
|
postKey: getCurrentPostKey()
|
||||||
|
});
|
||||||
applyAvailabilityState(status);
|
applyAvailabilityState(status);
|
||||||
return status;
|
return status;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -7040,7 +7082,7 @@ async function addAICommentButton(container, postElement) {
|
|||||||
return await rateLimitRefreshPromise;
|
return await rateLimitRefreshPromise;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSharedAISettingsUpdate = async (settings) => {
|
const handleSharedAISettingsUpdate = async () => {
|
||||||
if (!wrapper.isConnected) {
|
if (!wrapper.isConnected) {
|
||||||
clearBlockedCountdown();
|
clearBlockedCountdown();
|
||||||
aiAvailabilitySubscribers.delete(handleSharedAISettingsUpdate);
|
aiAvailabilitySubscribers.delete(handleSharedAISettingsUpdate);
|
||||||
@@ -7048,13 +7090,7 @@ async function addAICommentButton(container, postElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const profileNumber = await fetchBackendProfileNumber();
|
await refreshAvailabilityState(true);
|
||||||
if (!profileNumber) {
|
|
||||||
applyAvailabilityState(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const status = getAIAutoCommentRateLimitStatusFromSettings(settings, profileNumber);
|
|
||||||
applyAvailabilityState(status);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('[FB Tracker] Failed to apply shared AI settings update:', error);
|
console.warn('[FB Tracker] Failed to apply shared AI settings update:', error);
|
||||||
}
|
}
|
||||||
@@ -7841,7 +7877,8 @@ async function addAICommentButton(container, postElement) {
|
|||||||
preferredCredentialId,
|
preferredCredentialId,
|
||||||
flowId: flowTrace.flowId,
|
flowId: flowTrace.flowId,
|
||||||
source: flowTrace.source,
|
source: flowTrace.source,
|
||||||
returnMeta: true
|
returnMeta: true,
|
||||||
|
postKey: getCurrentPostKey()
|
||||||
});
|
});
|
||||||
endPhase('aiRequestMs', {
|
endPhase('aiRequestMs', {
|
||||||
traceId: aiResult.traceId || null,
|
traceId: aiResult.traceId || null,
|
||||||
@@ -7849,9 +7886,12 @@ async function addAICommentButton(container, postElement) {
|
|||||||
});
|
});
|
||||||
mergeTraceInfo(aiResult);
|
mergeTraceInfo(aiResult);
|
||||||
if (aiResult.autoCommentRateLimitStatus) {
|
if (aiResult.autoCommentRateLimitStatus) {
|
||||||
const nextSettings = upsertAIAutoCommentRateLimitStatus(aiSettingsCache.data, aiResult.autoCommentRateLimitStatus);
|
|
||||||
applyAISettingsSnapshot(nextSettings, { timestamp: Date.now(), broadcast: true });
|
|
||||||
applyAvailabilityState(aiResult.autoCommentRateLimitStatus);
|
applyAvailabilityState(aiResult.autoCommentRateLimitStatus);
|
||||||
|
const globalRateLimitStatus = aiResult.autoCommentRateLimitGlobalStatus || null;
|
||||||
|
if (globalRateLimitStatus) {
|
||||||
|
const nextSettings = upsertAIAutoCommentRateLimitStatus(aiSettingsCache.data, globalRateLimitStatus);
|
||||||
|
applyAISettingsSnapshot(nextSettings, { timestamp: Date.now(), broadcast: true });
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
void refreshAvailabilityState(true, profileNumber);
|
void refreshAvailabilityState(true, profileNumber);
|
||||||
}
|
}
|
||||||
@@ -7969,7 +8009,7 @@ async function addAICommentButton(container, postElement) {
|
|||||||
|
|
||||||
if (error && error.status === 429) {
|
if (error && error.status === 429) {
|
||||||
const syncedStatus = await refreshAvailabilityState(true);
|
const syncedStatus = await refreshAvailabilityState(true);
|
||||||
if (syncedStatus) {
|
if (syncedStatus && !syncedStatus.same_post_exempt) {
|
||||||
const nextSettings = upsertAIAutoCommentRateLimitStatus(aiSettingsCache.data, syncedStatus);
|
const nextSettings = upsertAIAutoCommentRateLimitStatus(aiSettingsCache.data, syncedStatus);
|
||||||
applyAISettingsSnapshot(nextSettings, { timestamp: Date.now(), broadcast: true });
|
applyAISettingsSnapshot(nextSettings, { timestamp: Date.now(), broadcast: true });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "Facebook Post Tracker",
|
"name": "Facebook Post Tracker",
|
||||||
"version": "1.2.2",
|
"version": "1.2.3",
|
||||||
"description": "Track Facebook posts across multiple profiles",
|
"description": "Track Facebook posts across multiple profiles",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"storage",
|
"storage",
|
||||||
|
|||||||
Reference in New Issue
Block a user