联动,定时任务

This commit is contained in:
2025-09-08 20:52:31 +08:00
parent 1666d9aa14
commit efee6eef2a
10 changed files with 488 additions and 68 deletions

View File

@@ -6,6 +6,9 @@ export function apiGetCart() {
export function login(data) {
return postAxios({ url: '/api/user/aiChat-doLogin', data })
}
export function logout(data) {
return postAxios({ url: '/api/user/aiChat-logout', data })
}
export function getIdByName(name) {
return getAxios({ url: `/api/tenant/get-id-by-name?name=${name}` })

View File

@@ -0,0 +1,56 @@
<template>
<el-dialog v-model="visible" title="分钟级轮换调度">
<div class="grid grid-cols-2 gap-3 items-center">
<div>阶段 A停留</div>
<div class="flex items-center gap-2">
<el-input-number v-model="aMin" :min="1" :max="59" />
<span>分钟</span>
</div>
<div>阶段 B执行/切换</div>
<div class="flex items-center gap-2">
<el-input-number v-model="bMin" :min="1" :max="59" />
<span>分钟</span>
</div>
<div>总时长</div>
<div><b>{{ aMin + bMin }}</b> 分钟必须等于 60</div>
<div>启用调度</div>
<div><el-switch v-model="enabled" /></div>
</div>
<template #footer>
<el-button @click="emit('update:visible', false)">取消</el-button>
<el-button type="primary" @click="onSave">保存并生效</el-button>
</template>
</el-dialog>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
modelValue: { type: Object, required: true }, // { aMin, bMin, enabled }
visible: { type: Boolean, default: false }
})
const emit = defineEmits(['update:modelValue', 'update:visible', 'save'])
const aMin = computed({
get: () => props.modelValue.aMin,
set: (v) => emit('update:modelValue', { ...props.modelValue, aMin: v })
})
const bMin = computed({
get: () => props.modelValue.bMin,
set: (v) => emit('update:modelValue', { ...props.modelValue, bMin: v })
})
const enabled = computed({
get: () => props.modelValue.enabled,
set: (v) => emit('update:modelValue', { ...props.modelValue, enabled: v })
})
function onSave() {
emit('save', { ...props.modelValue })
emit('update:visible', false)
}
</script>

View File

