Skip to content

Instantly share code, notes, and snippets.

@glyzinie
Created May 7, 2024 21:20
Show Gist options
  • Save glyzinie/52122bdd0d1834238f94c4121afce158 to your computer and use it in GitHub Desktop.
Save glyzinie/52122bdd0d1834238f94c4121afce158 to your computer and use it in GitHub Desktop.
import { Hono } from 'hono';
import { InteractionType, InteractionResponseType } from 'discord-api-types/v9';
// Function to convert a hex string to binary format
function hex2bin(hex: string): Uint8Array {
const buf = new Uint8Array(Math.ceil(hex.length / 2));
for (let i = 0; i < buf.length; i++) {
buf[i] = parseInt(hex.substr(i * 2, 2), 16);
}
return buf;
}
// Function to import the ED25519 public key
async function importPublicKey(publicKeyHex: string): Promise<CryptoKey> {
const publicKeyData = hex2bin(publicKeyHex);
return crypto.subtle.importKey(
'raw',
publicKeyData,
{
name: 'NODE-ED25519',
namedCurve: 'NODE-ED25519',
public: true,
},
true,
['verify']
);
}
// Function to verify the ED25519 signature
async function verifySignature(
publicKey: CryptoKey,
signature: Uint8Array,
timestamp: string,
body: string
): Promise<boolean> {
const encoder = new TextEncoder();
const data = encoder.encode(timestamp + body);
return crypto.subtle.verify(
'NODE-ED25519',
publicKey,
signature,
data
);
}
const PUBLIC_KEY_HEX = 'YOUR_DISCORD_PUBLIC_KEY_HEX';
const publicKeyPromise = importPublicKey(PUBLIC_KEY_HEX);
// Initialize the Hono app
const app = new Hono();
// Middleware to verify the request signature
app.use('/interactions', async (c, next) => {
const signature = c.req.header('X-Signature-Ed25519');
const timestamp = c.req.header('X-Signature-Timestamp');
const body = await c.req.text();
if (!signature || !timestamp) {
return c.text('Missing signature or timestamp', 401);
}
const isVerified = await verifySignature(
await publicKeyPromise,
hex2bin(signature),
timestamp,
body
);
if (!isVerified) {
return c.text('Invalid request signature', 401);
}
// Store the parsed body for use in the next middleware or handler
c.req.body = JSON.parse(body);
return next();
});
// Route handler
app.post('/interactions', async (c) => {
const data = c.req.body;
if (data.type === InteractionType.Ping) {
return c.json({ type: InteractionResponseType.Pong });
}
return c.text('Invalid request', 400);
});
// Export the fetch method of the app
export default {
fetch: app.fetch.bind(app),
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment