چطور با استفاده از Node.js و فندق، یک بات تلگرام بسازیم؟

اگه قبلا اسم فندق Fandogh به گوشتون خورده باشه، پس احتمالا میدونین که فندق یه PaaS است که به شما این امکان رو میده که سرویس‌های خودتون رو بر روی سرورهای ابری مستقر کنید و نگران مدیریت سرورها و سرویس‌هایی که نیاز دارید نباشید.با استفاده از فندق میشه هر نوع اپلیکیشنی که قابلیت داکرایز کردن داشته باشه رو با اجرا کردن چند دستور ساده روی حالت پروداکشن قرار داد، بدون نگرانی در مورد نحوه کانفیگ سرور که واسه خودش کلی زمان میبره تا بتونین اپلیکیشنتون رو روش اجرا کنین و دیگه نگران داون شدن یهویی سرور، لود بالانسینگ، ارتقاء سخت افزار و کلی مسائل دیگه نباشین!ما امروز میخوایم یک بات تلگرام رو با استفاده از Node.js و فندق به اجرا دربیاریم، پس من اینطور در نظر میگیرم که شما با Node.js و طریقه ساخت بات های تلگرامی رو دارین. ولی خشک نبودن مقاله، یه بات ساده رو هم اینجا مینویسیم.ثبت یک بات تلگرام جدید در BotFatherقبل از هر کاری وارد بات BotFather میشیم و بعد از ثبت بات جدید، توکن رو کپی میکنیم که واسه قدم بعدی آماده باشیم ;)راه اندازی پروژهبعد از درست کردن یه پوشه توی سیستممون، یک فایل با اسم app.js ایجاد کرده و همچنین فایل env.example. ایجاد میکنیم که واسه نگهداری متغیرهای محیطی استفاده میشه و توکن تلگراممون رو اونجا ذخیره میکنیم.حالا خط پایین رو توی فایل env.example. وارد میکنیم:TELEGRAM_BOT_TOKEN=یک کپی از فایل برمیداریم و اسمشو میزاریم env. و مقدار توکن بات تلگرام رو اونجا وارد میکنیم:TELEGRAM_BOT_TOKEN=640219366:AAFLlJm76pQSiCcRMDaZ0T3fOItawjCrjfAدرسته که در حال حاضر فایل env. ما فقط یک مقدار داره و شاید بگین واسه یک مقدار لازم نیست از dotenv استفاده کنیم، ولی خدا رو چی دیدین، شاید در آینده مقیاس پروژتون بالاتر بره. اونجاس که dotenv به کمکتون میاد!نصب پکیچ های مورد نیازپکیج هایی که میخوایم نصب کنیم، اولی Telebot واسه آسان سازی نوشتن بات های تلگرام هستش و دومی dotenv واسه دسترسی به مقدار توکن تلگرام که توی .env ذخیره کردیم. سومین پکیج یعنی axios هم واسه اجرای درخواست های http ازش استفاده میشه. npm install dotenv telebot axiosشروع کدنویسیتوی فایل app.js واسه شروع کدنویسی، ابتدا پکیج هایی که نصب کرده بودیم رو وارد میکنیم:require('dotenv').config() const axios = require('axios') const Telebot = require('telebot')وقت اینکه که یه نمونه از Telebot بسازیم که بتونیم باهاش دستورات و پیام هایی که از بات تلگرام میاد رو گرفته و جوابشون رو دوباره به بات تلگراممون بفرستیم:// Named buttons to make the our bot look better const BUTTONS = { cats: { label: 'Give Me A New Cats Fact ^_^', command: '/cats' } } // Our Telebot instance which contains the bot token and a Telebot plugin namedButtons const bot = new Telebot({ // Our bot token token: process.env.TELEGRAM_BOT_TOKEN, // The plugins provided by Telebot usePlugins: ['namedButtons'], // Our config for namedButtons plugin pluginConfig: { namedButtons: { buttons: BUTTONS } } })اینجا ما یه نمونه از پکیج Telebot ساختیم و واسه کانفیگ، مقدار TELEGRAM_BOT_TOKEN در فایل env. رو بعنوان آرگومان اول بهش میدیم. همچنین واسه ساختن کلیدهای شیشه ای از پلاگین namedButtons استفاده میکنیم که ما واسه سادگی بات فقط یک کلید توی متغیر BUTTONS براش تعریف کردیم.حال اگه کاربر واسه بار اول در بات روی دکمه Start کلیک کنه، ما کد زیر رو واسه خوش آمد گویی و نشون دادن دکمه شیشه ای که در بالا تعریف کردیم مینویسیم:bot.on('/start', msg => { // Create a keyboard given the name of our button label from BUTTON variable from above const replyMarkup = bot.keyboard([ [BUTTONS.cats.label] ], {resize: true}) return bot.sendMessage( msg.chat.id, 'Welcome!\nIf you’re like me, I’m sure you’d love to know facts about cats! ?', {replyMarkup} ) })حال اگه کاربر روی کلید کلیک کنه، دستور /cats اجرا میشه. پس ما باید کد پایین رو واسه هندل کردن دستور /cats اضافه کنیم:bot.on('/cats', async (msg) => { const { data } = await axios .get('https://cat-fact.herokuapp.com/facts/random?animal_type=cat') return bot.sendMessage(msg.chat.id, data.text) })هر بار که کاربر دستور cats/ رو میزنه، ما با استفاده از axios یک درخواست به API باحال cat-facts میفرستیم واسه گرفتن حقایقی در مورد گربه ها بصورت رندوم. لول! رسپانسی که از API دریافت میکنیم رو در آخر به بات پاس میدیم! تمام.صبر کنین! هنوز واسه اجرای بات به کد پایین نیاز داریم.bot.start()اگه شما هم طبق روال بالا جلو رفته باشین، در آخر فایل app.js تون به این صورت باید باشه:require('dotenv').config() const axios = require('axios') const Telebot = require('telebot') // Named buttons to make the our bot look better const BUTTONS = { cats: { label: 'Give Me A New Cats Fact ^_^', command: '/cats' } } const bot = new Telebot({ // Our bot token token: process.env.TELEGRAM_BOT_TOKEN, // The plugins provided by Telebot usePlugins: ['namedButtons'], // Our config for namedButtons plugin pluginConfig: { namedButtons: { buttons: BUTTONS } } }) bot.on('/start', msg => { // Create a keybord given the name of our button label from BUTTON variable from above const replyMarkup = bot.keyboard([ [BUTTONS.cats.label] ], {resize: true}) return bot.sendMessage( msg.chat.id, 'Welcome!\nIf you’re like me, I’m sure you’d love to know facts about cats! ?', {replyMarkup} ) }) bot.on('/cats', async (msg) => { const { data } = await axios .get('https://cat-fact.herokuapp.com/facts/random?animal_type=cat') return bot.sendMessage(msg.chat.id, data.text) }) bot.start()نوبتی هم باشه، نوبت فندقه!بعد اینکه از اجرای درست بات تلگراممنون مطمئن شدیم، نوبت به این میرسه که بات رو داکرایز کرده و روی سرورهای فندق دیپلوی کنیم.یک فایل توی روت پروژه با اسم Dockerfile ایجاد میکنیم. داکرفایل مشخص میکنه که چه کارهایی توی کانتینر اپلیکیشن ما موقع اجرا شدن اپلیکیشن انجام داده بشه.اولین قدم ما اضافه کردن ایمیج پایه هستش که یک نقطه آغاز واسه اپلیکیشن ما به حساب میاد. بزارین از ایمیج node:10-alpine استفاده کنیم که در زمان نوشتن این مقاله نسخه LTS از Node.js به حساب میاد. ایمیج alpine نشات گرفته از پروژه Alpine Linux هستش که کمک میکنه سایز ایمیج ما به کمترین حد ممکن برسه. تو داکرفایل دستور From رو به عنوان پایه ایمیج اضافه میکنیمFROM node:10-alpineاین ایمیج شامل Node.js و npm هستش. هر داکرفایل باید با From شروع بشه.بصورت پیش فرض، واسه ایمیج Node.js تو داکر یک یوزر به اسم node تعریف شده که واسه جلوگیری از اجرا شدن کانتینر اپلیکیشنمون بعنوان root ازش استفاده کنیم که البته یک عمل امنیتی پیشنهاد شده هم به حساب میاد. پس ما از دایرکتوری node که مختص یوزر node هستش "home/node/" رو به عنوان محل قرارگیری اپلیکیشنمون استفاده میکنیم.واسه تنظیم مجوزهای دسترسی کدهای اپلیکیشنمون توی کانتینر، اجازه بدین یه دایرکتوری به اسم node_modules توی home/node/ داخل دایرکتوری app ایجاد کنیم و مالکیت این دو دایرکتوری رو به یوزر node بدیم. با اینکار مطمئن میشیم که مجوزهای لازم هنگام اجرای دستور npm install رو خواهیم داشت.... RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/appحالا محل قرارگیری اپلیکیشن رو home/node/app/ تعیین میکنیم.WORKDIR /home/node/appاگه ما WORKDIR رو دستی وارد نکنیم، داکر بصورت پیش فرض یک محل براش تعیین میکنه.حالا فایل های package.json و package-lock.json رو کپی میکنیم.COPY package*.json ./اضافه کردن دستور COPY قبل از اجرای npm install یا کپی کدهای اپلیکیشن، قابلیت بهره گیری از مکانیزم کش تو داکر رو بهمون میده. تو هر مرحله از بیلد، داکر بررسی میکنه که آیا لایه ی کش شده ای واسه دستورا داده شده وجود داره یا نه. اگه ما فایل package.json رو تغییر بدیم، داکر دوباره از این لایه بیلد میگیره. در غیر اینصورت، از همون ایمیجی که قبلا بیلد شده استفاده میکنه و دوباره npm install رو اجرا نمیکنه.حال واسه اینکه مطمئن بشیم که کاربر node مالک همه فایل های اپلیکیشنمون هست، که شامل دایرکتوری node_modules هم میشه، بهتره که به کاربر node سوئیچ کنیم.USER nodeحالا نوبت میرسه به نصب پکیج هامونRUN npm install --silentبعد از نصب پکیج ها، حالا کدهای اپلیکیشن رو تو دایرکتوری پیش فرض اپلیکیشنمون تو کانتینر کپی میکنیم. همچنین واسه اطمینان، مالکیت کدها رو هم به یوزر node تغییر میدیم.COPY --chown=node:node . .در آخر، CMD هم که واسه اجرای اپلیکیشن استفاده میشه و دو تا آرگومان میگیره. اولی که خود node هستش و دومی هم اسم فایلی که قراره توسط node اجرا بشه.CMD [ "node", "app.js" ]داکرفایل کامل به این صورته:FROM node:10-alpine RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app WORKDIR /home/node/app COPY package*.json ./ USER node RUN npm install --silent COPY --chown=node:node . . CMD [ "node", "app.js" ]بعد از ذخیره داکرفایل، میریم سراغ اضافه کردن فایل dockerignore. که شبیه gitignore. هستش. لازمه اینجا فایل ها و دایرکتوری هایی که هنگام ساختن ایمیج از اپلیکیشن نیاز نداریم رو مشخص کنیم..env fandogh.manifest.yaml /node_modules /.fandoghاگه توجه کرده باشین، هنوز ما فایل fandogh.manifest.yaml رو نداریم. این فایل به عنوان فایل کانفیگ واسه زمان دپلوی اپلیکیشنمون میتونه بهمون کمک کنه که در مانیفست سرویس میتونین بیشتر راجبش بخونین.قبل ار ایجاد این فایل، بزارین اولین ایمیج رو از اپلیکینمون بسازیم. واسه اینکار، بعد از ثبت نام در فندق و نصب CLI نوبت میرسه به ساختن ایمیج.$ fandogh login --username=YOUR_USERNAME --password=YOUR_PASSWORD $ fandogh image init --name=catsfacts $ fandogh image publish --version 0.1همونطور که توی مستندات خوندین، بعد لاگین، با دستور fandogh image init --name=catsfacts یک ایمیج با اسم catsfacts ایجاد میکنیم و در بعدش، با fandogh image publish --version v0.1 اولین ایمیج از اپلیکیشنمون رو با نسخه 0.1 پابلیش میکنیم. توجه کنین که بعد هربار تغییر در کدهای اپلیکیشن و اجرای دستور پابلیش، باید مقدار نسخه رو هم تغییر بدیم. ایمیج ما ایجاد شد، حالا واسه دپلوی ایمیج روی سرورهای فندق میتونیم از روش دپلوی، یا بهتر، از مانیفست استفاده کنیم که هربار مجبور نشیم هنگام دیپلوی آپشن هایی که لازم هست رو دوباره وارد کنیم.واسه اینکار،‌ بعد از ایجاد فایل fandogh.manifest.yaml دستورات پایین رو بهش اضافه میکنیم:kind: ExternalService name: catsfacts spec: image: catsfacts:0.1 env: - name: TELEGRAM_BOT_TOKEN value: 640219366:AAFLlJm76pQSiCcRMDaZ0T3fOItawjCrjfAاینجا ما واسه مقدار kind از ExternalService استفاده کردیم چون احتمال داره بعدا بخوایم واسه بات تلگرام وب هوک اضافه کنیم که اینطور لازمه سرویسمون از خارج از فضانام قابل دسترسی باشه.مقدار name رو هم همون catsfacts گذاشتیم که در واقع به عنوان اسم سرویسی انتخاب بشه که قراره موقع دپلوی ایمیج ایجاد بشه.و اما داخل spec دو کلید image و env رو استفاده کردیم. کلید image واسه تعیین کردن ایمیجی که میخوایم دیپلوی بشه هست که در اینجا اسم ایمیجی که قبلا درست کردم به همراه مقدار آخرین نسخه یعنی 0.1 رو براش میزارم. توجه کنین که بعد از هربار پابلیش ایمیج، مقدار آخرین نسخه ایمیج رو در این فایل هم باید تغییر بدین. کلید env رو هم مطمئنا خبر دارین که قراره چیکار کنه. اینجا ما متغیرهای محلی رو تعیین میکنیم که قبل از اجرا اپلیکیشن بهشون نیاز داریم. در اینجا من مقدار توکن بات تلگرام رو واسه متغیر TELEGRAM_BOT_TOKEN مشخص کردم.و اما در آخر، کافیه که دستور پایین رو بزنیم که ایمیجمون دیپلوی بشه!fandogh service apply -f fandogh.manifest.yamlامیدوارم این مطلب براتون مفید واقع شده باشه. ولی اگه ابهام یا سوالی دارین، خوشحال میشم اگه کمکی از دستم بر بیاد.یادتون نره اسم بات هایی که با این روش دیپلوی کردین رو اینجا کامنت کنین که ما هم استفاده کنیم ازشون!