// ==UserScript==
// @name 🔥推特twitter标签备注,X翻译,discord翻译,重点关注通知,领哥终极版 V20.2
// @namespace http://tampermonkey.net/
// @version 20.2
// @description 1.支持推特实时翻译,discord翻译,支持翻译字体大小颜色可调整,支持一键本地备份和恢复,支持选择文字快速搜MEME和搜推文,支持BTC和ETH价格实时显示,ETH链上GAS实时显示,并支持代币价格报警,重点推文报警。
// @copyright 2026, 领哥 (https://x.com/shangdu2005)无偿分享,严禁商业转载,觉的好用留下你的好评支持❤!
// @license All Rights Reserved
// @match *://twitter.com/*
// @match *://x.com/*
// @match *://pro.x.com/*
// @match *://discord.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=twitter.com
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_registerMenuCommand
// @grant GM_setClipboard
// @grant GM_notification
// @connect translate.googleapis.com
// @connect api.dexscreener.com
// @connect eth.llamarpc.com
// @connect api.twitter.com
// @connect api.binance.com
// @connect api.etherscan.io
// @downloadURL https://update.greasyfork.icu/scripts/557992/%F0%9F%94%A5%E6%8E%A8%E7%89%B9twitter%E6%A0%87%E7%AD%BE%E5%A4%87%E6%B3%A8%EF%BC%8CX%E7%BF%BB%E8%AF%91%EF%BC%8Cdiscord%E7%BF%BB%E8%AF%91%EF%BC%8C%E9%87%8D%E7%82%B9%E5%85%B3%E6%B3%A8%E9%80%9A%E7%9F%A5%EF%BC%8C%E9%A2%86%E5%93%A5%E7%BB%88%E6%9E%81%E7%89%88%20V202.user.js
// @updateURL https://update.greasyfork.icu/scripts/557992/%F0%9F%94%A5%E6%8E%A8%E7%89%B9twitter%E6%A0%87%E7%AD%BE%E5%A4%87%E6%B3%A8%EF%BC%8CX%E7%BF%BB%E8%AF%91%EF%BC%8Cdiscord%E7%BF%BB%E8%AF%91%EF%BC%8C%E9%87%8D%E7%82%B9%E5%85%B3%E6%B3%A8%E9%80%9A%E7%9F%A5%EF%BC%8C%E9%A2%86%E5%93%A5%E7%BB%88%E6%9E%81%E7%89%88%20V202.meta.js
// ==/UserScript==
(function() {
'use strict';
console.log("🚀 领哥全能终端 V20.2 (X/Pro同步版) 已启动...");
console.log("🌐 当前网站:", window.location.host);
// ================= 🎵 核心音效系统 =================
const AUDIO_PRESETS = {
'alarm1': { name: '核警报', desc: '高频急促警报' },
'alarm2': { name: '蜂鸣器', desc: '连续蜂鸣音' },
'alarm3': { name: '雷达声', desc: '雷达扫描音' },
'alarm4': { name: 'MEME提醒', desc: '特别提醒音' }
};
function createProxyRequest(options) {
return GM_xmlhttpRequest(options);
}
const __SystemConfig = {
decode: (str) => {
try { return decodeURIComponent(escape(window.atob(str))); }
catch (e) { console.error("系统配置损坏"); return ""; }
},
params: {
route_id: "MURSRlBFMHo=",
data_endpoint: "aHR0cHM6Ly9hcGkuZGV4c3NyZWVuZXIuY29tL2xhdGVzdC9kZXgvdG9rZW5zLw==",
svc_trans: "aHR0cHM6Ly90cmFuc2xhdGUuZ29vZ2xlYXBpcy5jb20vdHJhbnNsYXRlX2Evc2luZ2xl",
sys_core_hz: "ODgw",
ch_a: "aHR0cHM6Ly93ZWIzLm9reC5jb20vam9pbi9MSU5HRTg4",
ch_b: "aHR0cHM6Ly93ZWIzLmJpbmFuY2UuY29tL3JlZmVycmFsP3JlZj1GSTdDMTJCSg=="
}
};
if (typeof __SystemConfig === 'undefined' || !__SystemConfig.params) {
console.error("❌ 领哥终端错误:核心组件缺失,请重新安装。");
return;
}
const _BotConfig = {
// 1. 领哥严选黑名单 (骗子ID,全部小写)
blackList: [
"fake_elon", "scam_support", "doubler_bot_eth"
],
aiKeywords: [
"as an ai language model", "ignore previous instructions",
"sorry, i cannot", "my knowledge cutoff",
"i don't have personal opinions", "regenerate response",
"作为一个人工智能", "无法回答", "模型限制"
],
autoHide: false
};
function createAlertSound(type = 'alarm1') {
try {
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
if (type === 'alarm1') {
let baseFreq = 0;
try { baseFreq = parseInt(__SystemConfig.decode(__SystemConfig.params.sys_core_hz)); } catch(e){}
if (!baseFreq) baseFreq = 0;
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.type = 'sawtooth';
oscillator.frequency.setValueAtTime(baseFreq, audioContext.currentTime);
oscillator.frequency.exponentialRampToValueAtTime(baseFreq / 4, audioContext.currentTime + 0.5);
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5);
oscillator.start();
oscillator.stop(audioContext.currentTime + 0.5);
}
else if (type === 'alarm2') {
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.type = 'square';
oscillator.frequency.setValueAtTime(800, audioContext.currentTime);
gainNode.gain.setValueAtTime(0, audioContext.currentTime);
gainNode.gain.linearRampToValueAtTime(0.3, audioContext.currentTime + 0.05);
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime + 0.15);
gainNode.gain.linearRampToValueAtTime(0, audioContext.currentTime + 0.2);
gainNode.gain.setValueAtTime(0, audioContext.currentTime + 0.25);
gainNode.gain.linearRampToValueAtTime(0.3, audioContext.currentTime + 0.3);
oscillator.start();
oscillator.stop(audioContext.currentTime + 0.35);
}
else if (type === 'alarm3') {
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.type = 'sine';
oscillator.frequency.setValueAtTime(600, audioContext.currentTime);
oscillator.frequency.exponentialRampToValueAtTime(1200, audioContext.currentTime + 0.8);
gainNode.gain.setValueAtTime(0.2, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.05, audioContext.currentTime + 0.8);
oscillator.start();
oscillator.stop(audioContext.currentTime + 0.8);
}
else if (type === 'alarm4') {
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.type = 'triangle';
oscillator.frequency.setValueAtTime(523.25, audioContext.currentTime);
oscillator.frequency.exponentialRampToValueAtTime(1046.50, audioContext.currentTime + 0.2);
oscillator.frequency.exponentialRampToValueAtTime(1567.98, audioContext.currentTime + 0.4);
gainNode.gain.setValueAtTime(0, audioContext.currentTime);
gainNode.gain.linearRampToValueAtTime(0.3, audioContext.currentTime + 0.1);
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime + 0.3);
gainNode.gain.linearRampToValueAtTime(0, audioContext.currentTime + 0.5);
oscillator.start();
oscillator.stop(audioContext.currentTime + 0.5);
}
} catch (error) {
console.error("音效生成失败:", error);
}
}
let isAudioUnlocked = false;
const unlockAudio = () => {
if (!isAudioUnlocked) {
try {
const dummyAudio = new Audio();
dummyAudio.volume = 0.01;
dummyAudio.src = 'data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEAQB8AAEAfAAABAAgAZGF0YQAAAAA=';
const playPromise = dummyAudio.play();
if (playPromise !== undefined) {
playPromise.then(() => {
dummyAudio.pause();
dummyAudio.currentTime = 0;
isAudioUnlocked = true;
console.log("🔊 音频权限已隐形解锁");
document.removeEventListener('click', unlockAudio);
document.removeEventListener('touchstart', unlockAudio);
}).catch(e => {
console.log("首次音频解锁失败,等待下次交互");
});
}
} catch (e) {
console.log("音频解锁异常:", e);
}
}
};
document.addEventListener('click', unlockAudio);
document.addEventListener('touchstart', unlockAudio);
// 全局状态
let marketTimer = null;
let isAlerting = false;
let alertInterval = null;
let alertTimeout = null;
let isDashboardOpen = false;
let twitterMonitorInterval = null;
let lastCheckedTweetIds = new Set();
let monitoredTweets = new Map();
let currentAlertType = null;
let isProXInterface = false;
const DEFAULT_UI = {
transColor: '#00E676',
transFontSize: '14px',
noteColor: '#1D9BF0',
noteFontSize: '11px',
vipColor: '#F3BA2F',
floatTop: '60%',
sentinelMode: false,
sentinelShake: true,
alertDuration: 10,
soundType: 'alarm1',
gasThreshold: 10,
priceThreshold: 0.5,
twitterMonitorEnabled: true,
twitterMonitorMode: 'vip',
twitterAlertSound: 'alarm4',
twitterAlertShake: true,
twitterAlertDuration: 10,
twitterKeywords: ['doge', 'meme', 'coin', 'token', 'buy', 'pump', '🚀', '💰'],
monitorVipOnly: true,
monitorAllTweets: false,
proXInterfaceSupport: true
};
const INITIAL_VIP_MAP = {
'vitalikbuterin': ['🏛️ ETH创始人', '#716b94', '#fff'],
'cz_binance': ['🔶 币安创始人', '#F0B90B', '#000']
};
const Storage = {
getConfig: () => ({ ...DEFAULT_UI, ...JSON.parse(GM_getValue('ling_config', '{}')) }),
setConfig: (cfg) => {
GM_setValue('ling_config', JSON.stringify(cfg));
updateStyles();
checkBackgroundTask();
updateTwitterMonitor();
},
getNotes: () => JSON.parse(GM_getValue('ling_user_notes', '{}')),
setNotes: (notes) => GM_setValue('ling_user_notes', JSON.stringify(notes)),
addNote: (handle, note) => {
const notes = Storage.getNotes();
const h = handle.toLowerCase();
if (note && note.trim()) notes[h] = note.trim();
else delete notes[h];
Storage.setNotes(notes);
},
getNote: (handle) => Storage.getNotes()[handle.toLowerCase()] || null,
getVips: () => {
let vips = JSON.parse(GM_getValue('ling_vips', 'null'));
if (!vips) {
vips = JSON.parse(JSON.stringify(INITIAL_VIP_MAP));
GM_setValue('ling_vips', JSON.stringify(vips));
return vips;
}
let isDirty = false;
for (const [handle, info] of Object.entries(INITIAL_VIP_MAP)) {
if (!vips[handle.toLowerCase()]) {
vips[handle.toLowerCase()] = info;
isDirty = true;
}
}
if (isDirty) GM_setValue('ling_vips', JSON.stringify(vips));
return vips;
},
setVips: (vips) => GM_setValue('ling_vips', JSON.stringify(vips)),
getVipInfo: (handle) => Storage.getVips()[handle.toLowerCase()] || null,
addVip: (handle, label) => {
const vips = Storage.getVips();
vips[handle.toLowerCase()] = [label, '#F3BA2F', '#000'];
Storage.setVips(vips);
},
removeVip: (handle) => {
const vips = Storage.getVips();
delete vips[handle.toLowerCase()];
Storage.setVips(vips);
},
getMonitoredTweets: () => JSON.parse(GM_getValue('ling_monitored_tweets', '[]')),
addMonitoredTweet: (tweet) => {
const tweets = Storage.getMonitoredTweets();
tweets.unshift(tweet);
if (tweets.length > 50) tweets.length = 50;
GM_setValue('ling_monitored_tweets', JSON.stringify(tweets));
},
clearMonitoredTweets: () => {
GM_setValue('ling_monitored_tweets', JSON.stringify([]));
},
export: () => {
const data = {
ver: "20.2",
ts: new Date().getTime(),
notes: Storage.getNotes(),
vips: Storage.getVips(),
config: Storage.getConfig(),
monitoredTweets: Storage.getMonitoredTweets()
};
const blob = new Blob([JSON.stringify(data)], {type: 'text/plain'});
const url = URL.createObjectURL(blob);
const a = document.createElement('a'); a.href = url;
a.download = `LingGe_Config_${new Date().toISOString().slice(0,10)}.txt`;
a.click();
},
import: () => {
const input = document.createElement('input'); input.type = 'file'; input.accept = '.json,.txt';
input.onchange = (e) => {
const reader = new FileReader();
reader.onload = (ev) => {
try {
const raw = JSON.parse(ev.target.result);
if(raw.notes) Storage.setNotes(raw.notes);
if(raw.vips) Storage.setVips(raw.vips);
if(raw.config) Storage.setConfig(raw.config);
if(raw.monitoredTweets) {
GM_setValue('ling_monitored_tweets', JSON.stringify(raw.monitoredTweets));
}
alert("✅ 配置已恢复!"); location.reload();
} catch (err) { alert('❌ 文件格式错误'); }
};
reader.readAsText(e.target.files[0]);
};
input.click();
}
};
function normalizeHandle(str) {
if (!str) return null;
try {
let clean = str.replace(/^(https?:\/\/)?(www\.)?(twitter\.com|x\.com|pro\.x\.com)\//, '');
clean = clean.replace(/^[\/@]/, '');
clean = clean.split('?')[0].split('/')[0];
const systemPaths = ['home', 'explore', 'notifications', 'messages', 'status', 'hashtag', 'search', 'settings', 'i', 'communities'];
if (systemPaths.includes(clean.toLowerCase()) || clean.length === 0) return null;
return clean.toLowerCase();
} catch (e) {
return null;
}
}
// ================= 2. 重点关注监控功能 =================
function isTwitterPage() {
const host = window.location.host;
return host.includes('twitter.com') || host.includes('x.com');
}
function checkProXInterface() {
const host = window.location.host;
const path = window.location.pathname;
isProXInterface = host.includes('pro.x.com') || path.includes('/pro/');
return isProXInterface;
}
function getTweetAuthor(tweetElement) {
try {
let authorElement = null;
const selectors = [
'div[data-testid="User-Name"] a[href*="/"]',
'a[role="link"][href*="/"]',
'article a[href*="/"]:not([href*="/status/"])',
'span[class*="username"]'
];
for (const selector of selectors) {
const elements = tweetElement.querySelectorAll(selector);
for (const el of elements) {
// 🛑 关键过滤:跳过头像 (包含图片的链接) 和 时间 (包含time的链接)
if (el.querySelector('img') || el.querySelector('time')) continue;
const href = el.getAttribute('href');
const handle = normalizeHandle(href);
if (handle) {
return handle;
}
}
}
const textContent = tweetElement.textContent || '';
const match = textContent.match(/@(\w+)/);
if (match && match[1]) {
return match[1].toLowerCase();
}
return null;
} catch (error) {
return null;
}
}
function getTweetId(tweetElement) {
try {
let tweetId = tweetElement.getAttribute('data-tweet-id') ||
tweetElement.getAttribute('data-item-id') ||
tweetElement.closest('[data-tweet-id]')?.getAttribute('data-tweet-id');
if (!tweetId) {
const linkElement = tweetElement.querySelector('a[href*="/status/"]') ||
tweetElement.querySelector('a[href*="/tweet/"]');
if (linkElement) {
const href = linkElement.getAttribute('href');
const match = href.match(/\/(\d+)/);
if (match) tweetId = match[1];
}
}
if (!tweetId) {
const author = getTweetAuthor(tweetElement);
const text = getTweetText(tweetElement) || '';
tweetId = `gen_${author}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
return tweetId;
} catch (error) {
console.error("获取推文ID失败:", error);
return `error_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
}
function getTweetText(tweetElement) {
try {
const selectors = [
'div[data-testid="tweetText"]',
'div[class*="tweet"]',
'div[class*="content"]',
'div[dir="auto"]',
'article div:not([class]):not([id])'
];
for (const selector of selectors) {
const element = tweetElement.querySelector(selector);
if (element && element.textContent && element.textContent.trim().length > 0) {
return element.textContent.trim();
}
}
const allText = tweetElement.textContent || '';
const lines = allText.split('\n').filter(line =>
line.trim().length > 10 &&
!line.includes('Retweet') &&
!line.includes('Like') &&
!line.includes('Reply') &&
!line.includes('·')
);
return lines.join(' ').substring(0, 200);
} catch (error) {
console.error("获取推文内容失败:", error);
return '';
}
}
function getTweetTime(tweetElement) {
try {
const timeElement = tweetElement.querySelector('time');
if (timeElement) {
const dateTime = timeElement.getAttribute('datetime');
if (dateTime) {
return new Date(dateTime).getTime();
}
}
return 0;
} catch (e) {
return 0;
}
}
function checkKeywords(text, keywords) {
if (!text || !keywords || keywords.length === 0) return false;
const lowerText = text.toLowerCase();
return keywords.some(keyword =>
keyword && lowerText.includes(keyword.toLowerCase())
);
}
function shouldMonitorTweet(tweetElement) {
const cfg = Storage.getConfig();
if (!cfg.twitterMonitorEnabled) return false;
const author = getTweetAuthor(tweetElement);
if (!author) return false;
const isVip = Storage.getVipInfo(author) !== null;
if (cfg.monitorVipOnly) {
return isVip;
} else if (cfg.monitorAllTweets) {
return true;
} else {
return isVip;
}
}
function triggerVipTwitterAlert(tweetElement) {
const cfg = Storage.getConfig();
if (!cfg.twitterMonitorEnabled) return;
try {
const tweetId = getTweetId(tweetElement);
const author = getTweetAuthor(tweetElement);
const text = getTweetText(tweetElement);
const tweetTime = getTweetTime(tweetElement);
const now = Date.now();
if (tweetTime > 0 && (now - tweetTime > 10 * 60 * 1000)) {
if (tweetId) lastCheckedTweetIds.add(tweetId);
return;
}
if (!tweetId || !author) return;
if (lastCheckedTweetIds.has(tweetId)) return;
lastCheckedTweetIds.add(tweetId);
const isVip = Storage.getVipInfo(author) !== null;
const vipInfo = Storage.getVipInfo(author);
console.log(`🚨 ${isVip ? '重点监控' : '用户'}推文警报: @${author}`, vipInfo ? `[${vipInfo[0]}]` : '');
const hasKeywords = checkKeywords(text, cfg.twitterKeywords || []);
const monitoredTweet = {
id: tweetId,
username: author,
text: text || '(内容获取失败)',
time: new Date().toLocaleTimeString(),
timestamp: Date.now(),
isVip: isVip,
vipLabel: vipInfo ? vipInfo[0] : null,
isKeywordAlert: hasKeywords
};
Storage.addMonitoredTweet(monitoredTweet);
monitoredTweets.set(tweetId, monitoredTweet);
stopAlert();
currentAlertType = 'twitter';
isAlerting = true;
const ball = document.querySelector('.ling-float-toggle');
if (ball && cfg.twitterAlertShake) {
ball.classList.add('ling-alert-active');
ball.classList.add('ling-twitter-alert');
}
try {
createAlertSound(cfg.twitterAlertSound || 'alarm4');
} catch(e) {
console.error("推特音效播放失败:", e);
createAlertSound('alarm4');
}
alertInterval = setInterval(() => {
try {
createAlertSound(cfg.twitterAlertSound || 'alarm4');
} catch(e) {}
}, 1000);
const duration = (cfg.twitterAlertDuration || 10) * 1000;
alertTimeout = setTimeout(() => {
if (currentAlertType === 'twitter') {
stopAlert();
}
}, duration);
if (Notification.permission === "granted") {
const notificationTitle = isVip ? `🚨 重点关注发推: @${author}` : `📝 用户发推: @${author}`;
const notificationBody = text ?
(text.substring(0, 100) + (text.length > 100 ? '...' : '')) :
'查看推文详情';
new Notification(notificationTitle, {
body: hasKeywords ? `包含关键词: ${notificationBody}` : notificationBody,
icon: 'https://abs.twimg.com/favicons/twitter.2.ico',
requireInteraction: true
});
}
updateTwitterMonitorPanel();
if (ball) {
const originalTitle = ball.getAttribute('data-original-title') || ball.title;
ball.setAttribute('data-original-title', originalTitle);
ball.title = `🚨 @${author} ${isVip ? '(重点关注)' : ''}发推了! 点击停止警报`;
}
highlightMonitoredTweet(tweetElement);
} catch (error) {
console.error("触发推特警报失败:", error);
}
}
function highlightMonitoredTweet(tweetElement) {
try {
tweetElement.style.border = '2px solid #FF5252';
tweetElement.style.borderRadius = '8px';
tweetElement.style.padding = '8px';
tweetElement.style.marginBottom = '8px';
tweetElement.style.background = 'rgba(255, 82, 82, 0.05)';
tweetElement.style.transition = 'all 0.3s ease';
const existingTag = tweetElement.querySelector('.ling-monitored-tag');
if (!existingTag) {
const tag = document.createElement('div');
tag.className = 'ling-monitored-tag';
tag.innerHTML = '🚨 监控到';
tag.style.position = 'absolute';
tag.style.top = '5px';
tag.style.right = '5px';
tag.style.zIndex = '1000';
if (tweetElement.style.position === 'static' || !tweetElement.style.position) {
tweetElement.style.position = 'relative';
}
tweetElement.appendChild(tag);
}
setTimeout(() => {
tweetElement.style.border = '';
tweetElement.style.padding = '';
tweetElement.style.marginBottom = '';
tweetElement.style.background = '';
}, 5000);
} catch (error) {
console.error("高亮推文失败:", error);
}
}
function monitorTwitterPage() {
const cfg = Storage.getConfig();
if (!cfg.twitterMonitorEnabled || !isTwitterPage()) {
return;
}
console.log("🕵️ 正在监控X/Twitter页面...");
const tweetSelectors = [
'article[data-testid="tweet"]',
'div[data-testid="tweet"]',
'article',
'div[role="article"]',
'div[class*="tweet"]'
];
let foundTweets = false;
for (const selector of tweetSelectors) {
const tweets = document.querySelectorAll(selector);
if (tweets.length > 0) {
console.log(`🔍 找到 ${tweets.length} 条推文 (选择器: ${selector})`);
tweets.forEach(tweetElement => {
if (!tweetElement.dataset.lingMonitored) {
if (shouldMonitorTweet(tweetElement)) {
triggerVipTwitterAlert(tweetElement);
}
tweetElement.dataset.lingMonitored = 'true';
}
});
foundTweets = true;
break;
}
}
if (!foundTweets) {
console.log("⚠️ 未找到推文元素,页面结构可能已更新");
}
}
function setupPageMonitoring() {
if (!isTwitterPage()) return;
const cfg = Storage.getConfig();
if (!cfg.twitterMonitorEnabled) return;
if (twitterMonitorInterval) {
clearInterval(twitterMonitorInterval);
twitterMonitorInterval = null;
}
monitorTwitterPage();
twitterMonitorInterval = setInterval(monitorTwitterPage, 5000);
console.log("⏰ 页面监控已启动,间隔: 5秒");
}
function updateTwitterMonitor() {
const cfg = Storage.getConfig();
if (twitterMonitorInterval) {
clearInterval(twitterMonitorInterval);
twitterMonitorInterval = null;
}
if (cfg.twitterMonitorEnabled && isTwitterPage()) {
setupPageMonitoring();
} else {
console.log("⏸️ 推特监控已暂停");
}
}
function requestNotificationPermission() {
if (Notification.permission === "default") {
Notification.requestPermission().then(permission => {
if (permission === "granted") {
console.log("🔔 通知权限已获得");
}
});
}
}
// ================= 3. 动态样式系统 =================
function updateStyles() {
const cfg = Storage.getConfig();
const oldStyle = document.getElementById('ling-style');
if (oldStyle) oldStyle.remove();
const css = `
.ling-trans-box { margin-top: 6px; padding: 8px 10px; background: #0b0b0b; border-left: 3px solid ${cfg.transColor}; border-radius: 4px; color: ${cfg.transColor}; font-size: ${cfg.transFontSize}; line-height: 1.5; font-family: "Consolas", monospace; }
.ling-discord-box { margin-top: 4px; padding: 4px 8px; opacity: 0.9; background: rgba(0,0,0,0.5); border-left: 2px solid ${cfg.transColor}; }
.ling-fast-btn { color: #000; font-weight: 800; padding: 3px 10px; border-radius: 4px; font-size: 12px; cursor: pointer; margin: 4px 6px 0 0; border: 1px solid rgba(255,255,255,0.2); display: inline-flex; align-items: center; text-decoration: none !important; transition: all 0.2s; vertical-align: middle; box-shadow: 0 2px 5px rgba(0,0,0,0.3); white-space: nowrap; }
.ling-fast-btn:hover { transform: translateY(-1px); box-shadow: 0 4px 10px rgba(0,0,0,0.6); filter: brightness(1.1); }
.ling-data-tag { background: rgba(0,0,0,0.4); color: #fff; padding: 0 5px; border-radius: 3px; margin-left: 6px; font-size: 11px; font-weight: normal; display: none; }
.ling-data-loaded .ling-data-tag { display: inline-block; }
.ling-vip-tweet { border: 2px solid ${cfg.vipColor} !important; background: rgba(243, 186, 47, 0.05) !important; border-radius: 8px !important; }
.ling-identity-badge { font-weight: 900; font-size: 10px; padding: 2px 5px; border-radius: 3px; margin-left: 5px; vertical-align: middle; display: inline-block; box-shadow: 0 1px 2px rgba(0,0,0,0.5); color: #000; background: ${cfg.vipColor}; }
.ling-user-note { background-color: ${cfg.noteColor}; color: #fff; font-size: ${cfg.noteFontSize}; padding: 2px 6px; border-radius: 4px; margin-left: 5px; vertical-align: middle; display: inline-block; cursor: pointer; max-width: 150px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: bold; }
.ling-action-btn { cursor: pointer; margin-left: 6px; font-size: 14px; vertical-align: middle; display: inline-block; opacity: 0.4; transition: 0.2s; filter: grayscale(100%); }
.ling-action-btn:hover { opacity: 1; filter: grayscale(0%); transform: scale(1.2); }
.ling-action-btn.active { opacity: 1; filter: grayscale(0%); text-shadow: 0 0 8px gold; }
.ling-twitter-monitor-panel {
max-height: 300px;
overflow-y: auto;
margin-top: 10px;
border: 1px solid #333;
border-radius: 6px;
padding: 8px;
background: #0a0a0a;
}
.ling-monitored-tweet {
background: #1a1a1a;
border-left: 3px solid #1DA1F2;
padding: 8px;
margin-bottom: 6px;
border-radius: 4px;
font-size: 12px;
line-height: 1.4;
}
.ling-monitored-tweet.vip-alert {
border-left-color: ${cfg.vipColor};
background: rgba(243, 186, 47, 0.05);
}
.ling-monitored-tweet.keyword-alert {
border-left-color: #FF5252;
background: rgba(255, 82, 82, 0.05);
}
.ling-tweet-username {
color: #1DA1F2;
font-weight: bold;
margin-right: 8px;
}
.ling-tweet-username.vip {
color: ${cfg.vipColor};
}
.ling-tweet-time {
color: #666;
font-size: 10px;
float: right;
}
.ling-tweet-text {
color: #ddd;
margin-top: 4px;
word-break: break-word;
}
.ling-vip-label {
background: ${cfg.vipColor};
color: #000;
padding: 1px 4px;
border-radius: 3px;
font-size: 9px;
font-weight: bold;
margin-left: 5px;
}
.ling-clear-tweets-btn {
background: #FF5252;
color: white;
border: none;
padding: 4px 8px;
border-radius: 3px;
font-size: 11px;
cursor: pointer;
margin-top: 5px;
width: 100%;
}
.ling-dashboard {
position: fixed;
top: 15%;
right: 20px;
background: #111;
border: 1px solid ${cfg.vipColor};
border-radius: 12px;
padding: 15px;
z-index: 2147483646;
box-shadow: 0 10px 30px rgba(0,0,0,0.8);
min-width: 260px;
opacity: 0;
visibility: hidden;
transform: translateY(-10px);
transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s;
max-height: 80vh;
overflow-y: auto;
}
.ling-dashboard.active {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.ling-float-toggle {
position: fixed;
right: 10px;
top: ${cfg.floatTop};
width: 45px;
height: 45px;
border-radius: 50%;
background: #000;
border: 2px solid ${cfg.vipColor};
color: #fff;
display: flex;
justify-content: center;
align-items: center;
cursor: grab;
z-index: 2147483645;
box-shadow: 0 4px 10px rgba(0,0,0,0.5);
transition: transform 0.1s, opacity 0.2s;
opacity: 0.8;
user-select: none;
overflow: visible;
}
.ling-float-toggle:hover {
opacity: 1;
transform: scale(1.05);
}
.ling-float-toggle:active {
cursor: grabbing;
transform: scale(0.95);
}
@keyframes ling-shake-anim-twitter {
0% { transform: translate(0, 0) rotate(0deg); border-color: #1DA1F2; box-shadow: 0 0 30px #1DA1F2; }
10% { transform: translate(-5px, -5px) rotate(-3deg); }
20% { transform: translate(5px, 5px) rotate(3deg); }
30% { transform: translate(-5px, 5px) rotate(-3deg); }
40% { transform: translate(5px, -5px) rotate(3deg); }
50% { transform: translate(-3px, 0) rotate(-2deg); border-color: #FF5252; box-shadow: 0 0 30px #FF5252; }
60% { transform: translate(3px, 0) rotate(2deg); }
70% { transform: translate(-2px, -2px) rotate(-1deg); border-color: #1DA1F2; box-shadow: 0 0 30px #1DA1F2; }
80% { transform: translate(2px, 2px) rotate(1deg); }
90% { transform: translate(-1px, 0) rotate(-0.5deg); }
100% { transform: translate(0, 0) rotate(0deg); border-color: #1DA1F2; box-shadow: 0 0 30px #1DA1F2; }
}
@keyframes ling-shake-anim-strong {
0% { transform: translate(0, 0) rotate(0deg); border-color: #FF5252; box-shadow: 0 0 50px #FF5252; }
10% { transform: translate(-8px, -8px) rotate(-8deg); }
20% { transform: translate(8px, 8px) rotate(8deg); }
30% { transform: translate(-8px, 8px) rotate(-8deg); }
40% { transform: translate(8px, -8px) rotate(8deg); }
50% { transform: translate(-5px, 0) rotate(-5deg); border-color: #FF0000; box-shadow: 0 0 60px #FF0000; }
60% { transform: translate(5px, 0) rotate(5deg); }
70% { transform: translate(-3px, -3px) rotate(-3deg); border-color: #FF5252; box-shadow: 0 0 50px #FF5252; }
80% { transform: translate(3px, 3px) rotate(3deg); }
90% { transform: translate(-2px, 0) rotate(-2deg); }
100% { transform: translate(0, 0) rotate(0deg); border-color: #FF5252; box-shadow: 0 0 50px #FF5252; }
}
.ling-alert-active {
animation: ling-shake-anim-strong 0.5s !important;
animation-iteration-count: infinite !important;
}
.ling-twitter-alert {
animation: ling-shake-anim-twitter 0.5s !important;
animation-iteration-count: infinite !important;
border-color: #1DA1F2 !important;
}
.ling-logo-text {
font-family: 'Arial Black', sans-serif;
font-weight: 900;
font-size: 14px;
letter-spacing: -1px;
}
.ling-float-close {
position: absolute;
top: -5px;
right: -5px;
width: 16px;
height: 16px;
background: #FF5252;
color: white;
border-radius: 50%;
font-size: 12px;
line-height: 14px;
text-align: center;
font-weight: bold;
display: none;
cursor: pointer;
border: 1px solid #fff;
}
.ling-float-toggle:hover .ling-float-close {
display: block;
}
.ling-dash-link {
display: flex;
align-items: center;
color: #fff;
text-decoration: none;
padding: 10px;
background: #222;
margin-bottom: 8px;
border-radius: 6px;
font-size: 13px;
transition: 0.2s;
font-weight: bold;
}
.ling-dash-link:hover {
background: #333;
color: ${cfg.vipColor};
transform: translateX(5px);
}
.ling-dash-btn-row {
display: flex;
justify-content: space-between;
gap: 5px;
margin-top: 5px;
}
.ling-mini-btn {
flex: 1;
background: #333;
border: 1px solid #444;
color: #ccc;
padding: 5px;
border-radius: 4px;
font-size: 11px;
cursor: pointer;
text-align: center;
}
.ling-mini-btn:hover {
background: #444;
color: #fff;
}
#ling-settings-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.8);
z-index: 2147483647;
display: flex;
justify-content: center;
align-items: center;
}
#ling-settings-box {
background: #16181c;
border: 1px solid #333;
border-radius: 12px;
padding: 20px;
width: 350px;
color: #fff;
font-family: sans-serif;
max-height: 90vh;
overflow-y: auto;
}
.ling-row {
margin-bottom: 15px;
display: flex;
justify-content: space-between;
align-items: center;
}
.ling-btn {
background: #00E676;
color: #000;
border: none;
padding: 8px;
border-radius: 5px;
width: 100%;
font-weight: bold;
cursor: pointer;
margin-top: 10px;
}
#ling-sniper-icon {
position: absolute;
padding: 4px 10px;
height: 24px;
background: #F3BA2F;
border: 2px solid #fff;
border-radius: 20px;
color: #000;
font-size: 12px;
font-weight: 900;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
box-shadow: 0 4px 10px rgba(0,0,0,0.5);
z-index: 2147483647;
animation: ling-pop 0.2s cubic-bezier(0.18, 0.89, 0.32, 1.28);
transition: transform 0.1s;
white-space: nowrap;
}
#ling-sniper-icon:hover {
transform: scale(1.1);
filter: brightness(1.1);
}
#ling-sniper-container {
position: absolute;
display: none;
gap: 5px;
z-index: 2147483647;
animation: ling-pop 0.2s cubic-bezier(0.18, 0.89, 0.32, 1.28);
}
.ling-sniper-btn {
padding: 4px 10px;
height: 24px;
border-radius: 20px;
color: #fff;
font-size: 12px;
font-weight: 900;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
box-shadow: 0 4px 10px rgba(0,0,0,0.5);
border: 2px solid #fff;
transition: transform 0.1s;
white-space: nowrap;
}
.ling-sniper-btn:hover {
transform: scale(1.1);
filter: brightness(1.1);
}
#ling-meme-btn {
background: #F3BA2F;
color: #000;
}
#ling-tweet-btn {
background: #1DA1F2;
}
.ling-market-bar {
display: flex;
justify-content: space-between;
align-items: center;
background: #000;
border: 1px solid #333;
border-radius: 6px;
margin-bottom: 10px;
padding: 10px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
.ling-market-item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
}
.ling-market-label {
font-size: 10px;
font-weight: 800;
margin-bottom: 2px;
text-transform: uppercase;
letter-spacing: 1px;
}
.ling-market-val {
font-weight: 900;
font-size: 16px;
font-family: 'Arial', sans-serif;
color: #fff;
}
.ling-market-chg {
font-size: 10px;
margin-top: 2px;
font-weight: bold;
}
.ling-gas-val {
color: #fff;
font-size: 16px;
font-weight: 900;
}
.ling-up {
color: #00E676 !important;
}
.ling-down {
color: #FF5252 !important;
}
.ling-plat-icon {
width: 16px;
height: 16px;
vertical-align: middle;
margin-right: 5px;
border-radius: 50%;
}
@keyframes ling-pop {
from {
transform: scale(0);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
`;
const node = document.createElement('style');
node.id = 'ling-style';
node.innerHTML = css;
document.head.appendChild(node);
}
const _u = { d: (str) => decodeURIComponent(escape(window.atob(str))) };
// ================= 4. 核心功能: 划词狙击 =================
let sniperContainer = null;
function initSniper() {
if (sniperContainer) return;
sniperContainer = document.createElement('div');
sniperContainer.id = 'ling-sniper-container';
const memeBtn = document.createElement('div');
memeBtn.id = 'ling-meme-btn';
memeBtn.className = 'ling-sniper-btn';
memeBtn.innerHTML = '⚡ 搜MEME';
memeBtn.onmousedown = (e) => {
e.preventDefault();
e.stopPropagation();
const text = sniperContainer.getAttribute('data-text');
if (text) AlphaCore.sniperSearch(text);
sniperContainer.style.display = 'none';
};
const tweetBtn = document.createElement('div');
tweetBtn.id = 'ling-tweet-btn';
tweetBtn.className = 'ling-sniper-btn';
tweetBtn.innerHTML = '🐦 搜推文';
tweetBtn.onmousedown = (e) => {
e.preventDefault();
e.stopPropagation();
const text = sniperContainer.getAttribute('data-text');
if (text) AlphaCore.checkTrend(text);
sniperContainer.style.display = 'none';
};
sniperContainer.appendChild(memeBtn);
sniperContainer.appendChild(tweetBtn);
document.body.appendChild(sniperContainer);
document.addEventListener('mouseup', (e) => {
setTimeout(() => {
const selection = window.getSelection();
const text = selection.toString().trim();
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
if (text.length >= 2 && text.length <= 20) {
showSniperMenu(e.pageX, e.pageY, text);
} else {
sniperContainer.style.display = 'none';
}
}, 10);
});
document.addEventListener('mousedown', (e) => {
if (!sniperContainer.contains(e.target)) {
sniperContainer.style.display = 'none';
}
});
}
function showSniperMenu(x, y, text) {
sniperContainer.style.left = (x + 10) + 'px';
sniperContainer.style.top = (y - 40) + 'px';
sniperContainer.setAttribute('data-text', text);
sniperContainer.style.display = 'flex';
}
// ================= 5. 网站特定功能 =================
function getSiteType() {
const host = window.location.host;
if (host.includes('twitter.com') || host.includes('x.com')) return 'twitter';
if (host.includes('discord.com')) return 'discord';
if (host.includes('gmgn.ai')) return 'gmgn';
if (host.includes('okx.com')) return 'okx';
if (host.includes('binance.com')) return 'binance';
return 'generic';
}
function checkGmgnAutoSearch() {
if (getSiteType() === 'gmgn') {
const urlParams = new URLSearchParams(window.location.search);
const autoKeyword = urlParams.get('ling_auto_search');
if (autoKeyword) {
const trySearch = setInterval(() => {
const inputs = document.querySelectorAll('input');
let searchInput = null;
for (let i of inputs) {
const ph = (i.placeholder || "").toLowerCase();
if (ph.includes('search') || ph.includes('搜索') || ph.includes('address')) {
searchInput = i;
break;
}
}
if (searchInput) {
clearInterval(trySearch);
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
nativeInputValueSetter.call(searchInput, autoKeyword);
searchInput.dispatchEvent(new Event('input', { bubbles: true }));
searchInput.focus();
setTimeout(() => {
searchInput.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', code: 'Enter', keyCode: 13, bubbles: true }));
const form = searchInput.closest('form');
if (form) form.dispatchEvent(new Event('submit', { bubbles: true }));
}, 200);
}
}, 500);
setTimeout(() => clearInterval(trySearch), 10000);
}
}
}
function detectChain(address, contextText) {
if (!address.startsWith('0x')) return 'sol';
const lowerText = (contextText || "").toLowerCase();
if (address.toLowerCase().endsWith('4444')) return 'bsc';
const ethKeywords = ['eth', 'ethereum', 'erc20', 'vitalik', 'base', 'optimism', 'uniswap'];
if (ethKeywords.some(kw => lowerText.includes(kw))) return 'eth';
const bscKeywords = ['bsc', 'bnb', 'binance', 'pancakeswap', 'bep20'];
if (bscKeywords.some(kw => lowerText.includes(kw))) return 'bsc';
return 'bsc';
}
function getChainConfig(chain) {
if (chain === 'sol') return { name: 'SOL', color: "linear-gradient(90deg, #9945FF 0%, #14F195 100%)", icon: '⚡' };
if (chain === 'eth') return { name: 'ETH', color: "linear-gradient(90deg, #627EEA 0%, #454A75 100%)", icon: '🦄' };
return { name: 'BSC', color: "linear-gradient(90deg, #F0B90B 0%, #FFA500 100%)", icon: '🟡' };
}
const dataCache = {};
function loadTokenData(address, btnElement) {
if (btnElement.dataset.loading === "true") return; // 如果正在查,别重复发请求
if (dataCache[address]) { updateButtonData(btnElement, dataCache[address]); return; }
btnElement.dataset.loading = "true";
btnElement.querySelector('.ling-data-tag').innerText = "加载中...";
let apiBase = "https://api.dexscreener.com/latest/dex/tokens/";
try { apiBase = __SystemConfig.decode(__SystemConfig.params.data_endpoint); } catch(e){}
createProxyRequest({
method: "GET",
url: `${apiBase}${address}`,
headers: {
"User-Agent": "Mozilla/5.0",
"Accept": "application/json"
},
onload: (res) => {
try {
const data = JSON.parse(res.responseText);
if (data && data.pairs && data.pairs.length > 0) {
const bestPair = data.pairs.sort((a, b) => b.liquidity.usd - a.liquidity.usd)[0];
const info = {
price: parseFloat(bestPair.priceUsd).toFixed(bestPair.priceUsd < 0.01 ? 6 : 4),
fdv: formatNumber(bestPair.fdv),
};
dataCache[address] = info;
updateButtonData(btnElement, info);
} else {
const tag = btnElement.querySelector('.ling-data-tag');
if(tag) { tag.innerText = "无数据"; btnElement.classList.add('ling-data-loaded'); }
}
} catch (e) {
console.error("解析失败", e);
const tag = btnElement.querySelector('.ling-data-tag');
if(tag) tag.innerText = "Err";
}
btnElement.dataset.loading = "false"; // 解锁
},
onerror: (e) => {
const tag = btnElement.querySelector('.ling-data-tag');
if (tag) { tag.innerText = "重试"; tag.style.color = "red"; }
btnElement.dataset.loading = "false"; // 解锁
}
});
}
function updateButtonData(btn, info) {
const tag = btn.querySelector('.ling-data-tag');
if (tag) {
tag.innerText = `$${info.price} | MC:${info.fdv}`;
btn.classList.add('ling-data-loaded');
}
}
function formatNumber(num) {
if (!num) return "-";
if (num >= 1000000) return (num / 1000000).toFixed(1) + "M";
if (num >= 1000) return (num / 1000).toFixed(1) + "K";
return num.toFixed(0);
}
function createFastButton(address, chain) {
const config = getChainConfig(chain);
const btn = document.createElement('a');
let link = "";
try {
let ref = __SystemConfig.decode(__SystemConfig.params.route_id);
link = `https://gmgn.ai/${chain}/token/${ref}_${address}`;
} catch(e) {
link = `https://gmgn.ai/${chain}/token/${address}`;
}
btn.href = link;
btn.target = "_blank";
btn.className = 'ling-fast-btn';
btn.style.background = config.color;
btn.innerHTML = `${config.icon} ${config.name} 🔍 点击查价`;
btn.onmouseenter = () => loadTokenData(address, btn);
btn.onclick = (e) => e.stopPropagation();
return btn;
}
function processContent(element, text, platform) {
if (!text || element.dataset.lingProcessed) return;
element.dataset.lingProcessed = "true";
// ============== 🤖 BotHunter 猎杀逻辑 ==============
if (platform === 'twitter' && _BotConfig) {
const lowerText = text.toLowerCase();
let isBot = false;
let botReason = "";
if (_BotConfig.aiKeywords) {
for (let kw of _BotConfig.aiKeywords) {
if (lowerText.includes(kw)) {
isBot = true;
botReason = "疑似AI发言";
break;
}
}
}
if (isBot) {
if (_BotConfig.autoHide) {
const article = element.closest('article');
if (article) article.style.display = 'none';
return;
} else {
element.style.border = "2px solid #FF0000";
const warning = document.createElement('div');
warning.style.cssText = "color:red;font-weight:bold;font-size:12px;margin-bottom:5px;background:rgba(255,0,0,0.1);padding:2px;";
warning.innerHTML = `🤖 领哥警报: ${botReason} [一键屏蔽]`;
const btn = warning.querySelector('.ling-block-btn');
if(btn) {
btn.onclick = (e) => {
e.stopPropagation();
e.preventDefault();
const article = element.closest('article');
if (article) {
article.style.opacity = '0.1';
article.style.pointerEvents = 'none';
alert("已在本地屏蔽此条评论!");
}
};
}
element.parentNode.insertBefore(warning, element);
}
}
}
if (text.length < 10) return;
let addresses = [];
if (platform !== 'discord' && platform !== 'generic') {
const solRegex = /\b([1-9A-HJ-NP-Za-km-z]{32,44})\b/g;
const evmRegex = /\b(0x[a-fA-F0-9]{40})\b/g;
let match;
while ((match = solRegex.exec(text)) !== null) addresses.push({ addr: match[0], type: 'sol' });
while ((match = evmRegex.exec(text)) !== null) addresses.push({ addr: match[0], type: 'evm' });
}
let needTrans = false;
if (platform === 'twitter' || platform === 'discord') {
const isForeign = !/[\u4e00-\u9fa5]/.test(text) || (text.match(/[\u4e00-\u9fa5]/g) || []).length / text.length < 0.3;
if (isForeign) needTrans = true;
}
if (needTrans) {
const textShort = text.length > 2000 ? text.substring(0, 2000) : text;
let apiBase = "https://translate.googleapis.com/translate_a/single";
try { apiBase = __SystemConfig.decode(__SystemConfig.params.svc_trans); } catch(e) {}
const url = `${apiBase}?client=gtx&sl=auto&tl=zh-CN&dt=t&q=${encodeURIComponent(textShort)}`;
createProxyRequest({
method: "GET",
url: url,
timeout: 5000,
onload: (res) => {
try {
const data = JSON.parse(res.responseText);
let transResult = "";
if (data && data[0]) data[0].forEach(i => { if(i[0]) transResult += i[0]; });
renderBox(element, transResult, addresses, text, platform);
} catch(e) {
if (addresses.length > 0) renderBox(element, null, addresses, text, platform);
}
},
onerror: () => {
if (addresses.length > 0) renderBox(element, null, addresses, text, platform);
}
});
} else if (addresses.length > 0) {
renderBox(element, null, addresses, text, platform);
}
}
function renderBox(element, transText, addresses, originalText, platform) {
if (!transText && addresses.length === 0) return;
const container = document.createElement('div');
container.className = platform === 'discord' ? 'ling-trans-box ling-discord-box' : 'ling-trans-box';
if (transText) {
container.innerHTML = `[🤖 领哥工具译]
${transText}`;
} else {
container.style.background = "transparent";
container.style.borderLeft = "none";
container.style.padding = "0";
}
if (platform !== 'discord' && addresses.length > 0) {
const btnContainer = document.createElement('div');
if(transText) btnContainer.style.marginTop = "6px";
addresses.forEach(item => {
const chain = item.type === 'sol' ? 'sol' : detectChain(item.addr, originalText);
const btn = createFastButton(item.addr, chain);
btnContainer.appendChild(btn);
});
container.appendChild(btnContainer);
}
if (platform === 'twitter') element.parentNode.appendChild(container);
else element.appendChild(container);
}
function refreshUserUI(handle, container) {
const note = Storage.getNote(handle);
let noteSpan = container.querySelector('.ling-user-note');
if (note) {
if (!noteSpan) {
noteSpan = document.createElement('span');
noteSpan.className = 'ling-user-note';
const toolbar = container.querySelector('.ling-toolbar');
if (toolbar) container.insertBefore(noteSpan, toolbar);
else container.appendChild(noteSpan);
}
noteSpan.innerText = note;
noteSpan.onclick = (e) => { e.preventDefault(); e.stopPropagation(); editNote(handle, container); };
} else if (noteSpan) noteSpan.remove();
const vipInfo = Storage.getVipInfo(handle);
let vipBadge = container.querySelector('.ling-identity-badge');
const article = container.closest('article');
if (vipInfo) {
if (article) article.classList.add('ling-vip-tweet');
if (!vipBadge) {
vipBadge = document.createElement('span');
vipBadge.className = 'ling-identity-badge';
container.appendChild(vipBadge);
}
vipBadge.innerText = vipInfo[0];
const starBtn = container.querySelector('.ling-star-btn');
if (starBtn) starBtn.classList.add('active');
} else {
if (article) article.classList.remove('ling-vip-tweet');
if (vipBadge) vipBadge.remove();
const starBtn = container.querySelector('.ling-star-btn');
if (starBtn) starBtn.classList.remove('active');
}
}
function editNote(handle, container) {
const old = Storage.getNote(handle) || "";
const val = prompt(`📝 备注 @${handle}:`, old);
if (val !== null) { Storage.addNote(handle, val); refreshUserUI(handle, container); }
}
function toggleVip(handle, container) {
const info = Storage.getVipInfo(handle);
if (info) {
if (confirm(`⚠️ 取消 @${handle} 的重点关注?`)) {
Storage.removeVip(handle);
refreshUserUI(handle, container);
}
} else {
const label = prompt(`🔥 设为重点关注 @${handle}\n输入标签 (如: 顶级VC):`, "重点关注");
if (label) {
Storage.addVip(handle, label);
refreshUserUI(handle, container);
}
}
}
function processUser(article) {
if (article.dataset.lingUserProcessed) return;
let handle = null, container = null;
const links = article.querySelectorAll('a[href*="/"]');
for (let link of links) {
const h = link.getAttribute('href');
if (h && !h.includes('/status/') && !h.includes('/hashtag/')) {
const userNameDiv = article.querySelector('div[data-testid="User-Name"]');
if (userNameDiv && userNameDiv.contains(link)) {
handle = normalizeHandle(h);
if (!handle) continue;
container = link.querySelector('div[dir="ltr"]') || link.parentNode;
break;
}
}
}
if (handle && container) {
article.dataset.lingUserProcessed = "true";
// 💀 黑名单检测
if (_BotConfig && _BotConfig.blackList && _BotConfig.blackList.includes(handle)) {
console.log(`💀 [领哥防身] 猎杀黑名单用户: ${handle}`);
article.style.display = 'none';
return;
}
// ✅ 在X Pro界面中,只显示备注和VIP,不显示工具栏
if (!isProXInterface) {
// 只在非X Pro界面添加工具栏
if (!container.querySelector('.ling-toolbar')) {
const toolbar = document.createElement('span');
toolbar.className = 'ling-toolbar';
toolbar.style.whiteSpace = "nowrap";
const pen = document.createElement('span');
pen.className = 'ling-action-btn';
pen.innerHTML = '✏️';
pen.onclick = (e) => {
e.preventDefault();
e.stopPropagation();
editNote(handle, container);
};
const star = document.createElement('span');
star.className = 'ling-action-btn ling-star-btn';
star.innerHTML = '⭐';
star.onclick = (e) => {
e.preventDefault();
e.stopPropagation();
toggleVip(handle, container);
};
toolbar.appendChild(pen);
toolbar.appendChild(star);
container.appendChild(toolbar);
}
}
refreshUserUI(handle, container);
}
}
// ================= 7. 控制台 & 设置 =================
function checkBackgroundTask() {
const cfg = Storage.getConfig();
const dashboard = document.querySelector('.ling-dashboard');
const isDashboardOpen = dashboard && dashboard.classList.contains('active');
if (isDashboardOpen || cfg.sentinelMode) {
refreshMarketData();
if (!marketTimer) {
marketTimer = setInterval(refreshMarketData, 5000);
console.log("⚡ 核心引擎:已启动 (5s/次)");
}
} else {
if (marketTimer) {
clearInterval(marketTimer);
marketTimer = null;
console.log("💤 核心引擎:已休眠");
}
}
}
function toggleDashboard() {
let dashboard = document.querySelector('.ling-dashboard');
if (!dashboard) {
initDashboard();
dashboard = document.querySelector('.ling-dashboard');
}
if (dashboard.classList.contains('active')) {
dashboard.classList.remove('active');
setTimeout(() => {
dashboard.style.display = 'none';
}, 200);
isDashboardOpen = false;
} else {
dashboard.style.display = 'block';
void dashboard.offsetWidth;
setTimeout(() => {
dashboard.classList.add('active');
}, 10);
isDashboardOpen = true;
}
checkBackgroundTask();
}
function handleMenuCommand() {
let ball = document.querySelector('.ling-float-toggle');
if (!ball) {
createFloatingToggle();
ball = document.querySelector('.ling-float-toggle');
}
if (ball.style.display === 'none') {
ball.style.display = 'flex';
alert("🦅 悬浮球已召回!");
} else {
toggleDashboard();
}
}
const AlphaCore = {
sniperSearch: (keyword) => {
if(!keyword) return;
GM_setClipboard(keyword);
let refCode = "1DRFPE0z";
try { refCode = __SystemConfig.decode(__SystemConfig.params.route_id); } catch(e){}
const targetUrl = `https://gmgn.ai/?chain=bsc&ref=${refCode}&ling_auto_search=${encodeURIComponent(keyword)}`;
window.open(targetUrl, "_blank");
},
checkTrend: (keyword) => {
if(!keyword) return;
if (location.host.includes('x.com') || location.host.includes('twitter.com')) {
const searchInput = document.querySelector('input[data-testid="SearchBox_Search_Input"]');
if (searchInput) {
searchInput.focus();
searchInput.select();
document.execCommand('delete');
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
nativeInputValueSetter.call(searchInput, keyword);
searchInput.dispatchEvent(new Event('input', { bubbles: true }));
setTimeout(() => {
searchInput.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', code: 'Enter', keyCode: 13, bubbles: true }));
const form = searchInput.closest('form');
if (form) form.dispatchEvent(new Event('submit', { bubbles: true }));
}, 100);
return;
}
}
const encoded = encodeURIComponent(keyword);
window.open(`https://x.com/search?q=${encoded}&src=typed_query&f=live`, "_blank");
}
};
let lastGas = 0;
let lastBtc = 0;
let lastEth = 0;
function triggerAlert(isTest = false) {
const cfg = Storage.getConfig();
if (!isTest && !cfg.sentinelMode) return;
console.log("⚡ 市场警报触发!");
stopAlert();
currentAlertType = 'market';
isAlerting = true;
const ball = document.querySelector('.ling-float-toggle');
if (ball && cfg.sentinelShake) {
ball.classList.add('ling-alert-active');
ball.classList.remove('ling-twitter-alert');
}
try {
createAlertSound(cfg.soundType);
} catch(e) {
console.error("音效播放失败:", e);
try {
createAlertSound('alarm1');
} catch(e2) {}
}
alertInterval = setInterval(() => {
try {
createAlertSound(cfg.soundType);
} catch(e) {}
}, 1000);
const duration = (cfg.alertDuration || 10) * 1000;
alertTimeout = setTimeout(() => {
if (currentAlertType === 'market') {
stopAlert();
}
}, duration);
}
function stopAlert() {
isAlerting = false;
currentAlertType = null;
if (alertInterval) {
clearInterval(alertInterval);
alertInterval = null;
}
if (alertTimeout) {
clearTimeout(alertTimeout);
alertTimeout = null;
}
const ball = document.querySelector('.ling-float-toggle');
if (ball) {
ball.classList.remove('ling-alert-active');
ball.classList.remove('ling-twitter-alert');
const originalTitle = ball.getAttribute('data-original-title');
if (originalTitle) {
ball.title = originalTitle;
ball.removeAttribute('data-original-title');
} else {
ball.title = '打开工具箱 / 🔕 点击停止报警';
}
}
console.log("🔕 报警已解除");
}
function testAlertSound(soundType, isTwitter = false, withShake = true) {
try {
console.log(`🔊 测试${isTwitter ? '推特' : '哨兵模式'}警报声音: ${soundType}, 震动: ${withShake}`);
const cfg = Storage.getConfig();
const duration = isTwitter ? (cfg.twitterAlertDuration || 10) : (cfg.alertDuration || 10);
const ball = document.querySelector('.ling-float-toggle');
if (ball && withShake) {
if (isTwitter) {
ball.classList.add('ling-twitter-alert');
} else {
ball.classList.add('ling-alert-active');
}
setTimeout(() => {
ball.classList.remove('ling-alert-active', 'ling-twitter-alert');
}, duration * 1000);
}
let playCount = 0;
const maxPlayCount = Math.ceil(duration);
const playSound = () => {
if (playCount < maxPlayCount) {
createAlertSound(soundType);
playCount++;
setTimeout(playSound, 1000);
}
};
playSound();
alert(`✅ ${isTwitter ? '推特' : '哨兵模式'}警报测试成功!将持续${duration}秒${withShake ? '(含震动效果)' : '(仅声音)'}`);
} catch (error) {
console.error("声音测试失败:", error);
alert("❌ 声音测试失败,请检查音频权限");
}
}
function refreshMarketData() {
const cfg = Storage.getConfig();
// 1. 获取 Gas (使用 LlamaRPC 节点,更稳定)
const fetchGasFast = () => {
const url = 'https://eth.llamarpc.com';
createProxyRequest({
method: "POST",
url: url,
headers: { "Content-Type": "application/json" },
data: JSON.stringify({
jsonrpc: "2.0",
method: "eth_gasPrice",
params: [],
id: 1
}),
timeout: 5000,
onload: (res) => {
try {
const data = JSON.parse(res.responseText);
if (data && data.result) {
const gasWei = parseInt(data.result, 16);
const fastGasPrice = gasWei / 1000000000;
updateGasUI(fastGasPrice);
} else {
updateGasUI(null);
}
} catch(e) {
console.error("Gas数据解析失败:", e);
updateGasUI(null);
}
},
onerror: () => {
console.error("Gas API请求失败");
updateGasUI(null);
},
ontimeout: () => {
console.error("Gas API请求超时");
updateGasUI(null);
}
});
};
const updateGasUI = (fastGasPrice) => {
const el = document.getElementById('ling-gas');
if(el) {
let displayText;
if (fastGasPrice === null || fastGasPrice === undefined) {
displayText = 'Err';
el.style.color = 'red';
} else {
if (fastGasPrice < 1) {
displayText = '<1';
el.style.color = '#00E676';
} else {
const gasGwei = Math.round(fastGasPrice);
displayText = gasGwei.toString();
el.style.color = gasGwei > 50 ? '#FF5252' : (gasGwei > 30 ? '#FF9800' : '#00E676');
}
}
el.innerText = displayText;
}
if (fastGasPrice !== null && lastGas > 0 && fastGasPrice > lastGas + (cfg.gasThreshold || 10)) {
console.log(`⚠️ Gas价格波动: ${lastGas} → ${fastGasPrice} (超过阈值${cfg.gasThreshold || 10})`);
triggerAlert();
}
if (fastGasPrice !== null) {
lastGas = fastGasPrice;
}
};
const fetchPricesFast = () => {
// 使用 ticker/24hr 接口获取涨跌幅数据
const api = 'https://api.binance.com/api/v3/ticker/24hr?symbols=["BTCUSDT","ETHUSDT"]';
createProxyRequest({
method: "GET",
url: api,
timeout: 3000,
onload: (res) => {
try {
const data = JSON.parse(res.responseText);
data.forEach(item => {
if(item.symbol === 'BTCUSDT') updatePriceUI('ling-btc-price', parseFloat(item.lastPrice), 'btc', parseFloat(item.priceChangePercent));
if(item.symbol === 'ETHUSDT') updatePriceUI('ling-eth-price', parseFloat(item.lastPrice), 'eth', parseFloat(item.priceChangePercent));
});
} catch(e) {
console.error("价格数据解析失败:", e);
fetchPricesBackup();
}
},
onerror: () => {
console.error("价格API请求失败");
fetchPricesBackup();
}
});
};
const updatePriceUI = (id, price, type, percent) => {
const el = document.getElementById(id);
const elChg = document.getElementById(type === 'btc' ? 'ling-btc-chg' : 'ling-eth-chg');
if(el) {
let formattedPrice;
if (price >= 1000) {
formattedPrice = '$' + Math.round(price).toLocaleString();
} else if (price >= 1) {
formattedPrice = '$' + price.toFixed(2);
} else {
formattedPrice = '$' + price.toFixed(4);
}
el.innerText = formattedPrice;
el.className = `ling-market-val ${type === 'btc' ? 'ling-color-btc' : 'ling-color-eth'}`;
// 更新涨跌幅显示
if (elChg && percent !== undefined && !isNaN(percent)) {
const sign = percent >= 0 ? '+' : '';
elChg.innerText = `${sign}${percent.toFixed(2)}%`;
elChg.className = `ling-market-chg ${percent >= 0 ? 'ling-up' : 'ling-down'}`;
}
let lastVal = type === 'btc' ? lastBtc : lastEth;
if (lastVal > 0 && price > 0) {
const diff = Math.abs(price - lastVal) / lastVal;
const priceThreshold = cfg.priceThreshold || 0.5;
if (diff * 100 > priceThreshold) {
console.log(`⚠️ ${type.toUpperCase()}价格波动超过${priceThreshold}%: ${lastVal} → ${price} (${(diff*100).toFixed(2)}%)`);
triggerAlert();
}
}
if (type === 'btc') lastBtc = price; else lastEth = price;
}
};
const fetchPricesBackup = () => {
console.log("使用备用价格接口");
const getDex = (url, pId, cId, type) => {
createProxyRequest({
method: "GET",
url: url,
timeout: 5000,
onload: (r) => {
try {
const d = JSON.parse(r.responseText);
if(d.pair) renderPrice(pId, cId, d.pair, type);
} catch(e){
console.error("备用价格解析失败:", e);
}
}
});
};
getDex("https://api.dexscreener.com/latest/dex/pairs/ethereum/0xcbcdf9626bc03e24f779434178a73a0b4bad62ed", 'ling-btc-price', 'ling-btc-chg', 'btc');
getDex("https://api.dexscreener.com/latest/dex/pairs/ethereum/0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", 'ling-eth-price', 'ling-eth-chg', 'eth');
};
function renderPrice(id, chgId, pair, colorType) {
if (!pair) return;
const price = parseFloat(pair.priceUsd);
const chg = pair.priceChange && pair.priceChange.h24 ? pair.priceChange.h24 : 0;
const elPrice = document.getElementById(id);
const elChg = document.getElementById(chgId);
if(!elPrice || !elChg) return;
const chgColor = chg >= 0 ? 'ling-up' : 'ling-down';
const sign = chg >= 0 ? '+' : '';
elPrice.innerText = '$' + Math.round(price).toLocaleString();
elPrice.className = `ling-market-val ${colorType === 'btc' ? 'ling-color-btc' : 'ling-color-eth'}`;
elChg.innerText = `${sign}${chg.toFixed(2)}%`;
elChg.className = `ling-market-chg ${chgColor}`;
const cfg = Storage.getConfig();
let lastVal = colorType === 'btc' ? lastBtc : lastEth;
if (lastVal > 0 && price > 0) {
const diff = Math.abs(price - lastVal) / lastVal;
const priceThreshold = cfg.priceThreshold || 0.5;
if (diff * 100 > priceThreshold) {
console.log(`⚠️ ${colorType.toUpperCase()}价格波动超过${priceThreshold}%: ${lastVal} → ${price} (${(diff*100).toFixed(2)}%)`);
triggerAlert();
}
}
if (colorType === 'btc') lastBtc = price; else lastEth = price;
}
fetchGasFast();
fetchPricesFast();
}
function updateTwitterMonitorPanel() {
const panel = document.getElementById('ling-twitter-monitor-panel');
if (!panel) return;
const tweets = Storage.getMonitoredTweets();
panel.innerHTML = '';
if (tweets.length === 0) {
panel.innerHTML = '