@@ -11,7 +11,8 @@ export function useDeviceDiscovery({
initCanvas, // (udid) => void
initVideoStream, // (udid, index) => void
wsActionsRef, // () => wsActions (可能一开始是 null)
td // 你的 useTeardown 实例,可选
td, // 你的 useTeardown 实例,可选
}) {
const decoder = new TextDecoder('utf-8')

View File

@@ -0,0 +1,45 @@
import { ref } from "vue"
import { ElMessage } from "element-plus"
import { tryActivate } from "./useTaskControl"
export function useSchedule(runTaskFn) {
const scheduleEnabled = ref(true)
let schedulePlan = [
{ key: "follow", duration: 40 * 60 * 1000 },
{ key: "like", duration: 20 * 60 * 1000 }
]
let scheduleState = { index: 0, startTime: Date.now() }
let scheduleTimer = null
function runTask(key) {
if (!scheduleEnabled.value) return
runTaskFn(key) // 交给外部实现 follow/like/brushLive 等
}
function startScheduleLoop() {
runTask(schedulePlan[scheduleState.index].key)
if (scheduleTimer) clearInterval(scheduleTimer)
scheduleTimer = setInterval(() => {
const now = Date.now()
const cur = schedulePlan[scheduleState.index]
if (now - scheduleState.startTime >= cur.duration) {
scheduleState.index = (scheduleState.index + 1) % schedulePlan.length
scheduleState.startTime = now
runTask(schedulePlan[scheduleState.index].key)
}
}, 30 * 1000)
}
function stopSchedule() {
scheduleEnabled.value = false
if (scheduleTimer) clearInterval(scheduleTimer)
}
return {
scheduleEnabled,
schedulePlan,
scheduleState,
startScheduleLoop,
stopSchedule
}
}

View File

@@ -0,0 +1,23 @@
import { ElMessage } from "element-plus"
export function useTaskControl(runType, activeKey, stopAll) {
function stop() {
stopAll()
runType.value = ""
}
function continueAfterReset(key, cb) {
if (runType.value === key) cb()
}
function tryActivate(key, runner, force = false) {
if (!force && activeKey.value && activeKey.value !== key) {
ElMessage.warning("请先停止当前任务")
return
}
runType.value = key
runner && runner()
}
return { stop, continueAfterReset, tryActivate }
}

BIN
src/src.zip Normal file

Binary file not shown.

View File

@@ -18,7 +18,7 @@ let baseURL = ''
if (process.env.NODE_ENV === 'development') {
// 生产环境
// baseURL = "https://api.tkpage.yolozs.com"
// baseURL = "http://192.168.1.174:8101"
// baseURL = "http://192.168.1.7:8101"
baseURL = "https://crawlclient.api.yolozs.com"
} else {
// 测试环境

View File

@@ -40,7 +40,7 @@ export function createWsActions(wslist) {
slideUp: (udid, index) => send(index, { udid, action: 'slideUp' }),//上滑动视频
slideRight: (udid, index) => send(index, { udid, action: 'slideRight' }),//右滑动视频
getSize: (udid, index) => send(index, { udid, action: 'getSize', index }),//右滑动视频
setClipboard: (udid, index, text, type) => send(index, { udid, action: 'setClipboard', type: type, index, resourceId: text }), //截屏测试
// setClipboard: (udid, index, text, type) => send(index, { udid, action: 'setClipboard', type: type, index, resourceId: text }), //截屏测试
clickLikes: (udid, index) => send(index, { udid, action: 'click', type: 'Likes', index, resourceId: 'com.zhiliaoapp.musically:id/dy6' }),//点赞
clickComment: (udid, index) => send(index, { udid, action: 'click', type: 'Comment', index, resourceId: 'com.zhiliaoapp.musically:id/cvd' }),//打开评论
clickComtext: (udid, index) => send(index, { udid, action: 'click', type: 'Comtext', index, resourceId: 'com.zhiliaoapp.musically:id/cs0' }),//点开输入框
@@ -66,6 +66,7 @@ export function createWsActions(wslist) {
isOneLive: (udid, index) => send(index, { udid, action: 'click', type: 'isOneLive', index, resourceId: 'com.zhiliaoapp.musically:id/s1w' }), //判断是否是单人直播
hostVideo: (udid, index, num) => send(index, { udid, action: 'click', type: 'hostVideo', index, resourceId: 'com.zhiliaoapp.musically:id/d3u', num: num }), //主播视频
test: (udid, index) => send(index, { udid, action: 'test', type: 'test', index, resourceId: 'com.zhiliaoapp.musically:id/TESTFFFXXX' }), //截屏测试
openClose: (udid, index) => send(index, { udid, action: 'click', type: 'openClose', index, resourceId: 'android:id/button2' }), //关闭弹窗
// test2: (udid, index) => send(index, { udid, action: 'dump', type: 'test', index, resourceId: 'com.zhiliaoapp.musically:id/kg4' }), //截屏测试

View File

@@ -79,13 +79,11 @@ import { getToken, setToken, setUser, setUserPass, getUserPass } from '@/stores/
import { ElLoading, ElMessage } from 'element-plus';
let version = ref('1.5.8(测试版)');
let version = ref('1.6.0(测试版)');
onMounted(() => {
})
const router = useRouter();
const formData = ref({
@@ -94,8 +92,6 @@ const formData = ref({
password: getUserPass() == null ? '' : getUserPass().password,
});
const onSubmit = () => {
const loading = ElLoading.service({
lock: true,

View File

@@ -47,11 +47,21 @@
@click="setComText(index)">发送
</div>
</div>
<div class="app-button" @click="installt(device.udid, index, false)">安装粘贴工具</div>
<div class="app-button" @click="mqSend()">mq</div>
<!-- <div class="app-button" @click="installt(device.udid, index, true)">安装tk</div> -->
<!-- <div style="display: flex;">
<input style="border: 1px solid #000;margin:0px 14px;" v-model="textContent[index]" type="text"></input>
<div class="app-button" style="margin: 0px;height: 40px;width: 60px;font-size: 14px;"
@click="setPrivateText(index)">发送
</div>
</div>
<div style="display: flex;">
<input style="border: 1px solid #000;margin:0px 14px;" v-model="textContent[index]" type="text"></input>
<div class="app-button" style="margin: 0px;height: 40px;width: 60px;font-size: 14px;"
@click="setHostId(index)">发送
</div>
</div> -->
<!-- <div class="app-button" @click="installt(device.udid, index, false)">安装粘贴工具</div> -->
<!-- <div class="app-button" @click="mqSend()">mq</div> -->
<div class="app-button" @click="installt(device.udid, index, true)">安装tk</div>
<!-- <div class="app-button" @click="wsActions.isHost(device.udid, index)">一键养号</div> -->
</div>
</div>
@@ -64,6 +74,46 @@
@confirm="onDialogConfirm" @cancel="stop" />
<HostListManagerDialog v-model:visible="showHostDlg" @save="onHostSaved" />
</div>
<!-- 定时调度配置弹窗 -->
<el-dialog v-model="showScheduleDlg" title="定时调度(每小时)" width="420px">
<div style="display:grid;grid-template-columns: 100px 1fr; gap:12px; align-items:center;">
<div>片段 A</div>
<div style="display:flex; gap:8px; align-items:center;">
<el-select v-model="schedAKey" style="width:140px;">
<el-option label="一键关注" value="follow" />
<el-option label="刷视频(养号)" value="like" />
<el-option label="刷直播" value="brushLive" />
<el-option label="监测消息" value="listen" />
</el-select>
<el-input-number v-model="schedAMin" :min="1" :max="59" />
<span>分钟</span>
</div>
<div>片段 B</div>
<div style="display:flex; gap:8px; align-items:center;">
<el-select v-model="schedBKey" style="width:140px;">
<el-option label="一键关注" value="follow" />
<el-option label="刷视频(养号)" value="like" />
<el-option label="刷直播" value="brushLive" />
<el-option label="监测消息" value="listen" />
</el-select>
<el-input-number v-model="schedBMin" :min="1" :max="59" />
<span>分钟</span>
</div>
<div>总时长</div>
<div><b>{{ schedAMin + schedBMin }}</b> 分钟必须等于 60</div>
<div>启用调度</div>
<div><el-switch v-model="scheduleEnabled" /></div>
</div>
<template #footer>
<el-button @click="showScheduleDlg = false">取消</el-button>
<el-button type="primary" @click="saveSchedule">保存并生效</el-button>
</template>
</el-dialog>
</template>
<script setup>
@@ -86,7 +136,7 @@ import ChatDialog from '@/components/ChatDialog.vue'
// import { splitArray } from '@/utils/arrUtil' //分割数组 分配主播 已废弃
import { chooseFile } from '@/utils/fileUtil'
import { connectSSE } from '@/utils/sseUtils'
import { prologue, comment, } from '@/api/account';
import { prologue, comment, logout } from '@/api/account';
import { createTaskQueue } from '@/composables/useTaskQueue' //创建任务
import { useCanvasPointer } from '@/composables/useCanvasPointer' //canvas 初始化 点击转换
import { attachTrimmerForIndex } from '@/composables/useVideoStream' //修剪器
@@ -168,6 +218,85 @@ let istranslate = ref(false); //是否是翻译本页
let phoneXYinfo = ref(getphoneXYinfo() == null ? [{}, {}, {}, {}, {}, {}, {}, {}] : getphoneXYinfo());
const isMonitorOn = ref(false) // false 表示关闭true 表示开启
// —— 每小时调度:前 40 分钟 follow后 20 分钟 like ——
// 想改配比就改这两个 duration毫秒
const schedulePlan = [
{ key: 'follow', duration: 40 * 60 * 1000 },
{ key: 'like', duration: 20 * 60 * 1000 },
]
// 调度状态(持久化一下,避免刷新丢失)
let scheduleState = (() => {
try {
const saved = JSON.parse(localStorage.getItem('SCHEDULE_STATE') || '{}')
if (saved && typeof saved.index === 'number' && typeof saved.startTime === 'number') {
return saved
}
} catch { }
return { index: 0, startTime: Date.now() }
})()
let scheduleTimer = null // 轮询定时器句柄
const scheduleTickMs = 30_000 // 每 30s 检查一次是否到切换点
let scheduleEnabled = ref(true) // 需要时可手动关闭调度(例如“全部停止”)
// —— 调度 UI 状态 ——
// 弹窗
const showScheduleDlg = ref(false)
// 两个时间片(默认 A=follow 40minB=like 20min
const schedAKey = ref('follow')
const schedAMin = ref(40)
const schedBKey = ref('like')
const schedBMin = ref(20)
// 打开弹窗:把当前 schedulePlan 映射到 UI
function openScheduleDialog() {
// 把当前计划读出来(只支持两个片段的简易版)
if (Array.isArray(schedulePlan) && schedulePlan.length >= 2) {
const a = schedulePlan[0], b = schedulePlan[1]
schedAKey.value = a?.key || 'follow'
schedAMin.value = Math.max(1, Math.round((a?.duration || 40 * 60_000) / 60_000))
schedBKey.value = b?.key || 'like'
schedBMin.value = Math.max(1, Math.round((b?.duration || 20 * 60_000) / 60_000))
}
showScheduleDlg.value = true
}
// 保存:校验=60 分钟 → 更新 schedulePlan → 持久化 → 重启轮询
function saveSchedule() {
const total = schedAMin.value + schedBMin.value
if (total !== 60) {
ElMessage.error('两个片段相加必须等于 60 分钟')
return
}
schedulePlan.splice(0, schedulePlan.length,
{ key: schedAKey.value, duration: schedAMin.value * 60_000 },
{ key: schedBKey.value, duration: schedBMin.value * 60_000 },
)
// 存 localStorage
localStorage.setItem('SCHEDULE_PLAN', JSON.stringify(schedulePlan))
localStorage.setItem('SCHEDULE_ENABLED', JSON.stringify(!!scheduleEnabled.value))
// 重置时间片起点并立即生效
scheduleState.index = 0
scheduleState.startTime = Date.now()
localStorage.setItem('SCHEDULE_STATE', JSON.stringify(scheduleState))
// 若启用则重启轮询
if (scheduleEnabled.value) startScheduleLoop()
showScheduleDlg.value = false
ElMessage.success('已保存定时调度')
}
// 这四个互斥模式的 key和你的 runType 对应
const EXCLUSIVE_KEYS = ['brushLive', 'like', 'follow', 'listen'];
@@ -310,6 +439,15 @@ const buttons = [
},
style: () => ({ backgroundColor: isActive({ key: 'listen' }) ? 'red' : '' })
},
{
label: '定时调度',
onClick: () => openScheduleDialog(),
show: () => true,
img: {
normal: new URL('@/assets/video/leftBtn1.png', import.meta.url).href,
hover: new URL('@/assets/video/leftBtn1-1.png', import.meta.url).href
}
},
{
label: '全部停止',
onClick: () => stop(),
@@ -323,6 +461,7 @@ const buttons = [
label: '登出',
onClick: () => {
td.disposeAll('logout')
logout({ userId: userdata.id, tenantId: userdata.tenantId })
router.push('/')
},
show: () => true,
@@ -458,6 +597,15 @@ const initVideoStream = async (udid, index) => {
// 发送 开启 视频流数据
setTimeout(() => {
wslist[index].send(openStr);
wsActions.openClose(udid, index)
if (runType.value) {
resetApp(udid, index)
setTimeout(() => {
continueAfterReset(udid, index)
}, 5000);
}
}, 300);
wsCache.set(udid, { ws: wslist[index], index });
};
@@ -490,7 +638,7 @@ const initVideoStream = async (udid, index) => {
if (resData.message == 0) {
console.log('没有消息')
if (runType.value == 'follow') {
LikesToLikesToLikes(deviceInformation.value[index].udid, index)
LikesToLikesToLikes(udid, index)
}
//如果检测到有新消息会收到两条ws回复一条message==1 一条message==成功
@@ -504,21 +652,21 @@ const initVideoStream = async (udid, index) => {
clickxy(resData.x * getphoneXYinfo()[index].width, resData.y * getphoneXYinfo()[index].height, index)
setTimeout(() => {
clickxy(resData.x * getphoneXYinfo()[index].width, resData.y * getphoneXYinfo()[index].height, index) //index为9的时候长按
wsActions.clickSysMesage(deviceInformation.value[index].udid, index) //点击消息进入对话框
wsActions.clickSysMesage(udid, index) //点击消息进入对话框
}, 100)
}, 2000)
}
} else if (resData.type == 'clickMesage') {
//点击进入新消息页面以后,获取页面信息
wsActions.clickCopyList(deviceInformation.value[index].udid, index)
wsActions.clickCopyList(udid, index)
} else if (resData.type == 'clickSysMesage') {
setTimeout(() => {
Back('', index)
setTimeout(() => {
Back('', index)
if (runType.value == 'follow') {
LikesToLikesToLikes(deviceInformation.value[index].udid, index)
LikesToLikesToLikes(udid, index)
}
// 仅监听模式下恢复
if (runType.value === 'listen' || isMonitorOn.value) {
@@ -557,7 +705,7 @@ const initVideoStream = async (udid, index) => {
chat({ msg: mesBox.text }).then(res => {
console.log("ai返回", res)
textContentpri.value[index] = res.result
PrivatetexToPrivatePush(deviceInformation.value[index].udid, index)
PrivatetexToPrivatePush(udid, index)
})
} else {
@@ -610,7 +758,7 @@ const initVideoStream = async (udid, index) => {
phoneXYinfo.value[index].id = resData.device
if (resData.type == 'Likes') {//判断是否是 点赞
if (runType.value == 'follow') {
wsActions.slideDown(deviceInformation.value[index].udid, index)//是否继续下一个视频
wsActions.slideDown(udid, index)//是否继续下一个视频
}
} else if (resData.type == 'Comment') {//打开评论
} else if (resData.type == 'CommentText') {//复制评论
@@ -625,22 +773,22 @@ const initVideoStream = async (udid, index) => {
}, 1000)
} else {
setTimeout(() => {
wsActions.slideDown(deviceInformation.value[index].udid, index)//是否继续下一个视频
wsActions.slideDown(udid, index)//是否继续下一个视频
setTimeout(() => {
console.log('观看视频中')
randomSeeVideo(deviceInformation.value[index].udid, index) //50秒后继续循环任务
randomSeeVideo(udid, index) //50秒后继续循环任务
}, 1000);
}, 1000)
}
}, 800)
} else if (resData.type == 'tomy') {//打开主页
} else if (resData.type == 'Attention') {//关注、打开私信
// LikesToCommentToComPush(deviceInformation.value[index].udid, index) //是否继续循环任务
// LikesToCommentToComPush(udid, index) //是否继续循环任务
} else if (resData.type == 'return') {//私信评论
} else if (resData.type == 'addHost') {//添加关注
wsActions.slideDown(deviceInformation.value[index].udid, index)//是否继续下一个视频
wsActions.slideDown(udid, index)//是否继续下一个视频
setTimeout(() => {
wsActions.isHost(deviceInformation.value[index].udid, index)//检测
wsActions.isHost(udid, index)//检测
}, 1000);
} else if (resData.type == 'Privatetex') {//私信评论
} else if (resData.type == 'PrivatePush' || resData.type == 'PrivatePushFollow') {//私信发送
@@ -659,9 +807,9 @@ const initVideoStream = async (udid, index) => {
Back('', index);
if (isMonitor.value) {
//正常没有消息,发送完私信以后,返回六次,然后继续下一个任务
wsActions.getmesNum(deviceInformation.value[index].udid, index)
wsActions.getmesNum(udid, index)
} else {
LikesToLikesToLikes(deviceInformation.value[index].udid, index)
LikesToLikesToLikes(udid, index)
}
@@ -669,8 +817,8 @@ const initVideoStream = async (udid, index) => {
}, 1000);
} else if (resData.type == 'PrivatePushFollow') {
//如果有新消息,回复完私信以后,返回三次,然后继续下一个任务
// wsActions.getmesNum(deviceInformation.value[index].udid, index)
// LikesToLikesToLikes(deviceInformation.value[index].udid, index)
// wsActions.getmesNum(udid, index)
// LikesToLikesToLikes(udid, index)
if (runType.value === 'listen' || isMonitorOn.value) {
resumeAndKick(index); // ← 一步到位:恢复并在 1.5s 后补一次 getmesNum
@@ -688,41 +836,41 @@ const initVideoStream = async (udid, index) => {
} else if (resData.type == 'isHost') {//视频关注主播
if (resData.message == 0) {
console.log('无关注', index)
Back(deviceInformation.value[index].udid, index)
Back(udid, index)
setTimeout(() => {
console.log('观看视频中', index)
randomSeeVideo(deviceInformation.value[index].udid, index) //30-50秒后继续循环任务
randomSeeVideo(udid, index) //30-50秒后继续循环任务
}, 1000);
} else if (resData.message == 1) {
console.log('有关注', index)
const randomNum = Math.random(); // 生成一个0到1之间的随机数
//是否点赞评论的概率
if (randomNum < 0.07) {
if (randomNum < 0.20) {
console.log('进行点赞评论', index)
LikesToCommentToComPush(deviceInformation.value[index].udid, index)
LikesToCommentToComPush(udid, index)
} else {
console.log('下一个', index)
wsActions.slideDown(deviceInformation.value[index].udid, index)//是否继续下一个视频
randomSeeVideo(deviceInformation.value[index].udid, index) //30-50秒后继续循环任务
wsActions.slideDown(udid, index)//是否继续下一个视频
randomSeeVideo(udid, index) //30-50秒后继续循环任务
}
}
} else if (resData.type == 'openDY') {//视频关注主播
createTaskQueue(index).clear();//清除队伍中的任务
// createTaskQueue(index).clear();//清除队伍中的任务
} else if (resData.type == 'toLive') {//打开直播
wsActions.isHead(deviceInformation.value[index].udid, index)//判断直播
wsActions.isHead(udid, index)//判断直播
} else if (resData.type == 'isHead') {//判断有没有头像
if (resData.message == 1) {
wsActions.isOneLive(deviceInformation.value[index].udid, index)//判断直播
wsActions.isOneLive(udid, index)//判断直播
} else {
return
}
} else if (resData.type == 'isOneLive') {//判断单人还是双人
if (resData.message == 0) {
wsActions.slideDown(deviceInformation.value[index].udid, index)//是否继续下一个视频
wsActions.isHead(deviceInformation.value[index].udid, index)//判断直播
wsActions.slideDown(udid, index)//是否继续下一个视频
wsActions.isHead(udid, index)//判断直播
return
}
// 用法:
@@ -739,8 +887,8 @@ const initVideoStream = async (udid, index) => {
const interval = setInterval(() => {
if (count >= num) {
clearInterval(interval);
wsActions.slideDown(deviceInformation.value[index].udid, index)//是否继续下一个视频
wsActions.isHead(deviceInformation.value[index].udid, index)//判断直播
wsActions.slideDown(udid, index)//是否继续下一个视频
wsActions.isHead(udid, index)//判断直播
return;
}
@@ -753,8 +901,8 @@ const initVideoStream = async (udid, index) => {
}, 300);
return;
} else {
wsActions.slideDown(deviceInformation.value[index].udid, index)//是否继续下一个视频
wsActions.isHead(deviceInformation.value[index].udid, index)//判断直播
wsActions.slideDown(udid, index)//是否继续下一个视频
wsActions.isHead(udid, index)//判断直播
return
}
})();
@@ -780,7 +928,7 @@ const initVideoStream = async (udid, index) => {
console.log("正在安装...")
} else if (resData.message == '安装成功') {
if (resData.apkName == 'clipper.apk') {
wsActions.startClipserve(deviceInformation.value[index].udid, index)
wsActions.startClipserve(udid, index)
}
} else if (resData.message === 500) {
@@ -808,8 +956,8 @@ const initVideoStream = async (udid, index) => {
} else {
resetApp(udid, index)
setTimeout(() => {
wsActions.isHost(deviceInformation.value[index].udid, index)
}, 1000)
wsActions.isHost(udid, index)
}, 2000)
}
//如果该视频无法被搜索到,返回刷下一条视频
} else if (resData.type == 'toHost') {
@@ -819,7 +967,7 @@ const initVideoStream = async (udid, index) => {
createTaskQueue(index).clear();//清除队伍中的任务
setTimeout(() => {
Back('', index)
LikesToLikesToLikes(deviceInformation.value[index].udid, index)
LikesToLikesToLikes(udid, index)
}, 1000)
}, 1000)
} else if (resData.type == 'PrivatePush' || resData.type == 'Privatetex' || resData.type == 'hostVideo' || resData.type == 'search' || resData.type == 'Attention' || resData.type == 'Comment') {
@@ -829,20 +977,20 @@ const initVideoStream = async (udid, index) => {
setTimeout(() => {
if (isMonitor.value) {
//正常没有消息,发送完私信以后,返回六次,然后继续下一个任务
wsActions.getmesNum(deviceInformation.value[index].udid, index)
wsActions.getmesNum(udid, index)
} else {
LikesToLikesToLikes(deviceInformation.value[index].udid, index)
LikesToLikesToLikes(udid, index)
}
// LikesToLikesToLikes(deviceInformation.value[index].udid, index)
}, 1000)
// LikesToLikesToLikes(udid, index)
}, 2000)
}
} else if (resData.type == 'getmesNum') {
if (runType.value == 'follow') {
LikesToLikesToLikes(deviceInformation.value[index].udid, index)
LikesToLikesToLikes(udid, index)
}
//getmesNum出现没有消息的情况
} else if (resData.type == 'clickMesage' || resData.type == 'ComPush') {
} else if (resData.type == 'clickMesage' || resData.type == 'ComPush' || resData.type == 'openClose') {
//ComPush关注主播的时候出现无法评论的视频 会跳过评论发送,不处理评论发送的返回错误
} else if (resData.type == 'clickCopyList') {
//如果无法获取到聊天记录,返回继续检测
@@ -855,11 +1003,17 @@ const initVideoStream = async (udid, index) => {
} else if (resData.type == 'clickSysMesage') {
//如果没有系统消息,则点击新消息
wsActions.clickMesage(deviceInformation.value[index].udid, index) //点击消息进入对话框
wsActions.clickMesage(udid, index) //点击消息进入对话框
} else {
console.error(resData.message, resData.type); // 错误处理
ElMessage.error(resData.message);
createTaskQueue(index).clear();//清除队伍中的任务
resetApp(udid, index)
setTimeout(() => {
continueAfterReset(udid, index)
}, 5000) // ← 一步到位:恢复并在 1.5s 重试
}
// createTaskQueue(index).next(); // 继续队列中下一个任务
}
@@ -903,7 +1057,7 @@ const { open: openDiscovery } = useDeviceDiscovery({
initCanvas,
initVideoStream,
wsActionsRef: () => wsActions,
td
td,
})
// 鼠标按下事件处理
@@ -1108,6 +1262,8 @@ onUnmounted(() => {
console.log("卸载组件");
td.disposeAll('logout')
cloesMonitor(); //关闭检测消息
if (scheduleTimer) clearInterval(scheduleTimer)
});
//获取设备信息
@@ -1157,18 +1313,77 @@ async function PrivatetexToPrivatePush(udid, index) {
//重置当前应用
async function resetApp(udid, index) {
createTaskQueue(index).clear();//清除队伍中的任务
wsActions.open(udid, index)
await sleep(1000); //等待1秒
await sendWsTask(index, { udid, action: 'killNow' });
await sendWsTask(index, { udid, action: 'openDY' });
continueAfterReset(udid, index)
}
//发送评论字符到手机方法
// //发送评论字符到手机方法
// function setComText(index) {
// isSend.value = true;
// setTimeout(() => {
// isSend.value = false;
// }, 300);
// console.log('发送评论内容', index, textContent.value[index])
// wsActions.setClipboard(deviceInformation.value[index].udid, index, textContent.value[index], "ComText")
// if (runType.value === 'follow') {
// textContent.value[index] = getContentList()[index][getRandomNumber(getContentList()[index].length - 1)];
// }
// }
// //发送主播ID字符到手机方法
// function setHostId(index) {
// isSend.value = true;
// setTimeout(() => {
// isSend.value = false;
// }, 300);
// const host = markFirstFalseAsTrue(getHostList())
// if (host == null) {
// createTaskQueue(index).clear();//清除队伍中的任务
// ElMessage.success('主播ID已全部发送完毕');
// return;
// }
// hostIdArr.value[index] = host;
// // wslist[index].send(setClipboard(host.text)); //发送内容
// wsActions.setClipboard(deviceInformation.value[index].udid, index, host.text, "id")
// }
// //发送私信字符到手机方法
// function setPrivateText(index) {
// isSend.value = true;
// setTimeout(() => {
// isSend.value = false;
// }, 300);
// console.log('发送私信内容', index, textContentpri.value[index])
// //如果是在关注主播中,发送的开场白进行对应国家翻译
// if (runType.value === 'follow') {
// translation({ msg: getContentpriList()[index][getRandomNumber(getContentpriList()[index].length - 1)], country: hostIdArr.value[index].country }).then(res => {
// console.log(res)
// textContentpri.value[index] = res;
// // wslist[index].send(setClipboard(textContentpri.value[index]));
// wsActions.setClipboard(deviceInformation.value[index].udid, index, textContentpri.value[index], "Private")
// })
// } else {
// // wslist[index].send(setClipboard(textContentpri.value[index]));
// wsActions.setClipboard(deviceInformation.value[index].udid, index, textContentpri.value[index], "Private")
// if (getContentpriList()[index]) {
// textContentpri.value[index] = getContentpriList()[index][getRandomNumber(getContentpriList()[index].length - 1)];
// }
// }
// }
// 发送评论字符到手机方法
function setComText(index) {
isSend.value = true;
setTimeout(() => {
isSend.value = false;
}, 300);
console.log('发送评论内容', index, textContent.value[index])
wsActions.setClipboard(deviceInformation.value[index].udid, index, textContent.value[index], "ComText")
wslist[index].send(setClipboard(textContent.value[index]));
if (runType.value === 'follow') {
textContent.value[index] = getContentList()[index][getRandomNumber(getContentList()[index].length - 1)];
}
@@ -1187,8 +1402,8 @@ function setHostId(index) {
return;
}
hostIdArr.value[index] = host;
// wslist[index].send(setClipboard(host.text)); //发送内容
wsActions.setClipboard(deviceInformation.value[index].udid, index, host.text, "id")
wslist[index].send(setClipboard(host.text)); //发送内容
}
@@ -1204,19 +1419,16 @@ function setPrivateText(index, datatype) {
translation({ msg: getContentpriList()[index][getRandomNumber(getContentpriList()[index].length - 1)], country: hostIdArr.value[index].country }).then(res => {
console.log(res)
textContentpri.value[index] = res;
// wslist[index].send(setClipboard(textContentpri.value[index]));
wsActions.setClipboard(deviceInformation.value[index].udid, index, textContentpri.value[index], "Private")
wslist[index].send(setClipboard(textContentpri.value[index]));
})
} else {
// wslist[index].send(setClipboard(textContentpri.value[index]));
wsActions.setClipboard(deviceInformation.value[index].udid, index, textContentpri.value[index], "Private")
wslist[index].send(setClipboard(textContentpri.value[index]));
if (getContentpriList()[index]) {
textContentpri.value[index] = getContentpriList()[index][getRandomNumber(getContentpriList()[index].length - 1)];
}
}
}
//获取手机粘贴板方法
function getText(index) {
wslist[index].send(getClipboard());
@@ -1280,7 +1492,10 @@ function sendWsTask(index, data) {
}
//发送私信的文本粘贴事件
if (data.type == 'PrivatePush' || data.type == 'PrivatePushFollow') {
setPrivateText(index, data.type)
setTimeout(() => {
setPrivateText(index, data.type)
}, 500)
}
//关注前的返回和右滑
if (data.type == 'Attention') {
@@ -1376,6 +1591,9 @@ function stop() {
isStop.value = true; //停止所有任务
isMsgPop.value = false;//关闭爬虫sse任务
// —— 暂停调度,防止它又切回来 ——
scheduleEnabled.value = false
deviceInformation.value.forEach((item, i) => {
console.log('停止', i);
createTaskQueue(i).clear();//清除队伍中的任务
@@ -1585,7 +1803,84 @@ function printCurrentTime() {
return now.toLocaleString()
}
//中途出错 → 重置应用 → 根据当前模式自动续跑同一类任务
function continueAfterReset(udid, index) {
console.error("请求重试", runType.value)
const dev = deviceInformation.value[index];
if (!dev) return;
const curUdid = dev.udid;
if (runType.value === 'follow') {
if (isMonitor.value) {
wsActions.getmesNum(curUdid, index);
} else {
LikesToLikesToLikes(curUdid, index);
}
} else if (runType.value === 'like') {
wsActions.isHost(curUdid, index);
setTimeout(() => randomSeeVideo(curUdid, index), 1000);
} else if (runType.value === 'brushLive') {
wsActions.toLive(curUdid, index);
} else if (runType.value === 'listen' || isMonitorOn.value) {
resumeAndKick(index);
}
}
function runTask(key) {
if (!scheduleEnabled.value) return
console.log('[schedule] 切换到任务:', key)
forceActivate(key, () => {
if (key === 'follow') {
runType.value = 'follow'
// 这三行保持你现有的唤起流程弹“主播ID”输入等
// showDialog.value = true
// dialogTitle.value = '主播ID'
// selectedDevice.value = 999
resetTk()
} else if (key === 'like') {
runType.value = 'like'
resetTk()
} else if (key === 'brushLive') {
runType.value = 'brushLive'
resetTk()
} else if (key === 'listen') {
runType.value = 'listen'
isMonitorOn.value = true
resetTk()
}
})
}
function startScheduleLoop() {
// 先按照当前 index 跑一次,保证“即刻对齐”
runTask(schedulePlan[scheduleState.index].key)
// 清理旧轮询,防止重复
if (scheduleTimer) clearInterval(scheduleTimer)
scheduleTimer = setInterval(() => {
if (!scheduleEnabled.value) return
const now = Date.now()
const cur = schedulePlan[scheduleState.index]
const elapsed = now - scheduleState.startTime
if (elapsed >= cur.duration) {
// 进入下一个时间片
scheduleState.index = (scheduleState.index + 1) % schedulePlan.length
scheduleState.startTime = now
localStorage.setItem('SCHEDULE_STATE', JSON.stringify(scheduleState))
runTask(schedulePlan[scheduleState.index].key)
}
}, scheduleTickMs)
}
function forceActivate(key, runner) {
// 跳过互斥逻辑,直接切换
runType.value = key;
if (typeof runner === 'function') runner();
}
</script>