Fetch vrc.tl events via Cloudflare Worker relay

vrc.tl returns 403 for the VPS IP range. Route lookups through a relay
Worker (relay/), configured via vrctl.relayUrl/relaySecret in config.json.
Also handle events without eventSlots/performers instead of crashing.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MrxnVXujrZqNrENxJZfVkU
This commit is contained in:
2026-09-30 23:45:38 +02:00
parent 0ecbf00ca2
commit bf5d61ffca
5 changed files with 45 additions and 5 deletions

10
relay/README.md Normal file
View File

@@ -0,0 +1,10 @@
# vrc.tl relay
Cloudflare Worker that proxies vrc.tl event lookups (vrc.tl blocks the VPS IP range).
Only serves `GET /events/<id>` and requires an `x-relay-secret` header.
cd relay
npx wrangler deploy
npx wrangler secret put RELAY_SECRET
Set `vrctl.relayUrl` and `vrctl.relaySecret` in the bot's config.json.

20
relay/src/index.js Normal file
View File

@@ -0,0 +1,20 @@
// Minimal relay for vrc.tl event lookups. Only GET /events/<digits>, requires secret header.
export default {
async fetch(request, env) {
if (request.method !== 'GET') return new Response('Method not allowed', { status: 405 });
if (!env.RELAY_SECRET || request.headers.get('x-relay-secret') !== env.RELAY_SECRET) {
return new Response('Unauthorized', { status: 401 });
}
const match = new URL(request.url).pathname.match(/^\/events\/(\d{1,10})$/);
if (!match) return new Response('Not found', { status: 404 });
const upstream = await fetch(`https://vrc.tl/api/v1/events/${match[1]}`, {
headers: { accept: 'application/json' },
cf: { cacheTtl: 300, cacheEverything: true },
});
return new Response(upstream.body, {
status: upstream.status,
headers: { 'content-type': upstream.headers.get('content-type') ?? 'application/json' },
});
},
};

3
relay/wrangler.toml Normal file
View File

@@ -0,0 +1,3 @@
name = "vrctl-relay"
main = "src/index.js"
compatibility_date = "2026-09-01"