import { AutoRouter } from 'itty-router'; import { InteractionResponseType, InteractionType, verifyKey, } from 'discord-interactions'; import { HOT_COMMAND, ROLEPLAY_COMMAND } from './commands.js'; import { getCutePost, getRoleplayPost } from './reddit.js'; class JSONResponse extends Response { constructor(body, init) { const jsonBody = JSON.stringify(body); init = init || { headers: { 'content-type': 'application/json;charset=UTF-8', }, }; super(jsonBody, init); } } const router = AutoRouter(); function createPostEmbed(post) { const mediaUrl = post.media?.reddit_video?.fallback_url || post.secure_media?.reddit_video?.fallback_url || post.url; let description = post.selftext || ''; if (description.length > 1000) description = description.substring(0, 997).trim() + ' ...'; const embed = { title: post.title, description: description, url: `https://reddit.com${post.permalink}`, color: 0xff4500, author: { name: `u/${post.author}`, url: `https://reddit.com/u/${post.author}`, }, fields: [ { name: 'Score', value: `${post.score} ⬆️`, inline: true, }, { name: 'Comments', value: `${post.num_comments} 💬`, inline: true, }, ], timestamp: new Date(post.created_utc * 1000).toISOString(), footer: { text: 'r/okbuddyumamusume', }, }; if (mediaUrl) if (post.media?.reddit_video || post.secure_media?.reddit_video) embed.video = { url: mediaUrl }; else embed.image = { url: mediaUrl }; return embed; } router.get('/', (_request, environment) => { return new Response(`👋 ${environment.DISCORD_APPLICATION_ID}`); }); router.post('/', async (request, environment) => { const { isValid, interaction } = await server.verifyDiscordRequest( request, environment, ); if (!isValid || !interaction) return new Response('Bad request signature.', { status: 401 }); if (interaction.type === InteractionType.PING) return new JSONResponse({ type: InteractionResponseType.PONG, }); if (interaction.type === InteractionType.APPLICATION_COMMAND) { switch (interaction.data.name.toLowerCase()) { case HOT_COMMAND.name.toLowerCase(): { const post = await getCutePost(); const embed = createPostEmbed(post); return new JSONResponse({ type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, data: { embeds: [embed], }, }); } case ROLEPLAY_COMMAND.name.toLowerCase(): { const post = await getRoleplayPost(); const embed = createPostEmbed(post); return new JSONResponse({ type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, data: { embeds: [embed], }, }); } default: return new JSONResponse({ error: 'Unknown Type' }, { status: 400 }); } } console.error('Unknown Type'); return new JSONResponse({ error: 'Unknown Type' }, { status: 400 }); }); router.all('*', () => new Response('Not Found.', { status: 404 })); async function verifyDiscordRequest(request, environment) { const signature = request.headers.get('x-signature-ed25519'); const timestamp = request.headers.get('x-signature-timestamp'); const body = await request.text(); const isValidRequest = signature && timestamp && (await verifyKey( body, signature, timestamp, environment.DISCORD_PUBLIC_KEY, )); if (!isValidRequest) return { isValid: false }; return { interaction: JSON.parse(body), isValid: true }; } const server = { verifyDiscordRequest, fetch: router.fetch, }; export default server;