import * as fs from "fs"; import axios from "axios"; import { chromium } from 'playwright' (async () => { const browser = await chromium.launch({ headless: true // 👈 Windows建议先开界面调试 }); // 👇 关键就在这里 let context = null if (fs.existsSync('state.json')) { console.log('✅ state.json 存在'); context = await browser.newContext({ storageState: 'state.json', locale: "zh-CN" }); } else { console.log('❌ state.json 不存在'); context = await browser.newContext({ locale: "zh-CN" }) } const page = await context.newPage(); await page.goto('https://gitcode.com/setting/points'); await page.waitForTimeout(5000); const isLogin = await page.locator('.g-user-avatar').isVisible(); if (isLogin) { console.log('✅ 已登录'); runTasks(page) } else { console.log('❌ 未登录'); await page.getByText('密码登录').click(); await page.getByPlaceholder('请填写手机号/用户名/邮箱').fill('test'); await page.getByPlaceholder('请填写密码').fill('test'); await page.getByText('登录或注册完成代表你同意').click(); await page.getByText('我同意将账号、组织、仓库等信息提供给AtomGit进行 ').click(); await page.locator('button:has-text("登 录")').click(); await page.waitForTimeout(1000); await context.storageState({ path: 'state.json' }); runTasks(page) } })(); async function runTasks(page) { console.log('✅ 开始寻找每日任务'); const tasks = await page.locator('.novice-task-item-box').all(); console.log('✅ 共找到' + tasks.length + '个每日任务'); for (const task of tasks) { const title = await task.locator('.task-card-title__label-text').innerText(); if (title.includes("更新项目")) { const taskStat = await taskIsCompleted(task) if (!taskStat) { await doUpdateProject(page) } console.log('✅ 更新项目已完成'); } else if (title.includes("每日分享")) { const taskStat = await taskIsCompleted(task) if (!taskStat) { await dailyShare(task, page) } console.log('✅ 每日分享已完成'); } else if (title.includes("每日签到")) { const taskStat = await taskIsCompleted(task) if (!taskStat) { await dailyCheckInTask(task, page) } console.log('✅ 每日签到已完成'); } else if (title.includes("查看热门")) { const taskStat = await taskIsCompleted(task) if (!taskStat) { await dailyViewTrending(task, page) } console.log('✅ 查看热门已完成'); } else if (title.includes("每日Star")) { const taskStat = await taskIsCompleted(task) if (!taskStat) { await dailyViewStar(task, page) } console.log('✅ 每日Star已完成'); } } await page.waitForTimeout(2000); await page.reload({ waitUntil: "domcontentloaded" }); await page.waitForTimeout(2000); await page.locator('button.pickup-btn:has-text("一键领取")').click(); console.log('✅ 已完成所有任务,奖励已领取 , 开始寻找卡片, 判断所有是否完成和领取'); const tasks2 = await page.locator('.novice-task-item-box').all(); console.log('✅ 共找到' + tasks2.length + '个每日任务'); for (const task of tasks2) { const isCompleted = await task.locator('.completed').count() > 0; const title = await task.locator('.task-card-title__label-text').innerText(); console.log('✅ ' + title + ' 任务完成状态: ' + (isCompleted ? '已完成' : '未完成')); } await page.waitForTimeout(1000); await sendFeishuText('✅ 已完成所有任务,奖励已领取'); process.exit(0) } async function taskIsCompleted(task) { const rewardBtn = task.locator('button:has-text("领取奖励")'); // 如果存在领取奖励按钮 -> 说明任务已完成但奖励未领取 if (await rewardBtn.count() > 0) { console.log('🎁 检测到可领取奖励,正在领取...'); await rewardBtn.click(); // 等待领取按钮消失 or completed 出现 await task.page().waitForTimeout(800); const stillHasRewardBtn = await rewardBtn.count() > 0; const isCompleted = await task.locator('.completed').count() > 0; if (!stillHasRewardBtn || isCompleted) { console.log('✅ 奖励领取成功'); return true; } console.log('⚠️ 已点击领取奖励,但状态未变化,可能领取失败或延迟刷新'); return false; } // 已完成标记 const isCompleted = await task.locator('.completed').count() > 0; if (isCompleted) { console.log('✅ 已完成,跳过'); return true; } return false; } async function doUpdateProject(page) { console.log('👉 执行项目更新'); const context = page.context(); const newPage = await context.newPage(); await newPage.goto('https://gitcode.com/fzxlzy/test/edit/main/testjpgp11'); // 你可以自己补逻辑 const frame = newPage.frameLocator('#codeartside'); // 1️⃣ 点击编辑器 await frame.locator('.monaco-editor').click({ position: { x: 200, y: 100 } }); // 2️⃣ 把光标移动到末尾(关键!) await newPage.keyboard.press('Control+End'); // 3️⃣ 追加内容 await newPage.keyboard.type(`\n测试图片生成功能 11 22 33`, { delay: 50 }); // 等按钮出现 await newPage.locator('button:has-text("提交修改")').click(); await page.waitForTimeout(1000); const submitButton = newPage.locator('button:has-text("提交修改")').nth(1); // 0 是页面按钮,1 是弹框按钮 await submitButton.click(); await newPage.waitForTimeout(500); await newPage.close(); } async function dailyShare(task, page) { // 等待新页面打开 const [newPage] = await Promise.all([ page.context().waitForEvent('page'), // 等待新页面事件 task.locator('.toComplete').click() ]); // 点击第一个 await newPage.waitForTimeout(1000); await newPage.locator('.immediately-invite-btn').nth(0).click(); await newPage.waitForTimeout(2000); await newPage.close(); } async function dailyCheckInTask(task, page) { await page.waitForTimeout(1000); task.locator('button:has-text("签到")').click() await page.waitForTimeout(1000); } async function dailyViewTrending(task, page) { // 等待新页面打开 const [newPage] = await Promise.all([ page.context().waitForEvent('page'), // 等待新页面事件 task.locator('.toComplete').click() ]) await newPage.waitForSelector('.hot-selection .recommend-card'); // 获取第一个热门项目卡片 const firstHotProjectCard = await newPage.$('.hot-selection .recommend-card'); await newPage.waitForTimeout(1000); // 点击它 const [newPage2] = await Promise.all([ newPage.context().waitForEvent('page'), // 等待新页面事件 await firstHotProjectCard.click() ]) await newPage2.waitForTimeout(2000); newPage2.close() newPage.close() } async function dailyViewStar(task, page) { // 等待新页面打开 const [newPage] = await Promise.all([ page.context().waitForEvent('page'), // 等待新页面事件 task.locator('.toComplete').click() ]) // 等待元素出现在 DOM(不要用 visible,容易超时) await newPage.waitForSelector('.hot-selection-list a.recommend-card', { state: 'attached' }); const cards = newPage.locator('.hot-selection-list a.recommend-card'); const count = await cards.count(); console.log("热门项目数量:", count); if (count === 0) { throw new Error("没有找到热门项目"); } await newPage.waitForTimeout(2000); // 随机选一个 const index = Math.floor(Math.random() * count); console.log("随机选择 index:", index); // 点击并捕获新 tab const [newPage2] = await Promise.all([ newPage.context().waitForEvent('page'), cards.nth(index).click() ]); // 等待 Star 按钮出现 const starBtn = newPage2.locator('#repo-header-tab .status-btn').filter({ hasText: 'Star' }).nth(1); await newPage2.waitForTimeout(2000); await starBtn.waitFor({ state: 'visible' }); // 判断是否已 Starred const text = await starBtn.innerText(); await newPage2.waitForTimeout(2000); if (text.includes('Starred')) { // 已经 Starred -> 先取消 await starBtn.click(); // 等待变回 Star await newPage2.waitForTimeout(500); } await starBtn.click(); await newPage2.waitForTimeout(2000); newPage2.close() newPage.close() } async function sleep(ms) { return new Promise(r => setTimeout(r, ms)); } async function sendFeishuText(text) { const webhook = process.env.FEISHU_WEBHOOK; if (!webhook) { console.log('FEISHU_WEBHOOK 未配置,跳过飞书通知'); return; } const payload = { msg_type: "text", content: { text } }; for (let i = 1; i <= 5; i++) { try { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 10000); // 10秒超时 const res = await fetch(webhook, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), signal: controller.signal }); clearTimeout(timeout); const data = await res.text(); console.log("Feishu response:", data); return; } catch (err) { console.error(`Feishu send failed (attempt ${i})`, err); await sleep(3000); } } throw new Error("Feishu webhook failed after retries"); }