From d77d50e188e26eb71e8c862ac898416dca0c5891 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 14 Jul 2023 13:30:57 +0100 Subject: [PATCH 01/10] feat: Discord Command Bot function --- discord-command-bot/.gitignore | 130 +++++++++++++++++++++++++ discord-command-bot/.prettierrc.json | 6 ++ discord-command-bot/README.md | 35 +++++++ discord-command-bot/env.d.ts | 9 ++ discord-command-bot/package-lock.json | 50 ++++++++++ discord-command-bot/package.json | 18 ++++ discord-command-bot/src/discord.js | 14 +++ discord-command-bot/src/environment.js | 17 ++++ discord-command-bot/src/main.js | 24 +++++ 9 files changed, 303 insertions(+) create mode 100644 discord-command-bot/.gitignore create mode 100644 discord-command-bot/.prettierrc.json create mode 100644 discord-command-bot/README.md create mode 100644 discord-command-bot/env.d.ts create mode 100644 discord-command-bot/package-lock.json create mode 100644 discord-command-bot/package.json create mode 100644 discord-command-bot/src/discord.js create mode 100644 discord-command-bot/src/environment.js create mode 100644 discord-command-bot/src/main.js diff --git a/discord-command-bot/.gitignore b/discord-command-bot/.gitignore new file mode 100644 index 00000000..6a7d6d8e --- /dev/null +++ b/discord-command-bot/.gitignore @@ -0,0 +1,130 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* \ No newline at end of file diff --git a/discord-command-bot/.prettierrc.json b/discord-command-bot/.prettierrc.json new file mode 100644 index 00000000..fa51da29 --- /dev/null +++ b/discord-command-bot/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "trailingComma": "es5", + "tabWidth": 2, + "semi": false, + "singleQuote": true +} diff --git a/discord-command-bot/README.md b/discord-command-bot/README.md new file mode 100644 index 00000000..b15ce1c4 --- /dev/null +++ b/discord-command-bot/README.md @@ -0,0 +1,35 @@ +# Discord Command Bot + +This function allows you to implement a simple command-interaction for a Discord bot using Discord Interactions. This bot is able to verify requests and handle them. In its current implementation, it responds to the '/hello' command with a message. + +## Environment Variables + +To ensure the function operates as intended, ensure the following variable is set: + +- **DISCORD_PUBLIC_KEY**: This is the public key of your Discord bot. + +## Discord Setup + +Before you can use this function, you need to have a bot set up on the Discord Developer Portal. You can create a new bot on the [Discord Developer Portal](https://discord.com/developers/applications) and navigate to the Bot section under the settings page of your application. Here, you can click the 'Add Bot' button. + +## Discord API & Interactions + +This function utilizes the Discord Interactions API. Interactions are the foundation upon which commands, components, and future user-input features are built. Discord provides a range of interaction types like `APPLICATION_COMMAND` which we use for our '/hello' command. + +To add an interaction to your bot, you need to define it first in the Discord Developer Portal under your application settings. Go to the 'Interactions' section and add a new command. In this case, you would add a command named 'hello'. + +## Usage + +This function supports the interaction of command type coming from Discord: + +1. **Executing the '/hello' command** + + - **Interaction Type:** APPLICATION_COMMAND + - **Command:** '/hello' + - **Response:** + - On success, the function will respond with "Hello from Appwrite 👋". + - If the command is not '/hello', the function will respond with a simple acknowledgement (PONG). + +## Error Handling + +In case of any error during interaction handling, the function will return a 500 error with the message "Failed to process interaction". \ No newline at end of file diff --git a/discord-command-bot/env.d.ts b/discord-command-bot/env.d.ts new file mode 100644 index 00000000..6198fea8 --- /dev/null +++ b/discord-command-bot/env.d.ts @@ -0,0 +1,9 @@ +declare global { + namespace NodeJS { + interface ProcessEnv { + DISCORD_PUBLIC_KEY?: string; + } + } +} + +export {}; diff --git a/discord-command-bot/package-lock.json b/discord-command-bot/package-lock.json new file mode 100644 index 00000000..12dd82fa --- /dev/null +++ b/discord-command-bot/package-lock.json @@ -0,0 +1,50 @@ +{ + "name": "discord-bot", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "discord-bot", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "discord-interactions": "^3.4.0" + }, + "devDependencies": { + "prettier": "^3.0.0" + } + }, + "node_modules/discord-interactions": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/discord-interactions/-/discord-interactions-3.4.0.tgz", + "integrity": "sha512-DG0Jxdd/FcK8liAPhIP4u5YHpnz50JWn9DK4OavxsLD49/WGimXtP3EdOY439MaWyCgQfsfFkA1GsTEyu63RzA==", + "dependencies": { + "tweetnacl": "^1.0.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/prettier": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz", + "integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + } + } +} diff --git a/discord-command-bot/package.json b/discord-command-bot/package.json new file mode 100644 index 00000000..b331aff1 --- /dev/null +++ b/discord-command-bot/package.json @@ -0,0 +1,18 @@ +{ + "name": "discord-bot", + "version": "1.0.0", + "description": "", + "main": "src/main.js", + "type": "module", + "scripts": { + "format": "prettier --write src/**/*.js" + }, + "author": "", + "license": "MIT", + "dependencies": { + "discord-interactions": "^3.4.0" + }, + "devDependencies": { + "prettier": "^3.0.0" + } +} diff --git a/discord-command-bot/src/discord.js b/discord-command-bot/src/discord.js new file mode 100644 index 00000000..bfc780f8 --- /dev/null +++ b/discord-command-bot/src/discord.js @@ -0,0 +1,14 @@ +import getEnvironment from './environment' +import { verifyKey } from 'discord-interactions' + +export default { + verifyWebhook: async function (req) { + const { DISCORD_PUBLIC_KEY } = getEnvironment() + return await verifyKey( + req.bodyString, + req.headers['x-signature-ed25519'], + req.headers['x-signature-timestamp'], + DISCORD_PUBLIC_KEY + ) + }, +} diff --git a/discord-command-bot/src/environment.js b/discord-command-bot/src/environment.js new file mode 100644 index 00000000..4e8dae78 --- /dev/null +++ b/discord-command-bot/src/environment.js @@ -0,0 +1,17 @@ +export default function getEnvironment() { + return { + DISCORD_PUBLIC_KEY: getRequiredEnv('DISCORD_PUBLIC_KEY'), + } +} + +/** + * @param {string} key + * @return {string} + */ +function getRequiredEnv(key) { + const value = process.env[key] + if (value === undefined) { + throw new Error(`Environment variable ${key} is not set`) + } + return value +} diff --git a/discord-command-bot/src/main.js b/discord-command-bot/src/main.js new file mode 100644 index 00000000..74bc3167 --- /dev/null +++ b/discord-command-bot/src/main.js @@ -0,0 +1,24 @@ +import { InteractionResponseType, InteractionType } from 'discord-interactions' +import discord from './discord' + +export default async ({ req, res, log, error }) => { + if (!(await discord.verifyWebhook(req))) { + error('Invalid request.') + return res.send('Invalid request signature', 401) + } + + const interaction = req.body + if ( + interaction.type === InteractionType.APPLICATION_COMMAND && + interaction.data.name === 'hello' + ) { + return res.json({ + type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, + data: { + content: 'Hello from Appwrite 👋', + }, + }) + } + + return res.json(InteractionResponseType.PONG) +} From 1fa57192be60ee79f938b277465b81d5098de6b3 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Mon, 17 Jul 2023 10:34:58 +0100 Subject: [PATCH 02/10] fix: esm migration --- discord-command-bot/src/discord.js | 25 ++++++++++++++----------- discord-command-bot/src/environment.js | 12 ++++++------ discord-command-bot/src/main.js | 8 ++++++-- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/discord-command-bot/src/discord.js b/discord-command-bot/src/discord.js index bfc780f8..9b26ae07 100644 --- a/discord-command-bot/src/discord.js +++ b/discord-command-bot/src/discord.js @@ -1,14 +1,17 @@ -import getEnvironment from './environment' import { verifyKey } from 'discord-interactions' -export default { - verifyWebhook: async function (req) { - const { DISCORD_PUBLIC_KEY } = getEnvironment() - return await verifyKey( - req.bodyString, - req.headers['x-signature-ed25519'], - req.headers['x-signature-timestamp'], - DISCORD_PUBLIC_KEY - ) - }, +function DiscordService(environment) { + return { + verifyWebhook: async function (req) { + const { DISCORD_PUBLIC_KEY } = environment + return await verifyKey( + req.bodyString, + req.headers['x-signature-ed25519'], + req.headers['x-signature-timestamp'], + DISCORD_PUBLIC_KEY + ) + }, + } } + +export default DiscordService diff --git a/discord-command-bot/src/environment.js b/discord-command-bot/src/environment.js index 4e8dae78..627789e0 100644 --- a/discord-command-bot/src/environment.js +++ b/discord-command-bot/src/environment.js @@ -1,9 +1,3 @@ -export default function getEnvironment() { - return { - DISCORD_PUBLIC_KEY: getRequiredEnv('DISCORD_PUBLIC_KEY'), - } -} - /** * @param {string} key * @return {string} @@ -15,3 +9,9 @@ function getRequiredEnv(key) { } return value } + +export default function EnvironmentService() { + return { + DISCORD_PUBLIC_KEY: getRequiredEnv('DISCORD_PUBLIC_KEY'), + } +} diff --git a/discord-command-bot/src/main.js b/discord-command-bot/src/main.js index 74bc3167..3cc061c9 100644 --- a/discord-command-bot/src/main.js +++ b/discord-command-bot/src/main.js @@ -1,7 +1,11 @@ import { InteractionResponseType, InteractionType } from 'discord-interactions' -import discord from './discord' +import DiscordService from './discord.js' +import EnvironmentService from './environment.js' + +export default async ({ req, res, error }) => { + const environment = EnvironmentService() + const discord = DiscordService(environment) -export default async ({ req, res, log, error }) => { if (!(await discord.verifyWebhook(req))) { error('Invalid request.') return res.send('Invalid request signature', 401) From 3057f32f2f687f56eaf858d61c3b5ad3cf665100 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Wed, 19 Jul 2023 10:59:53 +0100 Subject: [PATCH 03/10] chore: add semis --- discord-command-bot/.prettierrc.json | 2 +- discord-command-bot/package.json | 2 -- discord-command-bot/src/discord.js | 10 +++++----- discord-command-bot/src/environment.js | 8 ++++---- discord-command-bot/src/main.js | 22 +++++++++++----------- 5 files changed, 21 insertions(+), 23 deletions(-) diff --git a/discord-command-bot/.prettierrc.json b/discord-command-bot/.prettierrc.json index fa51da29..0a725205 100644 --- a/discord-command-bot/.prettierrc.json +++ b/discord-command-bot/.prettierrc.json @@ -1,6 +1,6 @@ { "trailingComma": "es5", "tabWidth": 2, - "semi": false, + "semi": true, "singleQuote": true } diff --git a/discord-command-bot/package.json b/discord-command-bot/package.json index b331aff1..493e4c21 100644 --- a/discord-command-bot/package.json +++ b/discord-command-bot/package.json @@ -7,8 +7,6 @@ "scripts": { "format": "prettier --write src/**/*.js" }, - "author": "", - "license": "MIT", "dependencies": { "discord-interactions": "^3.4.0" }, diff --git a/discord-command-bot/src/discord.js b/discord-command-bot/src/discord.js index 9b26ae07..e5080b7c 100644 --- a/discord-command-bot/src/discord.js +++ b/discord-command-bot/src/discord.js @@ -1,17 +1,17 @@ -import { verifyKey } from 'discord-interactions' +import { verifyKey } from 'discord-interactions'; function DiscordService(environment) { return { verifyWebhook: async function (req) { - const { DISCORD_PUBLIC_KEY } = environment + const { DISCORD_PUBLIC_KEY } = environment; return await verifyKey( req.bodyString, req.headers['x-signature-ed25519'], req.headers['x-signature-timestamp'], DISCORD_PUBLIC_KEY - ) + ); }, - } + }; } -export default DiscordService +export default DiscordService; diff --git a/discord-command-bot/src/environment.js b/discord-command-bot/src/environment.js index 627789e0..79cfa44c 100644 --- a/discord-command-bot/src/environment.js +++ b/discord-command-bot/src/environment.js @@ -3,15 +3,15 @@ * @return {string} */ function getRequiredEnv(key) { - const value = process.env[key] + const value = process.env[key]; if (value === undefined) { - throw new Error(`Environment variable ${key} is not set`) + throw new Error(`Environment variable ${key} is not set`); } - return value + return value; } export default function EnvironmentService() { return { DISCORD_PUBLIC_KEY: getRequiredEnv('DISCORD_PUBLIC_KEY'), - } + }; } diff --git a/discord-command-bot/src/main.js b/discord-command-bot/src/main.js index 3cc061c9..cdc08b91 100644 --- a/discord-command-bot/src/main.js +++ b/discord-command-bot/src/main.js @@ -1,17 +1,17 @@ -import { InteractionResponseType, InteractionType } from 'discord-interactions' -import DiscordService from './discord.js' -import EnvironmentService from './environment.js' +import { InteractionResponseType, InteractionType } from 'discord-interactions'; +import DiscordService from './discord.js'; +import EnvironmentService from './environment.js'; export default async ({ req, res, error }) => { - const environment = EnvironmentService() - const discord = DiscordService(environment) + const environment = EnvironmentService(); + const discord = DiscordService(environment); if (!(await discord.verifyWebhook(req))) { - error('Invalid request.') - return res.send('Invalid request signature', 401) + error('Invalid request.'); + return res.send('Invalid request signature', 401); } - const interaction = req.body + const interaction = req.body; if ( interaction.type === InteractionType.APPLICATION_COMMAND && interaction.data.name === 'hello' @@ -21,8 +21,8 @@ export default async ({ req, res, error }) => { data: { content: 'Hello from Appwrite 👋', }, - }) + }); } - return res.json(InteractionResponseType.PONG) -} + return res.json(InteractionResponseType.PONG); +}; From 887493d1b939dd4544417122f594ac6347cd9125 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Wed, 19 Jul 2023 20:50:56 +0100 Subject: [PATCH 04/10] chore: use classes --- discord-command-bot/src/discord.js | 28 +++++++++++++++----------- discord-command-bot/src/environment.js | 8 ++++---- discord-command-bot/src/main.js | 4 ++-- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/discord-command-bot/src/discord.js b/discord-command-bot/src/discord.js index e5080b7c..d22b6f0f 100644 --- a/discord-command-bot/src/discord.js +++ b/discord-command-bot/src/discord.js @@ -1,17 +1,21 @@ import { verifyKey } from 'discord-interactions'; -function DiscordService(environment) { - return { - verifyWebhook: async function (req) { - const { DISCORD_PUBLIC_KEY } = environment; - return await verifyKey( - req.bodyString, - req.headers['x-signature-ed25519'], - req.headers['x-signature-timestamp'], - DISCORD_PUBLIC_KEY - ); - }, - }; +class DiscordService { + /** + * @param {import('./environment').default} env + */ + constructor(env) { + this.env = env; + } + + async verifyWebhook(req) { + return await verifyKey( + req.bodyString, + req.headers['x-signature-ed25519'], + req.headers['x-signature-timestamp'], + this.env.DISCORD_PUBLIC_KEY + ); + } } export default DiscordService; diff --git a/discord-command-bot/src/environment.js b/discord-command-bot/src/environment.js index 79cfa44c..63712b7d 100644 --- a/discord-command-bot/src/environment.js +++ b/discord-command-bot/src/environment.js @@ -10,8 +10,8 @@ function getRequiredEnv(key) { return value; } -export default function EnvironmentService() { - return { - DISCORD_PUBLIC_KEY: getRequiredEnv('DISCORD_PUBLIC_KEY'), - }; +class EnvironmentService { + DISCORD_PUBLIC_KEY = getRequiredEnv('DISCORD_PUBLIC_KEY'); } + +export default EnvironmentService; diff --git a/discord-command-bot/src/main.js b/discord-command-bot/src/main.js index cdc08b91..7f992fbd 100644 --- a/discord-command-bot/src/main.js +++ b/discord-command-bot/src/main.js @@ -3,8 +3,8 @@ import DiscordService from './discord.js'; import EnvironmentService from './environment.js'; export default async ({ req, res, error }) => { - const environment = EnvironmentService(); - const discord = DiscordService(environment); + const env = new EnvironmentService(); + const discord = new DiscordService(env); if (!(await discord.verifyWebhook(req))) { error('Invalid request.'); From 46d32ae98bb712239fc5116743a49ddf1a5a5f94 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 20 Jul 2023 12:05:10 +0100 Subject: [PATCH 05/10] chore: restructure folders --- {discord-command-bot => node/discord-command-bot}/.gitignore | 0 .../discord-command-bot}/.prettierrc.json | 0 {discord-command-bot => node/discord-command-bot}/README.md | 0 {discord-command-bot => node/discord-command-bot}/env.d.ts | 0 .../discord-command-bot}/package-lock.json | 0 {discord-command-bot => node/discord-command-bot}/package.json | 0 {discord-command-bot => node/discord-command-bot}/src/discord.js | 0 .../discord-command-bot}/src/environment.js | 0 {discord-command-bot => node/discord-command-bot}/src/main.js | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename {discord-command-bot => node/discord-command-bot}/.gitignore (100%) rename {discord-command-bot => node/discord-command-bot}/.prettierrc.json (100%) rename {discord-command-bot => node/discord-command-bot}/README.md (100%) rename {discord-command-bot => node/discord-command-bot}/env.d.ts (100%) rename {discord-command-bot => node/discord-command-bot}/package-lock.json (100%) rename {discord-command-bot => node/discord-command-bot}/package.json (100%) rename {discord-command-bot => node/discord-command-bot}/src/discord.js (100%) rename {discord-command-bot => node/discord-command-bot}/src/environment.js (100%) rename {discord-command-bot => node/discord-command-bot}/src/main.js (100%) diff --git a/discord-command-bot/.gitignore b/node/discord-command-bot/.gitignore similarity index 100% rename from discord-command-bot/.gitignore rename to node/discord-command-bot/.gitignore diff --git a/discord-command-bot/.prettierrc.json b/node/discord-command-bot/.prettierrc.json similarity index 100% rename from discord-command-bot/.prettierrc.json rename to node/discord-command-bot/.prettierrc.json diff --git a/discord-command-bot/README.md b/node/discord-command-bot/README.md similarity index 100% rename from discord-command-bot/README.md rename to node/discord-command-bot/README.md diff --git a/discord-command-bot/env.d.ts b/node/discord-command-bot/env.d.ts similarity index 100% rename from discord-command-bot/env.d.ts rename to node/discord-command-bot/env.d.ts diff --git a/discord-command-bot/package-lock.json b/node/discord-command-bot/package-lock.json similarity index 100% rename from discord-command-bot/package-lock.json rename to node/discord-command-bot/package-lock.json diff --git a/discord-command-bot/package.json b/node/discord-command-bot/package.json similarity index 100% rename from discord-command-bot/package.json rename to node/discord-command-bot/package.json diff --git a/discord-command-bot/src/discord.js b/node/discord-command-bot/src/discord.js similarity index 100% rename from discord-command-bot/src/discord.js rename to node/discord-command-bot/src/discord.js diff --git a/discord-command-bot/src/environment.js b/node/discord-command-bot/src/environment.js similarity index 100% rename from discord-command-bot/src/environment.js rename to node/discord-command-bot/src/environment.js diff --git a/discord-command-bot/src/main.js b/node/discord-command-bot/src/main.js similarity index 100% rename from discord-command-bot/src/main.js rename to node/discord-command-bot/src/main.js From 59a627740aed912f1bc10c0f7e18c09dafa130ad Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 20 Jul 2023 12:05:33 +0100 Subject: [PATCH 06/10] chore: prettier script --- node/discord-command-bot/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/discord-command-bot/package.json b/node/discord-command-bot/package.json index 493e4c21..ad9c6f04 100644 --- a/node/discord-command-bot/package.json +++ b/node/discord-command-bot/package.json @@ -5,7 +5,7 @@ "main": "src/main.js", "type": "module", "scripts": { - "format": "prettier --write src/**/*.js" + "format": "prettier --write ." }, "dependencies": { "discord-interactions": "^3.4.0" From ee9a0ed1cf73486df9776bec095db9e97691ed26 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Sun, 30 Jul 2023 21:36:56 +0100 Subject: [PATCH 07/10] chore: new utils --- node/discord-command-bot/package-lock.json | 1 - node/discord-command-bot/src/discord.js | 21 -------------- node/discord-command-bot/src/environment.js | 17 ----------- node/discord-command-bot/src/main.js | 8 ++---- node/discord-command-bot/src/utils.js | 32 +++++++++++++++++++++ 5 files changed, 35 insertions(+), 44 deletions(-) delete mode 100644 node/discord-command-bot/src/discord.js delete mode 100644 node/discord-command-bot/src/environment.js create mode 100644 node/discord-command-bot/src/utils.js diff --git a/node/discord-command-bot/package-lock.json b/node/discord-command-bot/package-lock.json index 12dd82fa..566332f5 100644 --- a/node/discord-command-bot/package-lock.json +++ b/node/discord-command-bot/package-lock.json @@ -7,7 +7,6 @@ "": { "name": "discord-bot", "version": "1.0.0", - "license": "MIT", "dependencies": { "discord-interactions": "^3.4.0" }, diff --git a/node/discord-command-bot/src/discord.js b/node/discord-command-bot/src/discord.js deleted file mode 100644 index d22b6f0f..00000000 --- a/node/discord-command-bot/src/discord.js +++ /dev/null @@ -1,21 +0,0 @@ -import { verifyKey } from 'discord-interactions'; - -class DiscordService { - /** - * @param {import('./environment').default} env - */ - constructor(env) { - this.env = env; - } - - async verifyWebhook(req) { - return await verifyKey( - req.bodyString, - req.headers['x-signature-ed25519'], - req.headers['x-signature-timestamp'], - this.env.DISCORD_PUBLIC_KEY - ); - } -} - -export default DiscordService; diff --git a/node/discord-command-bot/src/environment.js b/node/discord-command-bot/src/environment.js deleted file mode 100644 index 63712b7d..00000000 --- a/node/discord-command-bot/src/environment.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @param {string} key - * @return {string} - */ -function getRequiredEnv(key) { - const value = process.env[key]; - if (value === undefined) { - throw new Error(`Environment variable ${key} is not set`); - } - return value; -} - -class EnvironmentService { - DISCORD_PUBLIC_KEY = getRequiredEnv('DISCORD_PUBLIC_KEY'); -} - -export default EnvironmentService; diff --git a/node/discord-command-bot/src/main.js b/node/discord-command-bot/src/main.js index 7f992fbd..ff549b74 100644 --- a/node/discord-command-bot/src/main.js +++ b/node/discord-command-bot/src/main.js @@ -1,12 +1,10 @@ import { InteractionResponseType, InteractionType } from 'discord-interactions'; -import DiscordService from './discord.js'; -import EnvironmentService from './environment.js'; +import { throwIfMissing, verifyWebhookRequest } from './utils.js'; export default async ({ req, res, error }) => { - const env = new EnvironmentService(); - const discord = new DiscordService(env); + throwIfMissing(process.env, ['DISCORD_PUBLIC_KEY']); - if (!(await discord.verifyWebhook(req))) { + if (!(await verifyWebhookRequest(req))) { error('Invalid request.'); return res.send('Invalid request signature', 401); } diff --git a/node/discord-command-bot/src/utils.js b/node/discord-command-bot/src/utils.js new file mode 100644 index 00000000..0148b724 --- /dev/null +++ b/node/discord-command-bot/src/utils.js @@ -0,0 +1,32 @@ +import { verifyKey } from 'discord-interactions'; + +/** + * Throws an error if any of the keys are missing from the object + * @param {*} obj + * @param {string[]} keys + * @throws {Error} + */ +export function throwIfMissing(obj, keys) { + const missing = []; + for (let key of keys) { + if (!(key in obj) || !obj[key]) { + missing.push(key); + } + } + if (missing.length > 0) { + throw new Error(`Missing required fields: ${missing.join(', ')}`); + } +} + +/** + * @param {*} req + * @returns {Promise} + */ +export async function verifyWebhookRequest(req) { + return await verifyKey( + req.bodyString, + req.headers['x-signature-ed25519'], + req.headers['x-signature-timestamp'], + this.env.DISCORD_PUBLIC_KEY + ); +} From 5622e59d0f67a7ffff887aa1507cad8c7f7a5878 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Mon, 31 Jul 2023 10:22:09 +0100 Subject: [PATCH 08/10] chore: env.d.ts --- node/discord-command-bot/env.d.ts | 2 +- node/discord-command-bot/src/utils.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/node/discord-command-bot/env.d.ts b/node/discord-command-bot/env.d.ts index 6198fea8..0183aadd 100644 --- a/node/discord-command-bot/env.d.ts +++ b/node/discord-command-bot/env.d.ts @@ -1,7 +1,7 @@ declare global { namespace NodeJS { interface ProcessEnv { - DISCORD_PUBLIC_KEY?: string; + DISCORD_PUBLIC_KEY: string; } } } diff --git a/node/discord-command-bot/src/utils.js b/node/discord-command-bot/src/utils.js index 0148b724..420a33ee 100644 --- a/node/discord-command-bot/src/utils.js +++ b/node/discord-command-bot/src/utils.js @@ -27,6 +27,6 @@ export async function verifyWebhookRequest(req) { req.bodyString, req.headers['x-signature-ed25519'], req.headers['x-signature-timestamp'], - this.env.DISCORD_PUBLIC_KEY + process.env.DISCORD_PUBLIC_KEY ); } From c356449c67b12a211ef9d7f36494fc3525c6c0bc Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Tue, 1 Aug 2023 11:03:46 +0100 Subject: [PATCH 09/10] docs: new readme --- node/discord-command-bot/README.md | 66 +++++++++++++++++++-------- node/discord-command-bot/src/main.js | 4 +- node/discord-command-bot/src/utils.js | 2 +- 3 files changed, 49 insertions(+), 23 deletions(-) diff --git a/node/discord-command-bot/README.md b/node/discord-command-bot/README.md index b15ce1c4..0a7b654d 100644 --- a/node/discord-command-bot/README.md +++ b/node/discord-command-bot/README.md @@ -1,35 +1,61 @@ -# Discord Command Bot +# 🤖 Discord Command Bot Function -This function allows you to implement a simple command-interaction for a Discord bot using Discord Interactions. This bot is able to verify requests and handle them. In its current implementation, it responds to the '/hello' command with a message. +Command-interaction bot for Discord. Responds to `/hello` command with a greeting. -## Environment Variables +## 🧰 Usage -To ensure the function operates as intended, ensure the following variable is set: +### `POST` -- **DISCORD_PUBLIC_KEY**: This is the public key of your Discord bot. +Webhook to receive Discord command events. To receive events, you must register your application as a [Discord bot](https://discord.com/developers/applications). -## Discord Setup +**Parameters** -Before you can use this function, you need to have a bot set up on the Discord Developer Portal. You can create a new bot on the [Discord Developer Portal](https://discord.com/developers/applications) and navigate to the Bot section under the settings page of your application. Here, you can click the 'Add Bot' button. +| Name | Description | Location | Type | Sample Value | +| --------------------- | -------------------------------- | -------- | ------ | --------------- | +| x-signature-ed25519 | Signature of the request payload | Header | string | `d1efb...aec35` | +| x-signature-timestamp | Timestamp of the request payload | Header | string | `1629837700` | -## Discord API & Interactions +**Response** -This function utilizes the Discord Interactions API. Interactions are the foundation upon which commands, components, and future user-input features are built. Discord provides a range of interaction types like `APPLICATION_COMMAND` which we use for our '/hello' command. +Sample `200` Response: -To add an interaction to your bot, you need to define it first in the Discord Developer Portal under your application settings. Go to the 'Interactions' section and add a new command. In this case, you would add a command named 'hello'. +Returns a Discord message object. -## Usage +```json +{ + "type": 4, + "data": { + "content": "Hello from Appwrite 👋" + } +} +``` -This function supports the interaction of command type coming from Discord: +Sample `401` Response: -1. **Executing the '/hello' command** +```json +{ + "error": "Invalid request signature" +} +``` - - **Interaction Type:** APPLICATION_COMMAND - - **Command:** '/hello' - - **Response:** - - On success, the function will respond with "Hello from Appwrite 👋". - - If the command is not '/hello', the function will respond with a simple acknowledgement (PONG). +## ⚙️ Configuration -## Error Handling +| Setting | Value | +| ----------------- | ------------- | +| Runtime | Node (18.0) | +| Entrypoint | `src/main.js` | +| Build Commands | `npm install` | +| Permissions | `any` | +| Timeout (Seconds) | 15 | -In case of any error during interaction handling, the function will return a 500 error with the message "Failed to process interaction". \ No newline at end of file +## 🔒 Environment Variables + +### DISCORD_PUBLIC_KEY + +Discord Public Key to verify request signature. + +| Question | Answer | +| ------------- | ---------------------------------------------------------------------------------------------------------------------- | +| Required | Yes | +| Sample Value | `d1efb...aec35` | +| Documentation | [Discord Docs](https://discord.com/developers/docs/tutorials/hosting-on-cloudflare-workers#creating-an-app-on-discord) | diff --git a/node/discord-command-bot/src/main.js b/node/discord-command-bot/src/main.js index ff549b74..52008ec9 100644 --- a/node/discord-command-bot/src/main.js +++ b/node/discord-command-bot/src/main.js @@ -6,7 +6,7 @@ export default async ({ req, res, error }) => { if (!(await verifyWebhookRequest(req))) { error('Invalid request.'); - return res.send('Invalid request signature', 401); + return res.json({ error: 'Invalid request signature' }, 401); } const interaction = req.body; @@ -22,5 +22,5 @@ export default async ({ req, res, error }) => { }); } - return res.json(InteractionResponseType.PONG); + return res.json({ type: InteractionResponseType.PONG }); }; diff --git a/node/discord-command-bot/src/utils.js b/node/discord-command-bot/src/utils.js index 420a33ee..4dba2efa 100644 --- a/node/discord-command-bot/src/utils.js +++ b/node/discord-command-bot/src/utils.js @@ -24,7 +24,7 @@ export function throwIfMissing(obj, keys) { */ export async function verifyWebhookRequest(req) { return await verifyKey( - req.bodyString, + req.bodyRaw, req.headers['x-signature-ed25519'], req.headers['x-signature-timestamp'], process.env.DISCORD_PUBLIC_KEY From 88214df573a44c9487de04221e2f428d3a7afe28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 1 Aug 2023 11:37:28 +0000 Subject: [PATCH 10/10] PR review changes --- node/discord-command-bot/README.md | 4 ++-- node/discord-command-bot/src/main.js | 11 +++++++++-- node/discord-command-bot/src/utils.js | 15 --------------- 3 files changed, 11 insertions(+), 19 deletions(-) diff --git a/node/discord-command-bot/README.md b/node/discord-command-bot/README.md index 0a7b654d..63cd9f7b 100644 --- a/node/discord-command-bot/README.md +++ b/node/discord-command-bot/README.md @@ -1,6 +1,6 @@ -# 🤖 Discord Command Bot Function +# 🤖 Node.js Discord Command Bot Function -Command-interaction bot for Discord. Responds to `/hello` command with a greeting. +Simple command using Discord Interactions. ## 🧰 Usage diff --git a/node/discord-command-bot/src/main.js b/node/discord-command-bot/src/main.js index 52008ec9..1ca15146 100644 --- a/node/discord-command-bot/src/main.js +++ b/node/discord-command-bot/src/main.js @@ -1,10 +1,17 @@ import { InteractionResponseType, InteractionType } from 'discord-interactions'; -import { throwIfMissing, verifyWebhookRequest } from './utils.js'; +import { throwIfMissing } from './utils.js'; export default async ({ req, res, error }) => { throwIfMissing(process.env, ['DISCORD_PUBLIC_KEY']); - if (!(await verifyWebhookRequest(req))) { + const verified = await verifyKey( + req.bodyRaw, + req.headers['x-signature-ed25519'], + req.headers['x-signature-timestamp'], + process.env.DISCORD_PUBLIC_KEY + ); + + if (!verified) { error('Invalid request.'); return res.json({ error: 'Invalid request signature' }, 401); } diff --git a/node/discord-command-bot/src/utils.js b/node/discord-command-bot/src/utils.js index 4dba2efa..dcca7015 100644 --- a/node/discord-command-bot/src/utils.js +++ b/node/discord-command-bot/src/utils.js @@ -1,5 +1,3 @@ -import { verifyKey } from 'discord-interactions'; - /** * Throws an error if any of the keys are missing from the object * @param {*} obj @@ -17,16 +15,3 @@ export function throwIfMissing(obj, keys) { throw new Error(`Missing required fields: ${missing.join(', ')}`); } } - -/** - * @param {*} req - * @returns {Promise} - */ -export async function verifyWebhookRequest(req) { - return await verifyKey( - req.bodyRaw, - req.headers['x-signature-ed25519'], - req.headers['x-signature-timestamp'], - process.env.DISCORD_PUBLIC_KEY - ); -}