feat: 新增多个脚本用于监控抖音直播、店铺评价、售后及体验分数据
新增了多个脚本文件,用于监控抖音直播间的弹幕、店铺评价、售后数据及商家体验分。这些脚本通过飞书多维表格进行数据存储,并支持定时任务自动更新数据。具体包括: 1. 直播间弹幕监控脚本 2. 店铺评价监控脚本 3. 售后数据监控脚本 4. 商家体验分监控脚本 5. 竞品、行业及跨行业热门千川素材获取脚本 这些脚本通过飞书API进行数据写入,并支持去重和定时任务调度。
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
// ==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();
|
||||
})();
|
||||
Reference in New Issue
Block a user