feat: interactive bot developer documentation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
e10c1dc8aa
commit
c654ff9f73
@@ -162,3 +162,123 @@ docsRouter.get('/webhook', (c) => {
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
// POST /test — interactive webhook tester (no auth required)
|
||||
docsRouter.post('/test', async (c) => {
|
||||
const body = await c.req.json()
|
||||
const { url, type } = body as { url?: string; type?: string }
|
||||
|
||||
if (!url || typeof url !== 'string') {
|
||||
return c.json({ error: 'Missing "url" field' }, 400)
|
||||
}
|
||||
|
||||
// Basic URL validation — must be https, no private IPs
|
||||
let parsed: URL
|
||||
try {
|
||||
parsed = new URL(url)
|
||||
} catch {
|
||||
return c.json({ error: 'Invalid URL' }, 400)
|
||||
}
|
||||
if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
|
||||
return c.json({ error: 'URL must use https://' }, 400)
|
||||
}
|
||||
const host = parsed.hostname
|
||||
if (host === 'localhost' || host === '127.0.0.1' || host.startsWith('192.168.') || host.startsWith('10.') || host.endsWith('.local')) {
|
||||
return c.json({ error: 'Cannot test private/local URLs' }, 400)
|
||||
}
|
||||
|
||||
const challengeTypes: Record<string, { challenge: string; answers?: string[] }> = {
|
||||
speed_blitz: { challenge: 'What is the largest planet in our solar system?', answers: ['jupiter'] },
|
||||
math_blitz: { challenge: 'What is 17 * 23?', answers: ['391'] },
|
||||
hallucination_check: { challenge: 'True or false: The Great Wall of China is visible from space with the naked eye.', answers: ['false'] },
|
||||
roast_battle: { challenge: 'Roast your opponent who calls themselves "test_bot".' },
|
||||
creative_writing: { challenge: 'Write a haiku about a robot learning to fight.' },
|
||||
webhook_test: { challenge: 'Respond with {"answer": "pong"}', answers: ['pong'] },
|
||||
}
|
||||
|
||||
const selectedType = (type && challengeTypes[type]) ? type : 'speed_blitz'
|
||||
const ct = challengeTypes[selectedType]
|
||||
|
||||
const payload = {
|
||||
fight_id: 'test_000000',
|
||||
round: 1,
|
||||
type: selectedType,
|
||||
challenge: ct.challenge,
|
||||
constraints: { timeout_ms: 10000, max_tokens: 500 },
|
||||
opponent: { name: 'test_bot', wins: 42, losses: 10 },
|
||||
arena: 'test_arena',
|
||||
arena_modifier: null,
|
||||
}
|
||||
|
||||
const startMs = Date.now()
|
||||
try {
|
||||
const controller = new AbortController()
|
||||
const timeout = setTimeout(() => controller.abort(), 10000)
|
||||
|
||||
const res = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload),
|
||||
signal: controller.signal,
|
||||
})
|
||||
clearTimeout(timeout)
|
||||
|
||||
const elapsed = Date.now() - startMs
|
||||
|
||||
if (!res.ok) {
|
||||
return c.json({
|
||||
success: false,
|
||||
payload,
|
||||
error: `HTTP ${res.status} ${res.statusText}`,
|
||||
elapsed,
|
||||
})
|
||||
}
|
||||
|
||||
let responseBody: any
|
||||
try {
|
||||
responseBody = await res.json()
|
||||
} catch {
|
||||
return c.json({
|
||||
success: false,
|
||||
payload,
|
||||
error: 'Response is not valid JSON',
|
||||
elapsed,
|
||||
})
|
||||
}
|
||||
|
||||
const answer = responseBody?.answer
|
||||
if (typeof answer !== 'string') {
|
||||
return c.json({
|
||||
success: false,
|
||||
payload,
|
||||
response: responseBody,
|
||||
error: 'Missing "answer" field in response',
|
||||
elapsed,
|
||||
})
|
||||
}
|
||||
|
||||
// Check correctness for factual types
|
||||
let correct: boolean | null = null
|
||||
if (ct.answers) {
|
||||
const normalized = answer.toLowerCase().trim()
|
||||
correct = ct.answers.some(a => normalized.includes(a))
|
||||
}
|
||||
|
||||
return c.json({
|
||||
success: true,
|
||||
payload,
|
||||
response: responseBody,
|
||||
correct,
|
||||
elapsed,
|
||||
})
|
||||
} catch (err: unknown) {
|
||||
const elapsed = Date.now() - startMs
|
||||
const message = err instanceof Error ? err.message : 'Unknown error'
|
||||
return c.json({
|
||||
success: false,
|
||||
payload,
|
||||
error: message.includes('abort') ? 'Timeout (10s)' : message,
|
||||
elapsed,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user