// ==UserScript== // @name 抖音直播间评论次数监控(独立版) // @namespace https://bbs.tampermonkey.net.cn/ // @version 1.2 // @description 每五分钟获取一次直播间的评论次数,并写入飞书多维表格中展示 // @author 汪喜 // @match https://compass.jinritemai.com/screen/live/shop?live_room_id=* // @match https://compass.jinritemai.com/screen/live/* // @match https://compass.jinritemai.com/shop/live-detail?live_room_id=* // @grant GM_xmlhttpRequest // @connect open.feishu.cn // @connect compass.jinritemai.com // @crontab */5 * * * * // ==/UserScript== const CONFIG = { id: "cli_a6f25876ea28100d", secret: "raLC56ZLIara07nKigpysfoDxHTAeyJf", appId: 'Wx1Jb6chwagzmfsOtyycaYmLnpf', tableId: 'tbl0Ig2UVbIRuRFZ', // 修改为评论数据的表格ID rooms: [ { name: "夸迪官方旗舰店" }, { name: "夸迪官方旗舰店直播间" } // 添加更多直播间 ], urls: { tenantAccessToken: "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal", bitableUpdateRecords: (appId, tableId) => `https://open.feishu.cn/open-apis/bitable/v1/apps/${appId}/tables/${tableId}/records`, fetchLiveRoomId: 'https://compass.jinritemai.com/compass_api/shop/live/live_list/realtime?page_no=1&page_size=100', fetchCommentsData: (roomId) => `https://compass.jinritemai.com/compass_api/content_live/shop_official/live_screen/five_min_data?room_id=${roomId}` } }; function log(message) { console.log(message); } const retry = async (fn, retries = 3, delay = 10000) => { for (let i = 0; i < retries; i++) { try { return await fn(); } catch (error) { if (i < retries - 1) { console.warn(`Retrying... (${i + 1}/${retries})`); await new Promise(resolve => setTimeout(resolve, delay)); } else { throw error; } } } }; async function sendHttpRequest(method, url, body = null, headers = { 'Content-Type': 'application/json' }) { return retry(() => new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: method, url: url, data: body, headers: headers, onload: function(response) { if (response.status >= 200 && response.status < 300) { try { const jsonResponse = JSON.parse(response.responseText); resolve(jsonResponse); } catch (e) { reject(`Failed to parse JSON: ${e}`); } } else { reject(`Error: ${response.status} ${response.statusText}`); } }, onerror: function(error) { reject(`Network Error: ${error}`); } }); })); } async function fetchTenantAccessToken(id, secret) { try { const response = await sendHttpRequest("POST", CONFIG.urls.tenantAccessToken, JSON.stringify({ "app_id": id, "app_secret": secret })); return response.tenant_access_token; } catch (error) { throw new Error(`Error fetching Tenant Access Token: ${error}`); } } async function updateBitableRecords(accessToken, appId, tableId, items) { try { const response = await sendHttpRequest("POST", CONFIG.urls.bitableUpdateRecords(appId, tableId), JSON.stringify(items), { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json' }); log('Updated record: ' + JSON.stringify(response)); return response; } catch (error) { throw new Error(`Failed to update record in Bitable: ${error}`); } } async function fetchLiveRoomId(authorNickName) { try { const response = await sendHttpRequest("GET", CONFIG.urls.fetchLiveRoomId); const liveRooms = response.data.card_list; const targetRoom = liveRooms.find(room => room.author.author_nick_name === authorNickName); if (targetRoom) { return targetRoom.live_room_id; } else { throw new Error(`未找到符合条件的直播间: ${authorNickName}`); } } catch (error) { log('Error fetching live room list: ' + error.message); return null; } } async function fetchCommentsData(roomId) { try { const response = await sendHttpRequest('GET', CONFIG.urls.fetchCommentsData(roomId)); const comments = response.data.card.find(d => d.index_display === '评论次数').value.value; const updateTimeStr = response.data.update_time.replace("更新", "").trim(); const updateTime = new Date(updateTimeStr).getTime(); return { count: comments, timestamp: updateTime }; } catch (error) { log('Error fetching interaction data: ' + error.message); return { count: 0, timestamp: new Date().getTime() }; } } async function fetchComments() { try { for (const room of CONFIG.rooms) { log(`Fetching comments data for ${room.name}...`); const liveRoomId = await fetchLiveRoomId(room.name); if (!liveRoomId) { log(`未找到符合条件的直播间 ${room.name},跳过执行`); continue; } log(`Live room id for ${room.name}: ${liveRoomId}`); const commentsData = await fetchCommentsData(liveRoomId); log(`Fetched comments count for ${room.name}: ${commentsData.count} at ${new Date(commentsData.timestamp).toLocaleString()}`); // 上传数据到多维表格 log(`Uploading comments data to bitable for ${room.name}...`); const accessToken = await fetchTenantAccessToken(CONFIG.id, CONFIG.secret); const itemsToWrite = { fields: { "评论次数": commentsData.count, "时间": commentsData.timestamp, // 直接使用毫秒级时间戳 "直播间名": room.name, "直播间id": liveRoomId } }; await updateBitableRecords(accessToken, CONFIG.appId, CONFIG.tableId, itemsToWrite); log(`Comments data for ${room.name} uploaded successfully.`); } } catch (error) { log('Error fetching comments data: ' + error.message); } } (async function() { log('Script started'); await fetchComments(); })();