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
Some checks failed
Deploy to 110 WOOO Server / deploy (push) Failing after 6s
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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 });
|
||||
|
||||
70
apps/web/src/lib/x-broadcaster.ts
Normal file
70
apps/web/src/lib/x-broadcaster.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user