const wdio = require("webdriverio");
const cmd = require('node-cmd');
const { log, MyAction, sleep } = require('../utils/baseActions')
const {
startChecking,
myDYSignUp,
searchVideo,
searchUser,
releaseVideo,
replyMessage,
groupChat,
clearCache
} = require('./douyinAction')
const { MyApi, setAccout } = require('../utils/database');
const { checkFile, clearDailyFollow, autoClearDailyFollow, getInfo } = require('../utils/localDatabase');
//这里是外部传参
const indOffset = 120
const projectInd = Number(process.argv.slice(2)[0]) + indOffset
const keepCookie = true
//基本信息
const packageName = 'com.ss.android.ugc.aweme.lite'
const activityName = 'com.ss.android.ugc.aweme.main.MainActivity'
const robotName = `robot${projectInd}`
const projectName = `douyinWork`
//创建api
const robotApi = new MyApi('bucket-robots', robotName)
const phoneListApi = new MyApi('json-bucket', 'phonelist')
//appium设置
const opts = {
path: '/',
port: 4723,
capabilities: {
"platformName": "Android",
"appium:noReset": keepCookie,
"appium:newCommandTimeout": "9999",
"appium:automationName": "UiAutomator2",
"appium:disableWindowAnimation": true,
"appium:waitForIdleTimeout": 0,
"appium:disableIdLocatorAutocompletion": true
},
logLevel: "error",
};
//任务执行设置
const taskOpt = {
bucket: 'bucket-task',
totalRetry: 86400, //获取新任务的尝试次数
taskRetry: 86400, //等待任务开始的尝试次数
totalRetryGap: 10, //单位秒,支持小数
taskRetryGap: 10, //单位秒,支持小数
}
/**
* 执行任务各个功能
* @param {*} act - MyAction实例
* @param {*} type - 执行功能的名字
* @param {*} data - 执行功能所需的数据
* @returns {string}
*/
const myCapacity = async (act, type, data) => {
let result = ''
switch (type) {
case 'searchVideo':
result = await searchVideo(act, data);
break;
case 'searchUser':
result = await searchUser(act, data);
break;
case 'releaseVideo':
result = await releaseVideo(act, data);
break;
case 'replyMessage':
result = await replyMessage(act, data);
break;
case 'groupChat':
result = await groupChat(act, data);
break;
case 'clearCache':
result = await clearCache(act);
break;
case 'clearDailyFollow':
result = await clearDailyFollow(projectName);
break;
default:
break;
}
return result
}
/**
* 获取所有任务队列文件,提取要执行的文件
* @param {Array<string>} queue - 任务对面数组
* @returns {string}
*/
const getLists = async (queue) => {
const { bucket } = taskOpt
const startApi = new MyApi(bucket, '');
const level = [
'first', 'second', '--'
]
try {
let lists = await startApi.getAllInfo();
//按级筛选任务
for (let lv = 0; lv < level.length; lv++) {
let thisLevel = lists.filter(v => v.fileName.includes(level[lv]) && v.fileName.includes('douyin') && v.fileName.includes(robotName))
if (thisLevel.length > 0) {
// console.log(level[lv], '有任务', thisLevel.length);
//筛选未做过且未标记的任务
for (let i = 0; i < thisLevel.length; i++) {
let fileName = thisLevel[i].fileName.replace('.json', '')
if (!queue.includes(fileName)) {
let fileApi = new MyApi(bucket, fileName)
let data = await fileApi.getInfo();
if (!data.missionComplete) {
log.info(`获取到任务: ${fileName}, 优先级${level[lv]}`);
return fileName
}
};
}
}
}
}
catch (err) {
console.log(err.message);
log.warning('获取所有任务队列报错');
}
return ''
}
/**
* 查看当前任务文件
* @param {string} fileName - 任务文件名
* @returns {string}
*/
const getTodoList = async (fileName) => {
const { bucket, taskRetry, taskRetryGap } = taskOpt
const startApi = new MyApi(bucket, fileName)
for (let i = 0; i < taskRetry; i++) {
try {
let data = await startApi.getInfo();
if (!data) {
log.warning(`getTodoList ==> missing data`);
}
else {
if (!data.missionStart) {
if (i % 5 === 0) log.warning(`等待开始任务, ${i}`);
}
else {
if (data.result) {
let newList = []
let arr = data.todoList
log.warning(`~~恢复任务中`);
for (let i = 0; i < arr.length; i++) {
try {
if (data.result[arr[i].type].main === 'over') {
console.log('跳过主任务', arr[i].type);
continue
}
}
catch { }
if (arr[i].type === 'searchVideo' || arr[i].type === 'replyMessage') {
let oldData = arr[i].data
let newData = {}
for (const value in oldData) {
try {
if (data.result[arr[i].type][value] === 'over') {
console.log('跳过子任务', `${arr[i].type} ===> ${value}`);
continue
}
}
catch { }
if (value === 'review') newData[value] = data.result[arr[i].type][value]
else newData[value] = oldData[value]
}
arr[i].data = newData
newList.push(arr[i])
}
else newList.push(arr[i])
}
return newList
}
return data.todoList
}
}
}
catch (err) {
if (i % 3 === 0) {
console.log(err.message);
log.warning('请求S3数据库报错');
}
}
await sleep(taskRetryGap);
}
return 'timeout'
}
/**
* 更新任务进度
* @param {string} fileName - 要更新的任务文件名
* @param {string|object|null} schedule - 要更新的任务细节
* @param {*} type - 要更新的任务文件类型
* @param {*} over - 是否是最后一项更新
*/
const updateTask = async (fileName, schedule, type, over) => {
const { bucket } = taskOpt
for (let i = 0; i < 3; i++) {
try {
const api = new MyApi(bucket, fileName)
let obj = await api.getInfo();
if (!obj) {
await sleep(10);
continue
}
if (!obj.result) {
log.info(`该任务没有完成进度,创建新的`)
obj.result = {}
}
if (over) obj.missionComplete = over;
else obj.result[type] = Object.assign({}, obj.result[type], schedule);
let updateP = await api.updateInfo(obj);
if (updateP) break
}
catch (err) {
console.log(err.message);
log.warning('请求S3数据库报错');
}
await sleep(10);
}
}
const main = async (robotStat, act, restart) => {
try {
let phonelist = await phoneListApi.getInfo();
if (!phonelist[projectInd]) return `get phonelist Fail`
else console.log('手机号信息', phonelist[projectInd]);
log.info('app ==>> appium start');
//0.启动appium打开damai
let startC = await startChecking(act, restart);
if (startC === 'error') return `startChecking Error ==>> ${startC}`
//1.注册登录
if (robotStat.step === 1) {
let phoneNumber = phonelist[projectInd].phone;
let signU = 'login Error'
if (phonelist[projectInd].msgType === 'bumoyu') {
log.info(`开始注册登录:${phoneNumber},使用自己的平台`);
signU = await myDYSignUp(act, robotStat.name, true);
}
else {
log.info(`开始注册登录:${phoneNumber},使用短信收发平台`);
// signU = await signUp(act, phoneNumber, true);
}
if (signU === 'already login') {
log.info('已有登录账号')
}
else if (signU.includes('msg Error') || signU.includes('captcha Error')) {
return signU
}
else if (signU.includes('Error')) {
return `signUp ${signU}`
}
else {
log.info(`登录完成: ${signU}`)
if (signU.includes('bmymsg')) {
const msgApi = new MyApi('bucket-message', robotName);
let msgBase = await msgApi.getInfo();
if (!msgBase) return 'mySignUp Error ==>> getRobot in the end'
msgBase.status = 'finish';
let updateMs = await msgApi.updateInfo(msgBase);
let updateAc = await setAccout(signU.split('_')[1], msgBase.taskId, 'finish')
if (!updateMs || !updateAc) return 'mySignUp Error ==>> update in the end'
log.debug('修改状态完成');
}
let newPhonelist = await phoneListApi.getInfo();
newPhonelist[projectInd].userName = signU;
newPhonelist[projectInd].platform = robotStat?.name;
let updateP = await phoneListApi.updateInfo(newPhonelist);
if (!updateP) return `phoneList Update Fail`
}
// if (robotStat?.account !== phoneNumber) log.warning('账号不一致')
robotStat.account = phoneNumber;
robotStat.step = 2;
robotStat.status = "login";
let update = await robotApi.updateInfo(robotStat);
if (!update) return `signUp Update Fail`
}
//2.执行功能
if (robotStat.step === 2) {
log.info('~~~~~~ 开始循环执行任务队列 ~~~~~~');
var queue = [], waitTime = 5, errorTimes = 0
const { totalRetry, totalRetryGap } = taskOpt
for (let i = 0; i < totalRetry; i++) {
try {
let task = await getLists(queue);
if (task) {
let todoList = await getTodoList(task);
log.notice(`开始执行任务列表, ${i} ==> ${task}`)
try {
let total = await getInfo("douyinWork", 'dailyFollow');
log.notice(`今日关注数, ${total}`)
}
catch { }
if (Array.isArray(todoList)) {
let fullComplete = true //任务无错检测
console.time('任务运行总时间')
for (let n = 0; n < todoList.length; n++) {
await autoClearDailyFollow(projectName);
//任务队列执行
let res = await myCapacity(act, todoList[n].type, todoList[n].data);
if (res.main !== 'over') {
fullComplete = false
errorTimes++
}
else {
errorTimes = 0
}
await updateTask(task, res, todoList[n].type);
log.notice(`任务执行完毕 ==> ${todoList[n].type}`);
//防崩溃检测
await act.client.pressKeyCode(8);
try {
await act.waitForElement(['text', '下线提醒'], 3000);
// await act.clickElement(['text', '好']);
log.warning('被踢下线了');
await updateTask(task, { banned: 'true' }, 'banned');
break
} catch { }
//检测休息弹窗
try {
try {
await act.clickElement(['text', '忽略提醒'], 3000);
log.warning('出现休息弹窗');
}
catch {
await act.clickElement(['desc', '忽略提醒'], 3000);
log.warning('出现休息弹窗2');
}
} catch { }
let start = await act.client.queryAppState(packageName)
if (start !== 4 || errorTimes > 2) {
log.warning(`~~程序崩溃,重启app, ${errorTimes}`);
await act.client.startActivity(packageName, activityName);
await startChecking(act, restart);
n--
}
}
console.timeEnd('任务运行总时间')
log.notice(`任务列表执行完毕, ${i} ==> ${task}`);
if (fullComplete) {
log.notice(`所有任务都完成了`);
await updateTask(task, null, null, true);
}
queue.push(task);
}
else {
log.warning('未获取到该任务');
}
}
else {
if (i % waitTime === 0) {
log.warning(`等待新任务中--${i}, 当前已完成:${queue.length}`);
if (waitTime < 80) waitTime = waitTime * 2
else if (waitTime < 180) waitTime = waitTime + 50
}
}
}
catch (err) {
console.log('~~~任务执行中断,尝试重启中~~~', err.message);
return 'Unknown Error'
}
await sleep(totalRetryGap);
}
}
return 'over'
}
catch (err) {
log.error('Main error', err)
return 'Unknown Error'
}
}
(async () => {
await checkFile(projectName);
let robotStat = await robotApi.getInfo();
if (!robotStat) {
robotStat.error = 'api getInfo error'
await robotApi.updateInfo(robotStat);
return
}
else {
robotStat.error = ''
await robotApi.updateInfo(robotStat);
}
console.log('robot信息', robotStat);
for (let running = 0; running < 5; running++) {
try {
const client = await wdio.remote(opts);
const act = new MyAction(client);
let start = await client.queryAppState(packageName)
if (start === 1) {
console.log('启动app');
await client.startActivity(packageName, activityName);
}
else if (start === 3 || start === 2) {
console.log('后台拉出app');
await client.activateApp(packageName);
}
else if (start === 4) {
if (robotStat.step < 4) {
await client.terminateApp(packageName);
await new Promise(resolve => setTimeout(resolve, 2000));
console.log('重新打开app');
await client.startActivity(packageName, activityName);
}
else { console.log('app已启动保持状态'); }
}
let runningRes = await main(robotStat, act);
log.info(runningRes);
await client.pressKeyCode(8);
if (runningRes !== 'over') {
if (runningRes.includes('restart')) {
log.warning('app卡住了,重启');
continue
}
else {
let cheakApp = await client.queryAppState(packageName);
if (cheakApp !== 4) {
log.warning('app崩溃了,重启');
continue
}
else {
log.warning('代码执行错误,请检查');
break
}
}
}
else {
log.info('all over');
break
}
}
catch (err) {
console.log(err.message);
for (let i = 0; i < 99999; i++) {
let checkAdb = await cmd.runSync(`adb devices`);
let adbData = checkAdb.data.replace("devices", "")
if (adbData.includes("device")) {
console.log(adbData);
break
}
else {
if (i % 2 === 0) { console.log('adb 断开了', i, adbData); }
}
await sleep(2);
}
log.info('程序崩溃,尝试完全重启');
}
}
})()