diff --git a/.env.example b/.env.example index dd258919..2890fa5d 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,6 @@ NODE_ENV=development LOG_LEVEL=debug BOT_TOKEN=123:ABCABCD -BOT_WEBHOOK=https://www.example.com/webhook BOT_WEBHOOK_SECRET=RANDOM_SECRET_VALUE BOT_SERVER_HOST=localhost BOT_SERVER_PORT=3000 diff --git a/.gitignore b/.gitignore index 9170b038..9506f5e8 100644 --- a/.gitignore +++ b/.gitignore @@ -135,4 +135,7 @@ data/ # Ignore SQLite database *.db -*.db-journal \ No newline at end of file +*.db-journal + +# Vercel project configuration +.vercel diff --git a/.vercelignore b/.vercelignore new file mode 100644 index 00000000..e6905a23 --- /dev/null +++ b/.vercelignore @@ -0,0 +1 @@ +.env* \ No newline at end of file diff --git a/README.md b/README.md index ca53bb61..ce83869a 100644 --- a/README.md +++ b/README.md @@ -55,28 +55,41 @@ Follow these steps to set up and run your bot using this template: npm run dev ``` - **Production Mode:** + **Production Mode:** - Install only production dependencies (no development dependencies): + Install Vercel CLI: ```bash - npm install --only=prod + npm i -g vercel ``` - Set `NODE_ENV` environment variable to `production` in your `.env` file.
- Update `BOT_WEBHOOK` with the actual URL where your bot will receive updates.
- Update `BOT_WEBHOOK_SECRET` with a random secret token. + Create a project: + ```bash + vercel link + ``` + + Set `NODEJS_HELPERS` environment variable to `0`: + ```bash + vercel env add NODEJS_HELPERS + ``` - ```dotenv - NODE_ENV=production - BOT_WEBHOOK=/webhook - BOT_WEBHOOK_SECRET= + Set `BOT_TOKEN` environment variable: + ```bash + vercel env add BOT_TOKEN --sensitive ``` - Start the bot in production mode: + Set `BOT_WEBHOOK_SECRET` environment variable to a random secret token: ```bash - npm start # with type checking (requires development dependencies) - # or - npm run start:force # skip type checking and start + vercel env add BOT_WEBHOOK_SECRET --sensitive + ``` + + Deploy your bot: + ```bash + vercel + ``` + + After successful deployment, set up a webhook to connect your Vercel app with Telegram, modify the below URL to your credentials and visit it from your browser: + ``` + https://APP_NAME.vercel.app/BOT_TOKEN ``` ### List of Available Commands @@ -337,16 +350,6 @@ bun add -d @types/bun Default depends on NODE_ENV (polling for development, webhook for production). - - BOT_WEBHOOK - - String - - - Optional in polling mode. - Webhook endpoint URL, used to configure webhook. - - BOT_WEBHOOK_SECRET diff --git a/api/index.ts b/api/index.ts new file mode 100644 index 00000000..441d71e6 --- /dev/null +++ b/api/index.ts @@ -0,0 +1,9 @@ +import { handle } from "@hono/node-server/vercel"; +import { createBot } from "#root/bot/index.js"; +import { config as configuration } from "#root/config.js"; +import { createServer } from "#root/server/index.js"; + +const bot = createBot(configuration.BOT_TOKEN); +const server = await createServer(bot); + +export default handle(server); diff --git a/src/config.ts b/src/config.ts index 0ff44397..a6c2208f 100644 --- a/src/config.ts +++ b/src/config.ts @@ -24,7 +24,6 @@ function createConfigFromEnvironment(environment: NodeJS.ProcessEnv) { }, }, BOT_TOKEN: z.string(), - BOT_WEBHOOK: z.string().default(''), BOT_WEBHOOK_SECRET: z.string().default(''), BOT_SERVER_HOST: z.string().default('0.0.0.0'), BOT_SERVER_PORT: port().default(80), @@ -35,12 +34,6 @@ function createConfigFromEnvironment(environment: NodeJS.ProcessEnv) { }) if (config.BOT_MODE === 'webhook') { - // validate webhook url in webhook mode - z.string() - .url() - .parse(config.BOT_WEBHOOK, { - path: ['BOT_WEBHOOK'], - }) // validate webhook secret in webhook mode z.string() .min(1) diff --git a/src/main.ts b/src/main.ts index 7b677b46..913b3ed7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -63,16 +63,6 @@ async function startWebhook() { ? `http://[${info.address}]:${info.port}` : `http://${info.address}:${info.port}`, }) - - // set webhook - await bot.api.setWebhook(config.BOT_WEBHOOK, { - allowed_updates: config.BOT_ALLOWED_UPDATES, - secret_token: config.BOT_WEBHOOK_SECRET, - }) - logger.info({ - msg: 'Webhook was set', - url: config.BOT_WEBHOOK, - }) } try { diff --git a/src/server/index.ts b/src/server/index.ts index 4fadd096..3e9536bf 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -46,6 +46,24 @@ export function createServer(bot: Bot) { server.get('/', c => c.json({ status: true })) + server.get(`/${bot.token}`, async (c) => { + const hostname = c.req.header("x-forwarded-host"); + if (typeof hostname === "string") { + const webhookUrl = new URL("webhook", `https://${hostname}`).href; + await bot.api.setWebhook(webhookUrl, { + allowed_updates: config.BOT_ALLOWED_UPDATES, + secret_token: config.BOT_WEBHOOK_SECRET, + }); + return c.json({ + status: true, + }); + } + c.status(500); + return c.json({ + status: false, + }); + }); + server.post( '/webhook', webhookCallback(bot, 'hono', { diff --git a/tsconfig.json b/tsconfig.json index fca6c24c..65886b8c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,6 +18,7 @@ "preserveWatchOutput": true }, "include": [ + "api/**/*", "src/**/*" ] } diff --git a/vercel.json b/vercel.json new file mode 100644 index 00000000..f96d4935 --- /dev/null +++ b/vercel.json @@ -0,0 +1,12 @@ +{ + "installCommand": "npm install", + "buildCommand": "npm run build", + "devCommand": "npm run dev", + "outputDirectory": "build", + "rewrites": [ + { + "source": "/(.*)", + "destination": "/api" + } + ] +} \ No newline at end of file