This commit is contained in:
NAME
2026-05-16 16:15:01 +00:00
parent e506996855
commit 1608122288
4 changed files with 49 additions and 36 deletions
+31 -18
View File
@@ -1,34 +1,47 @@
console.log('[XAI Background] ✅ Service worker started'); console.log('[XAI Background] ✅ Service worker started');
// ===== TẠO MENU ===== // ===== TẠO MENU: XÓA CŨ → TẠO MỚI, KHÔNG GỘP =====
chrome.runtime.onInstalled.addListener(() => { chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({ chrome.contextMenus.removeAll(() => {
id: 'writeComment', // Menu 1: Comment — KHÔNG cần bôi đen, chỉ trên X
title: '✍️ Viết comment', chrome.contextMenus.create({
contexts: ['all'], id: 'writeComment',
documentUrlPatterns: ['https://x.com/*'] title: '✍️ Viết comment',
}); contexts: ['all'],
chrome.contextMenus.create({ documentUrlPatterns: ['https://x.com/*']
id: 'translateText', });
title: '🌐 Dịch văn bản',
contexts: ['selection'], // Menu 2: Dịch — CHỈ hiện khi đã bôi đen text, có thể dùng trên mọi trang
documentUrlPatterns: ['https://x.com/*', 'http://*/*', 'https://*/*'] chrome.contextMenus.create({
id: 'translateText',
title: '🌐 Dịch văn bản',
contexts: ['selection'],
documentUrlPatterns: ['https://x.com/*', 'http://*/*', 'https://*/*']
});
}); });
}); });
// ===== CONTEXT MENU CLICK ===== // ===== XỬ LÝ CLICK MENU =====
chrome.contextMenus.onClicked.addListener((info, tab) => { chrome.contextMenus.onClicked.addListener((info, tab) => {
if (!tab?.id) return; if (!tab?.id) return;
if (info.menuItemId === 'writeComment') { if (info.menuItemId === 'writeComment') {
// Gửi xuống content: mở form comment
chrome.tabs.sendMessage(tab.id, { action: 'OPEN_FORM' }).catch(() => {}); chrome.tabs.sendMessage(tab.id, { action: 'OPEN_FORM' }).catch(() => {});
return;
} }
if (info.menuItemId === 'translateText' && info.selectionText) { if (info.menuItemId === 'translateText' && info.selectionText) {
chrome.tabs.sendMessage(tab.id, { action: 'OPEN_TRANSLATE_FORM', text: info.selectionText }).catch(() => {}); // Gửi xuống content: mở tab Dịch + fill text sẵn
chrome.tabs.sendMessage(tab.id, {
action: 'OPEN_TRANSLATE_FORM',
text: info.selectionText
}).catch(() => {});
return;
} }
}); });
// ===== MESSAGE LISTENER ===== // ===== NHẬN MESSAGE TỪ CONTENT =====
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === 'GENERATE_COMMENT') { if (request.action === 'GENERATE_COMMENT') {
handleGenerate(request.data, sender.tab.id); handleGenerate(request.data, sender.tab.id);
@@ -44,7 +57,7 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
return false; return false;
}); });
// ===== COMMENT API ===== // ===== GỌI API COMMENT =====
async function handleGenerate({ text, lang, tone, angle }, tabId) { async function handleGenerate({ text, lang, tone, angle }, tabId) {
const cfg = await chrome.storage.local.get(['apiUrl', 'apiKey']); const cfg = await chrome.storage.local.get(['apiUrl', 'apiKey']);
if (!cfg.apiUrl || !cfg.apiKey) { if (!cfg.apiUrl || !cfg.apiKey) {
@@ -78,7 +91,7 @@ async function handleGenerate({ text, lang, tone, angle }, tabId) {
} }
} }
// ===== TRANSLATE API ===== // ===== GỌI API DỊCH =====
async function handleTranslate({ text, target_lang }, tabId) { async function handleTranslate({ text, target_lang }, tabId) {
const cfg = await chrome.storage.local.get(['apiUrl','apiKey','translateUrl','translateKey']); const cfg = await chrome.storage.local.get(['apiUrl','apiKey','translateUrl','translateKey']);
const url = cfg.translateUrl || cfg.apiUrl; const url = cfg.translateUrl || cfg.apiUrl;
@@ -87,7 +100,7 @@ async function handleTranslate({ text, target_lang }, tabId) {
if (!url || !key) { if (!url || !key) {
await chrome.tabs.sendMessage(tabId, { await chrome.tabs.sendMessage(tabId, {
action: 'SHOW_TRANSLATE_ERROR', action: 'SHOW_TRANSLATE_ERROR',
error: '⚠️ Chưa cấu hình API.\n\n👉 Vào tab ⚙️ Config để nhập URL và Key.' error: '⚠️ Chưa cấu hình API.'
}).catch(() => {}); }).catch(() => {});
return; return;
} }
+12 -13
View File
@@ -70,14 +70,16 @@
const sel = window.getSelection().toString().trim(); const sel = window.getSelection().toString().trim();
if (sel) lastSelectedText = sel; if (sel) lastSelectedText = sel;
// Lưu tweet text // Lưu tweet text — CHỈ khi click đúng vào article
const article = e.target.closest('article'); const article = e.target.closest('article');
if (!article) { lastTweetText = ''; return; } if (article) {
const textEl = const textEl =
article.querySelector('[data-testid="tweetText"]') || article.querySelector('[data-testid="tweetText"]') ||
article.querySelector('div[lang]') || article.querySelector('div[lang]') ||
article.querySelector('div[dir="auto"]'); article.querySelector('div[dir="auto"]');
lastTweetText = textEl ? textEl.innerText.trim() : article.innerText.trim().slice(0, 600); lastTweetText = textEl ? textEl.innerText.trim() : article.innerText.trim().slice(0, 600);
}
// ⚠️ KHÔNG xóa lastTweetText nếu không có article (tránh lỗi click vào padding/lề)
}); });
// ===== B. LISTENER ===== // ===== B. LISTENER =====
@@ -188,7 +190,6 @@
function openSidebar(tweetText) { function openSidebar(tweetText) {
if (sidebarHost) { if (sidebarHost) {
// Nếu sidebar đã mở, chỉ cần mở drawer
const drawer = sidebarHost.shadowRoot.querySelector('.drawer'); const drawer = sidebarHost.shadowRoot.querySelector('.drawer');
if (drawer) drawer.classList.add('open'); if (drawer) drawer.classList.add('open');
return; return;
@@ -435,7 +436,6 @@
); );
}); });
// Comment buttons
copyBtn.addEventListener('click', async () => { copyBtn.addEventListener('click', async () => {
const text = statusEl.textContent; const text = statusEl.textContent;
if (!text || text.startsWith('⏳') || text.startsWith('Lỗi') || text.startsWith('⚠️')) { if (!text || text.startsWith('⏳') || text.startsWith('Lỗi') || text.startsWith('⚠️')) {
@@ -556,12 +556,11 @@
const url = urlIn.value.trim(), key = keyIn.value.trim(); const url = urlIn.value.trim(), key = keyIn.value.trim();
if (!url || !key) { showCfgStatus('❌ Comment URL và Key không được để trống!', true); return; } if (!url || !key) { showCfgStatus('❌ Comment URL và Key không được để trống!', true); return; }
try { new URL(url); } catch { showCfgStatus('❌ URL không hợp lệ', true); return; } try { new URL(url); } catch { showCfgStatus('❌ URL không hợp lệ', true); return; }
const tUrl = transUrlIn.value.trim() || url; const tUrl = transUrlIn.value.trim();
const tKey = transKeyIn.value.trim() || key; const tKey = transKeyIn.value.trim();
chrome.storage.local.set({ chrome.storage.local.set({
apiUrl: url, apiKey: key, apiUrl: url, apiKey: key,
translateUrl: tUrl === url ? '' : tUrl, translateUrl: tUrl, translateKey: tKey
translateKey: transKeyIn.value.trim()
}, () => { }, () => {
showCfgStatus('✅ Đã lưu!', false); showCfgStatus('✅ Đã lưu!', false);
cfgBadge.style.display = 'none'; cfgBadge.style.display = 'none';
+4 -4
View File
@@ -22,7 +22,7 @@ export class XCacheService {
} }
async setCacheTwRefreshToken(refreshToken: string) { async setCacheTwRefreshToken(refreshToken: string) {
await this.cacheManager.set('tw_app_refresh_token', refreshToken, 30 * 24 * 3600); await this.setCachedKey('tw_app_refresh_token', refreshToken, 30 * 24 * 3600);
} }
async getCacheTwRefreshToken() { async getCacheTwRefreshToken() {
@@ -61,16 +61,16 @@ export class XCacheService {
async changeStateXCookiesIsAlive() { async changeStateXCookiesIsAlive() {
const cacheKey = 'state_xcookie_status'; const cacheKey = 'state_xcookie_status';
const currentState = await this.isXCookiesAlive(); const currentState = await this.isXCookiesAlive();
return this.cacheManager.set(cacheKey, currentState ? 0 : 1, 365 * 24 * 3600); return this.setCachedKey(cacheKey, currentState ? 0 : 1, 365 * 24 * 3600);
} }
async setStateXCookiesIsDie() { async setStateXCookiesIsDie() {
const cacheKey = 'state_xcookie_status'; const cacheKey = 'state_xcookie_status';
return this.cacheManager.set(cacheKey, 0, 365 * 24 * 3600); return this.setCachedKey(cacheKey, 0, 3 * 24 * 3600);
} }
async setStateXCookiesIsSillALive() { async setStateXCookiesIsSillALive() {
const cacheKey = 'state_xcookie_status'; const cacheKey = 'state_xcookie_status';
return this.cacheManager.set(cacheKey, 1, 365 * 24 * 3600); return this.setCachedKey(cacheKey, 1, 3 * 24 * 3600);
} }
async isXCookiesAlive() { async isXCookiesAlive() {
+2 -1
View File
@@ -173,7 +173,7 @@ export class XBrowserService implements OnModuleInit, OnModuleDestroy {
this.logger.debug('isCookieLive?'); this.logger.debug('isCookieLive?');
if (page.url().includes('/home')) { if (page.url().includes('/home')) {
this.logger.log('Cookies live'); this.logger.log('Cookies live');
// await this.xCacheService.setStateXCookiesIsSillALive(); await this.xCacheService.setStateXCookiesIsSillALive();
if (sendNotiWhenAlive) { if (sendNotiWhenAlive) {
// await this.notifyService.sendMessageToTele('✅Verify cookie pass') // await this.notifyService.sendMessageToTele('✅Verify cookie pass')
} }
@@ -330,6 +330,7 @@ export class XBrowserService implements OnModuleInit, OnModuleDestroy {
// const composer = page.locator('a[href="/compose/post"]').first(); // const composer = page.locator('a[href="/compose/post"]').first();
// await page.waitForTimeout(2000 + (Math.random()+Math.random()) * 3000); // await page.waitForTimeout(2000 + (Math.random()+Math.random()) * 3000);
// await composer.click(); // await composer.click();
this.logger.debug('Bắt đầu nhập tweet ...');
const textarea = page.locator('div[data-testid="tweetTextarea_0"]'); const textarea = page.locator('div[data-testid="tweetTextarea_0"]');
await page.waitForTimeout(2000 + (Math.random() + Math.random()) * 3000); await page.waitForTimeout(2000 + (Math.random() + Math.random()) * 3000);