D:\project\chatape_web-knowledge\package.json
{
"dependencies": {
"puppeteer": "^23.0.0"
}
}
D:\project\chatape_web-knowledge\vercel.json
{
"crons": [
{
"path": "/api/faucet/schedule",
"schedule": "0 23 * * *"
}
]
}
D:\project\chatape_web-knowledge.env.faucet.example
# Faucet API Configuration
# Your application URL (required for scheduled tasks)
NEXT_PUBLIC_APP_URL=https://your-domain.com
# Cron secret for protecting the schedule endpoint (optional but recommended)
# Generate a random string for this value
CRON_SECRET=your-random-secret-key-here
# Example:
# NEXT_PUBLIC_APP_URL=https://chatape.vercel.app
# CRON_SECRET=abc123xyz789secretkey
D:\project\chatape_web-knowledge.github\workflows\faucet-cron.yml
name: Daily Faucet Task
on:
schedule:
# Runs at 7:00 AM Beijing time (23:00 UTC previous day)
- cron: '0 23 * * *'
workflow_dispatch: # Allows manual trigger
jobs:
run-faucet:
runs-on: ubuntu-latest
steps:
- name: Trigger Faucet Task
run: |
curl -X GET "${{ secrets.APP_URL }}/api/faucet/schedule" \
-H "Authorization: Bearer ${{ secrets.CRON_SECRET }}"
D:\project\chatape_web-knowledge\app\api\faucet\README.md
Faucet API - 自动领取 Sepolia ETH
这是一个自动化任务,每天早上7点访问 Google Cloud Faucet 页面并自动领取 Sepolia ETH。
功能特点
- ✅ 每天早上7点自动执行
- ✅ 自动填写钱包地址:
0xf0aC9747345c23B6ba451d9103F8C2785800998D
- ✅ 自动点击领取按钮
- ✅ 等待交易完成确认
- ✅ 失败自动重试(最多50次,每次间隔30秒)
安装依赖
npm install puppeteer
# 或
yarn add puppeteer
# 或
pnpm add puppeteer
环境变量配置
在 .env.local 文件中添加以下配置:
# 应用URL(用于定时任务回调)
NEXT_PUBLIC_APP_URL=https://your-domain.com
# Cron任务密钥(可选,用于保护定时任务端点)
CRON_SECRET=your-secret-key-here
API 端点
1. POST /api/faucet
手动触发领取任务
请求示例:
curl -X POST http://localhost:3000/api/faucet
响应示例:
{
"success": true,
"message": "Faucet task completed successfully",
"attempts": 1,
"timestamp": "2024-01-01T07:00:00.000Z"
}
2. GET /api/faucet
查看API状态
请求示例:
curl http://localhost:3000/api/faucet
3. GET /api/faucet/schedule
定时任务端点(由 Cron 服务调用)
请求示例:
curl -X GET http://localhost:3000/api/faucet/schedule \
-H "Authorization: Bearer your-secret-key"
部署方式
方式一:Vercel Cron(推荐)
- 部署到 Vercel
vercel.json 已配置好定时任务(每天23:00 UTC = 北京时间早上7:00)
- 在 Vercel 项目设置中添加环境变量:
NEXT_PUBLIC_APP_URL
CRON_SECRET(可选)
方式二:GitHub Actions
- 在 GitHub 仓库的 Settings > Secrets 中添加:
APP_URL: 你的应用URL
CRON_SECRET: 定时任务密钥
- GitHub Actions 会每天自动触发(
.github/workflows/faucet-cron.yml)
方式三:外部 Cron 服务
使用 cron-job.org、EasyCron 等服务,配置每天早上7点调用:
GET https://your-domain.com/api/faucet/schedule
Header: Authorization: Bearer your-secret-key
本地测试
# 启动开发服务器
npm run dev
# 手动触发任务
curl -X POST http://localhost:3000/api/faucet
注意事项
- Puppeteer 依赖:确保安装了 puppeteer 及其依赖
- 执行时间:任务可能需要几分钟完成,请确保服务器超时设置足够长
- 重试机制:如果失败会自动重试50次,每次间隔30秒
- 日志监控:建议监控日志以确保任务正常执行
故障排查
Puppeteer 安装问题
如果在服务器上运行遇到问题,可能需要安装额外的依赖:
Ubuntu/Debian:
apt-get install -y \
chromium-browser \
fonts-liberation \
libasound2 \
libatk-bridge2.0-0 \
libatk1.0-0 \
libcups2 \
libdbus-1-3 \
libdrm2 \
libgbm1 \
libgtk-3-0 \
libnspr4 \
libnss3 \
libxcomposite1 \
libxdamage1 \
libxrandr2 \
xdg-utils
Vercel 部署注意
Vercel 的 Serverless Functions 有执行时间限制:
- Hobby 计划:10秒
- Pro 计划:60秒
- Enterprise 计划:900秒
建议使用 Pro 或 Enterprise 计划,或考虑使用其他支持长时间运行的平台。
许可证
MIT
D:\project\chatape_web-knowledge\app\api\faucet\faucet-task.ts
import puppeteer, { Browser, Page } from 'puppeteer';
const TARGET_URL = 'https://cloud.google.com/application/web3/faucet/ethereum/sepolia';
const WALLET_ADDRESS = '0xf0aC9747345c23B6ba451d9103F8C2785800998D';
const MAX_RETRIES = 50;
const RETRY_DELAY = 30000; // 30 seconds
interface TaskResult {
success: boolean;
attempts: number;
error?: string;
}
export async function runFaucetTask(): Promise<TaskResult> {
let browser: Browser | null = null;
let attempts = 0;
try {
console.log('Starting faucet task...');
browser = await puppeteer.launch({
headless: true,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-accelerated-2d-canvas',
'--disable-gpu'
]
});
const page = await browser.newPage();
await page.setViewport({ width: 1920, height: 1080 });
// Navigate to the faucet page
console.log('Navigating to faucet page...');
await page.goto(TARGET_URL, {
waitUntil: 'networkidle2',
timeout: 60000
});
// Wait for page to load
await page.waitForTimeout(3000);
for (attempts = 1; attempts <= MAX_RETRIES; attempts++) {
try {
console.log(`Attempt ${attempts}/${MAX_RETRIES}`);
// Find and fill the input field
console.log('Looking for input field...');
const inputSelector = 'input[type="text"], input[placeholder*="address"], input[name*="address"]';
await page.waitForSelector(inputSelector, { timeout: 10000 });
await page.evaluate((selector: string, address: string) => {
const input = document.querySelector(selector) as HTMLInputElement;
if (input) {
input.value = address;
input.dispatchEvent(new Event('input', { bubbles: true }));
input.dispatchEvent(new Event('change', { bubbles: true }));
}
}, inputSelector, WALLET_ADDRESS);
console.log('Wallet address filled');
await page.waitForTimeout(1000);
// Find and click the "Get 0.05 Sepolia ETH" button
console.log('Looking for submit button...');
const buttonSelectors = [
'button:contains("Get 0.05 Sepolia ETH")',
'button[type="submit"]',
'button.submit',
'button'
];
let buttonClicked = false;
for (const selector of buttonSelectors) {
try {
const buttons = await page.$$('button');
for (const button of buttons) {
const text = await page.evaluate((el: Element) => el.textContent, button);
if (text && text.includes('Get 0.05 Sepolia ETH')) {
await button.click();
buttonClicked = true;
console.log('Submit button clicked');
break;
}
}
if (buttonClicked) break;
} catch (e) {
continue;
}
}
if (!buttonClicked) {
throw new Error('Could not find submit button');
}
// Wait for the success message
console.log('Waiting for transaction completion...');
const maxWaitTime = 120000; // 2 minutes
const startTime = Date.now();
while (Date.now() - startTime < maxWaitTime) {
try {
const successElement = await page.$('.drip-status-card-banner-body');
if (successElement) {
const text = await page.evaluate((el: Element) => el.textContent, successElement);
if (text && text.includes('Transaction complete! Check your wallet address')) {
console.log('✅ Transaction completed successfully!');
return {
success: true,
attempts
};
}
}
} catch (e) {
// Element not found yet, continue waiting
}
await page.waitForTimeout(2000); // Check every 2 seconds
}
// If we reach here, the transaction didn't complete in time
console.log(`Attempt ${attempts} timed out, retrying...`);
if (attempts < MAX_RETRIES) {
console.log(`Waiting ${RETRY_DELAY / 1000} seconds before retry...`);
await page.waitForTimeout(RETRY_DELAY);
// Reload the page for next attempt
await page.reload({ waitUntil: 'networkidle2' });
await page.waitForTimeout(3000);
}
} catch (error: any) {
console.error(`Error in attempt ${attempts}:`, error.message);
if (attempts < MAX_RETRIES) {
console.log(`Waiting ${RETRY_DELAY / 1000} seconds before retry...`);
await page.waitForTimeout(RETRY_DELAY);
// Reload the page for next attempt
try {
await page.reload({ waitUntil: 'networkidle2', timeout: 30000 });
await page.waitForTimeout(3000);
} catch (reloadError) {
console.error('Error reloading page:', reloadError);
// Try to navigate again
await page.goto(TARGET_URL, { waitUntil: 'networkidle2', timeout: 60000 });
await page.waitForTimeout(3000);
}
}
}
}
return {
success: false,
attempts,
error: 'Maximum retries reached without success'
};
} catch (error: any) {
console.error('Fatal error in faucet task:', error);
return {
success: false,
attempts,
error: error.message
};
} finally {
if (browser) {
await browser.close();
console.log('Browser closed');
}
}
}
D:\project\chatape_web-knowledge\app\api\faucet\schedule\route.ts
import { NextRequest, NextResponse } from 'next/server';
export const dynamic = 'force-dynamic';
// This endpoint can be called by a cron job service (like Vercel Cron, GitHub Actions, or external cron services)
export async function GET(request: NextRequest) {
try {
// Verify the request is from an authorized source (optional but recommended)
const authHeader = request.headers.get('authorization');
const cronSecret = process.env.CRON_SECRET;
if (cronSecret && authHeader !== `Bearer ${cronSecret}`) {
return NextResponse.json({
success: false,
message: 'Unauthorized'
}, { status: 401 });
}
// Check if it's the right time (7:00 AM in your timezone)
const now = new Date();
const hour = now.getHours();
console.log(`Cron job triggered at ${now.toISOString()}`);
// Trigger the faucet task
const response = await fetch(`${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/api/faucet`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
});
const result = await response.json();
return NextResponse.json({
success: true,
message: 'Scheduled task executed',
timestamp: now.toISOString(),
taskResult: result
});
} catch (error: any) {
console.error('Schedule error:', error);
return NextResponse.json({
success: false,
message: 'Schedule execution failed',
error: error.message
}, { status: 500 });
}
}
D:\project\chatape_web-knowledge\package.json
{ "dependencies": { "puppeteer": "^23.0.0" } }D:\project\chatape_web-knowledge\vercel.json
{ "crons": [ { "path": "/api/faucet/schedule", "schedule": "0 23 * * *" } ] }D:\project\chatape_web-knowledge.env.faucet.example
D:\project\chatape_web-knowledge.github\workflows\faucet-cron.yml
D:\project\chatape_web-knowledge\app\api\faucet\README.md
Faucet API - 自动领取 Sepolia ETH
这是一个自动化任务,每天早上7点访问 Google Cloud Faucet 页面并自动领取 Sepolia ETH。
功能特点
0xf0aC9747345c23B6ba451d9103F8C2785800998D安装依赖
环境变量配置
在
.env.local文件中添加以下配置:API 端点
1. POST /api/faucet
手动触发领取任务
请求示例:
响应示例:
{ "success": true, "message": "Faucet task completed successfully", "attempts": 1, "timestamp": "2024-01-01T07:00:00.000Z" }2. GET /api/faucet
查看API状态
请求示例:
3. GET /api/faucet/schedule
定时任务端点(由 Cron 服务调用)
请求示例:
curl -X GET http://localhost:3000/api/faucet/schedule \ -H "Authorization: Bearer your-secret-key"部署方式
方式一:Vercel Cron(推荐)
vercel.json已配置好定时任务(每天23:00 UTC = 北京时间早上7:00)NEXT_PUBLIC_APP_URLCRON_SECRET(可选)方式二:GitHub Actions
APP_URL: 你的应用URLCRON_SECRET: 定时任务密钥.github/workflows/faucet-cron.yml)方式三:外部 Cron 服务
使用 cron-job.org、EasyCron 等服务,配置每天早上7点调用:
本地测试
注意事项
故障排查
Puppeteer 安装问题
如果在服务器上运行遇到问题,可能需要安装额外的依赖:
Ubuntu/Debian:
Vercel 部署注意
Vercel 的 Serverless Functions 有执行时间限制:
建议使用 Pro 或 Enterprise 计划,或考虑使用其他支持长时间运行的平台。
许可证
MIT
D:\project\chatape_web-knowledge\app\api\faucet\faucet-task.ts
D:\project\chatape_web-knowledge\app\api\faucet\schedule\route.ts