feat(web): add X/Twitter FOMO broadcaster for high-value bounties, A2A, and speed runs
Some checks failed
Deploy to 110 WOOO Server / deploy (push) Failing after 6s

This commit is contained in:
OG T
2026-06-08 00:14:00 +08:00
parent 8c69a44b9c
commit 207c0e2151
3 changed files with 114 additions and 0 deletions

View File

@@ -11,6 +11,8 @@ import {
import { isIP } from "node:net";
import { prisma } from "@/lib/prisma";
import { runSubmissionInSandbox } from "@/lib/sandbox";
import { summarizeRequestPayload } from "@/lib/audit";
import { broadcastFomoEvent } from "@/lib/x-broadcaster";
import { logAuditEvent } from "@/lib/audit";
import { redis } from "@/lib/redis";
import { authHold, capturePayment } from "@/lib/payment";
@@ -672,6 +674,24 @@ export async function POST(request: NextRequest, props: { params: Promise<{ tool
metadata: { overall_result: result.overall_result, error_classification: result.error_classification }
});
});
// SPEED_RUN FOMO Broadcaster
if (result.overall_result === JudgeOverallResult.PASS) {
const solveTimeMs = new Date().getTime() - new Date(taskObj.created_at).getTime();
const solveTimeMinutes = Math.floor(solveTimeMs / 60000);
if (solveTimeMinutes <= 10) {
const formatted = taskObj.reward_currency === "USD"
? `$${(taskObj.reward_amount / 100).toFixed(0)}`
: `NT$${taskObj.reward_amount}`;
void broadcastFomoEvent({
type: "SPEED_RUN",
taskId: taskObj.id,
amountFormatted: formatted,
timeToSolveMinutes: Math.max(1, solveTimeMinutes)
});
}
}
}).catch(console.error);
}
}
@@ -742,6 +762,16 @@ export async function POST(request: NextRequest, props: { params: Promise<{ tool
}
});
// A2A Subcontracting FOMO
const formatted = subTask.reward_currency === "USD"
? `$${(subTask.reward_amount / 100).toFixed(0)}`
: `NT$${subTask.reward_amount}`;
void broadcastFomoEvent({
type: "A2A_SUBCONTRACT",
taskId: subTask.id,
amountFormatted: formatted
});
return NextResponse.json({
sub_task_id: subTask.id,
status: subTask.status,

View File

@@ -2,6 +2,7 @@ import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import Stripe from "stripe";
import { TaskStatus } from "@agent-bounty/contracts";
import { broadcastFomoEvent } from "@/lib/x-broadcaster";
const stripe = process.env.STRIPE_SECRET_KEY ? new Stripe(process.env.STRIPE_SECRET_KEY, {
apiVersion: "2026-05-27.dahlia",
@@ -55,6 +56,19 @@ export async function POST(request: NextRequest) {
});
console.log(`[Webhook] Task ${task.id} is now OPEN. Payment Intent: ${session.payment_intent}`);
// High-Value Bounty FOMO trigger ($50 USD = 5000 cents)
if (task.reward_amount >= 5000) {
const formatted = task.reward_currency === "USD"
? `$${(task.reward_amount / 100).toFixed(0)}`
: `NT$${task.reward_amount}`;
void broadcastFomoEvent({
type: "HIGH_VALUE_BOUNTY",
taskId: task.id,
amountFormatted: formatted
});
}
}
return NextResponse.json({ received: true });

View File

@@ -0,0 +1,70 @@
/**
* X (Twitter) FOMO Broadcaster
* 用於發送病毒式行銷推文,製造錯失恐懼 (FOMO)。
*/
import { sendTrafficAlert } from "./traffic-alert";
export type FomoEventType = "HIGH_VALUE_BOUNTY" | "SPEED_RUN" | "A2A_SUBCONTRACT";
export interface FomoEvent {
type: FomoEventType;
taskId: string;
amountFormatted: string; // e.g., "$500"
agentId?: string;
timeToSolveMinutes?: number;
}
function generateTweetText(event: FomoEvent): string {
const url = `https://agent.wooo.work/tasks/${event.taskId}`;
switch (event.type) {
case "HIGH_VALUE_BOUNTY":
return `🔥 A massive ${event.amountFormatted} AI Bounty just dropped on VibeWork! First Agent to solve it takes the cash. Can your autonomous agent beat the competition? Prove it here: ${url}`;
case "SPEED_RUN":
return `⚡️ WOW! A ${event.amountFormatted} task was just solved by an AI in exactly ${event.timeToSolveMinutes} minutes! The Agent Economy is moving at lightspeed. Is your Agent this fast? Join the arena: ${url}`;
case "A2A_SUBCONTRACT":
return `🤯 An AI just hired another AI on VibeWork to solve a ${event.amountFormatted} bug! The autonomous economy is literally building itself. Watch it happen live: ${url}`;
}
}
export async function broadcastFomoEvent(event: FomoEvent) {
const text = generateTweetText(event);
const bearerToken = process.env.TWITTER_BEARER_TOKEN;
if (!bearerToken) {
// Mock Mode: Log to console and send to internal traffic alert
console.log(`[X Broadcaster Mock] Would tweet: "${text}"`);
void sendTrafficAlert({
level: "info",
action: "FOMO_TWEET_MOCK",
surface: "x-broadcaster",
actorType: "SYSTEM",
actorId: "broadcaster",
message: `準備發布的 FOMO 推文: ${text}`,
});
return;
}
try {
// 簡單的 Fetch 實作,針對 Twitter V2 API
const response = await fetch("https://api.twitter.com/2/tweets", {
method: "POST",
headers: {
"Authorization": `Bearer ${bearerToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ text }),
});
if (!response.ok) {
const errText = await response.text();
console.error("[X Broadcaster] API Error:", errText);
} else {
console.log("[X Broadcaster] Successfully posted tweet.");
}
} catch (err) {
console.error("[X Broadcaster] Network Error:", err);
}
}