// ==UserScript==
// @name 在雪球首页显示CNN恐惧与贪婪指数
// @namespace http://tampermonkey.net/
// @version 0.0.3
// @description 在雪球网显示CNN恐慌贪婪指数
// @author Wab
// @match https://*.xueqiu.com/*
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @license GPL-3.0
// @connect i@zyzhang.com
// @connect zhunzhua.com
// @run-at document-end
// @downloadURL https://update.greasyfork.icu/scripts/513484/%E5%9C%A8%E9%9B%AA%E7%90%83%E9%A6%96%E9%A1%B5%E6%98%BE%E7%A4%BACNN%E6%81%90%E6%83%A7%E4%B8%8E%E8%B4%AA%E5%A9%AA%E6%8C%87%E6%95%B0.user.js
// @updateURL https://update.greasyfork.icu/scripts/513484/%E5%9C%A8%E9%9B%AA%E7%90%83%E9%A6%96%E9%A1%B5%E6%98%BE%E7%A4%BACNN%E6%81%90%E6%83%A7%E4%B8%8E%E8%B4%AA%E5%A9%AA%E6%8C%87%E6%95%B0.meta.js
// ==/UserScript==
(function() {
'use strict';
function createCNNDiv() {
return new Promise((resolve) => {
const waitForElement = setInterval(() => {
const targetContainer = document.querySelector('.user__col--con');
if (targetContainer) {
clearInterval(waitForElement);
if (document.getElementById('cnn-index')) {
resolve(document.getElementById('cnn-index'));
return;
}
const width = targetContainer.offsetWidth;
const cnnDiv = document.createElement('div');
cnnDiv.id = 'cnn-index';
cnnDiv.style.cssText = `
width: ${width * 0.8}px;
height: 40px;
line-height: 40px;
text-align: center;
font-size: 16px;
font-weight: bold;
margin: 10px 0;
border-radius: 20px;
position: relative;
z-index: 1000;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
background: #ffffff;
`;
const titleDiv = document.createElement('div');
titleDiv.style.cssText = `
font-size: 12px;
color: #666;
text-align: center;
margin-top: 5px;
`;
titleDiv.textContent = '恐惧贪婪指数';
const wrapper = document.createElement('div');
wrapper.style.cssText = `
margin: 5px 0;
padding: 10px;
background: #ffffff;
`;
wrapper.appendChild(cnnDiv);
wrapper.appendChild(titleDiv);
const friendsDiv = targetContainer.querySelector('.user__col--friends');
if (friendsDiv) {
friendsDiv.parentNode.insertBefore(wrapper, friendsDiv.nextSibling);
} else {
targetContainer.appendChild(wrapper);
}
cnnDiv.innerHTML = '加载中...';
resolve(cnnDiv);
}
}, 500);
});
}
function fetchCNNData(cnnDiv) {
const timestamp = new Date().getTime();
const url = 'https://zhunzhua.com/cnn/cnn.txt';
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'GET',
url: `${url}?t=${timestamp}`,
headers: {
'Accept': 'text/plain',
'Cache-Control': 'no-cache'
},
timeout: 10000,
onload: function(response) {
if (response.status === 200) {
try {
const lines = response.responseText.trim().split('\n');
if (lines.length > 0) {
const firstLine = lines[0];
const matches = firstLine.match(/(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2})\s+(\d+)/);
if (matches) {
const value = parseInt(matches[2]);
let status, color;
if (value <= 25) {
status = '极度恐惧';
color = '#ff4444';
} else if (value <= 45) {
status = '恐惧';
color = '#ffa726';
} else if (value <= 55) {
status = '中立';
color = 'transparent';
} else if (value <= 75) {
status = '贪婪';
color = '#66bb6a';
} else {
status = '极度贪婪';
color = '#42a5f5';
}
cnnDiv.style.backgroundColor = color;
cnnDiv.style.border = color === 'transparent' ? '1px solid #e9e9e9' : 'none';
cnnDiv.style.color = color === 'transparent' ? '#333' : '#fff';
cnnDiv.innerHTML = `${value} (${status})`;
resolve(true);
} else {
throw new Error('数据格式不匹配');
}
} else {
throw new Error('响应内容为空');
}
} catch (error) {
reject(error);
}
} else {
reject(new Error(`HTTP 错误: ${response.status}`));
}
},
onerror: function(error) {
reject(error);
},
ontimeout: function() {
reject(new Error('请求超时'));
}
});
});
}
async function init() {
try {
const cnnDiv = await createCNNDiv();
async function updateData() {
try {
await fetchCNNData(cnnDiv);
} catch (error) {
console.error('数据更新失败:', error);
cnnDiv.innerHTML = '数据获取失败';
}
}
await updateData();
setInterval(updateData, 5 * 60 * 1000);
} catch (error) {
console.error('初始化失败:', error);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
let lastUrl = location.href;
new MutationObserver(() => {
const url = location.href;
if (url !== lastUrl) {
lastUrl = url;
setTimeout(init, 1000);
}
}).observe(document, { subtree: true, childList: true });
})();