Cover photo

زولبیا و بامیه با طعم node.js ?

به نام او

خب اینسری میخوایم یکار جدیدتر انجام بدیم، یه ربات تلگرامی بسازیم که کاربر با فرستادن اسم یک شهر بتونه اوقات شرعی اون شهر رو دریافت کنه .

خب نیازمندی ها :

یک عدد api برای گرفتن اطلاعات

یک عدد توکن ربات تلگرامی

کمی آشنایی با RegEx

کمی آشنایی با نحوه خوندن داکیومنت

چیز شکن برای درست کارکردن تلگرام

داشتن خوراکی کافی برای افطار ? + ?

خب بریم که شروع کنیم :

خب اول از همه اونجایی که قراره دیتاهامون رو ازش دریافت کنیم معرفی میکنم . وبسایت آوینی.کام، و از این بخش هم میتونید توضیحات وب سرویس رو بخونید.

زیاد کار پیچیده‌ایی روبرو نداریم

 https://prayer.aviny.com/api/prayertimes/[CITY_CODE]

فقط کافیه یه ریکوست از نوع GET بفرستیم به آدرس بالا و به جای [CITY_CODE]، کد شهری که میخوایم رو قرار بدیم.

خب حالا این کدها رو از کجا بدست بیاریم. این لینک شامل تمام شهر هاست که میتونید کد هر شهر رو استخراج کنید، از اونجایی که من واقعا با توجه به شرایطی که دارم اصلا دوستندارم دونه دونه کدهارو در بیاریم یسری راهکار مضخرف پیاده سازی کردم که کد شهر ها رو و اسمشون رو در بیارم.

که توی این آدرس براتون این فایل json رو گذاشتم، الانم از این دارم میسوزم با اینکه کاری که انجام دادم یه راهکار خیلی سریع بود اما چرا اون صفحه رو یبار با دقت نخوندم که ببینم خودشون api برای اینکار دارن (?)

حالا شما هم با هرکدوم راحتترید کار کنید مهم یه خروجی مثل فایل json ایی هستش که من درست کردم .

خب پروژه‌مون رو شروع میکنیم

اول از همه یه پوشه با اسم سامانه بچه مثبت?میسازیم . و وقتی داخلش شدیم یدونه دستور

npm init -y 

رو برای ساخته شدن فایل package.json میکنیم.

بعدشم axios رو نصب میکنیم.

npm install --save axios

خب حالا فایل index.js رو هم میسازیم.

 // index.js
 const axios = require("axios");
 axios
 .get(`https://prayer.aviny.com/api/prayertimes/1`)
 .then(response => {
        console.log(response.data);
 })
 .catch(err => {
        console.log(err);
 });

خب اگه مشکلی نباشه باید توی کنسول اوقات شرعی تهران رو دریافت کنیم. ( عدد 1 متعلق به تهرانه)

خب حالا ما یکسری متد داریم و این متدها رو داخل functions.js مینویسیم.

خب فایل cities.json رو در پروژمون وارد میکنیم.

حالا functions.js رو باز میکنیم

// functions.js
const cities = require("../cities.json");
module.exports = {
    pickByCity: city => {
    const regex = new RegExp(city, "gm");
    return cities.filter(value => {
        return value.city
            .replace("(", "")
            .replace(")", "")
            .match(regex);
    });
    },
};

داخل این قطعه کد من اومدم و تابع pickByCity رو نوشتم که یک ورودی میگیره و در فایل json ما میگیرده، اگه موردی مطابق با مورد وارد شده ما پیدا شد برمیگرده.

نکات کلیدی این قطعه کد :

  • ما اگه بخوایم متغییری رو به عنوان regex در نظر بگیریم باید اول یه شی از نوع RegExp بسازیم.

  • اگه فایل json رو نگاه کرده باشید ما همچنین چیزی داریم "city": "تهران - ایران (iran, tehran)" نکته مهم اینه که علامت های ) و ( در regex مفهوم خاصی دارن ولی چون ما اون مفهوم مد نظرمون نیست میایم یه حرکت ساده میزنیم و بررسی کردنی این دو تا کاراکتر رو با هیچی replace میکنیم.

خب حالا برمیگردیم به index.js

const functions = require("./functions");
const pickByCity = functions.pickByCity;

متدی که نوشتیم رو به این صورت فراخوانی میکنیم.

حالا میایم و میگیم

let picked = pickByCity("tehran");

اساسا قطعا کد زیر باید به ما یه آرایه برگردونه که داخل اون آبجکتی هست که متعلق به تهرانه و ما در مرحله بعد به سادگی بیایم و قطعه کدی که نوشتیم رو تغییر بدیم به :

axios
 .get(`https://prayer.aviny.com/api/prayertimes/${picked[0].code}`)
 .then(response => {
        console.log(response.data);
 })
 .catch(err => {
        console.log(err);
 });

اما به یه نکته ایی توجه نکردیم و اونم اینکه ممکنه تعداد مکان های بیشتری باشن که متن tehran رو شامل بشن در نتیجه ما تعدادی رکورد داریم و نه یدونه، درسته قطعه کد بالا الانم درست کار میکنه اما شاید منظور کاربر اون الزاما مورد اول نباشه (چون ما index رو تو آرایه برابر با صفر گذاشتیم) و شاید اصلا شهری با اون اسمی که کاربر وارد کرده نداشته باشیم (روستای دیو های زشت در نقطه کور شمالی مرز ایران با چین ??)

مثلا برای تهران :

 [     { "code": "1", "city": "تهران - ایران (iran, tehran)"    },      { "code": "1024", "city": "تهران - برج آزادی - ایران (iran, tehran - azadi tower)"    },      { "code": "1061", "city": "تهرانپارس - ایران (iran, tehran pars)"    },      { "code": "1835", "city": "تهرانسر - ایران (iran, tehransar)"    },  ]

رو داریم.

خب پس سه تا حالت پیش میاد

if (picked.length < 1) { .... }
if (picked.length === 1)  { .... }
if (picked.length > 1)  { .... }

که ما قطعه کد

axios
 .get(`https://prayer.aviny.com/api/prayertimes/${picked[0].code}`)
 .then(response => {
        console.log(response.data);
        })
 .catch(err => {
        console.log(err);
 });

رو فقط داخل حالت وسط مینویسم، دوتا حالت دگ رو هم وقتی داریم رباتمون رو مینویسیم درست میکنم.

خب گام بعدی اتصال ربات تلگرامی، برای اینکار از پکیج node-telegram-bot-api استفاده میکنیم.

خب حالا نصبش میکنیم

npm install --save  node-telegram-bot-api

بعد داخل index.js

const TelegramBot = require("node-telegram-bot-api");

const token = "YOUR_TOKEN";
const bot = new TelegramBot(token, { polling: true });

bot.onText(/(.+)/, (msg, match) => {
 const chatId = msg.chat.id;
 const resp = match[1]
 bot.sendMessage(chatId, resp);
});

این قطعه کد میگه هر متنی که کاربر به ربات ما فرستاد ما هم همونو براش بفرستیم.

خب حالا قطعه کد بالا رو قسمت onText رو به شکل زیر تغییر میدیم

bot.onText(/(.+)/, (msg, match) => {
 const chatId = msg.chat.id;
 const resp = match[1]
 .toLowerCase()
 .replace("(", "")
 .replace(")", "");

 if (resp === "/start") return bot.sendMessage(chatId, "چاکریم");

 let picked = pickByCity(resp);

 if (picked.length < 1)
 bot.sendMessage(chatId, "نام استان یا شهر خود را با دقت وارد نمایید");

 if (picked.length === 1)
 axios
 .get(`https://prayer.aviny.com/api/prayertimes/${picked[0].code}`)
 .then(response => {
        console.log(response.data);
 bot.sendMessage(chatId, JSON.stringify(response.data));
 })
 .catch(err => {
        console.log(err);
 });
 });

تمامی نکات تا به اینجا قبلا بحث شده فقط میمونه .

زمانی که تعداد داده های ما بیشتر از یک عه ! اینجا میتونیم بیایم از دکمه هایی که در ربات ها تعبیه شده استفاده کنیم

قطعه کد پایین رو به کد بالا اضافه میکنیم (قبل از آخرین }); )

if (picked.length > 1) {
 let keyboardArray = [];
 for (let i = 0; i < picked.length; i++) {
 let item = [];
 item.push({ text: picked[i].city });
 keyboardArray.push(item);
 }

 let options = {
      parse_mode: "Markdown",
      reply_markup: JSON.stringify({
        keyboard: keyboardArray,
        one_time_keyboard: true,
        resize_keyboard:true
 })
 };
 bot.sendMessage(chatId, "منطقه خود را انتخاب کنید", options);
 }

تو اینجا options همون دکمه های ما هستن، ما باید دکمه های خودمون رو به صورت آرایه در مقابل keyboard بنویسیم.

[// Array of Buttons    [{}], // Btn One
    [{}] // Btn Two
]

خب تقریبا به جایی رسیدیم که باید بگیم و من الله التوفیق ....

اما اگه شما ربات رو تست کنید یسری پیامای داغون براتون میفرسته با اعداد و محتوای انگلیسی .

برای رفع این مشکل وارد functions.js میشیم و اونو به صورت زیر تغییر میدیم

const cities = require("../cities.json");

const numbers = {
 "0": "۰",
 "1": "۱",
 "2": "۲",
 "3": "۳",
 "4": "۴",
 "5": "۵",
 "6": "۶",
 "7": "۷",
 "8": "۸",
 "9": "۹"
};
const persianNumbers = input => {
 return String(input)
 .split("")
 .map(char => (char in numbers ? numbers[char] : char))
 .join("");
};

module.exports = {
 pickByCity: city => {
 const regex = new RegExp(city, "gm");
 return cities.filter(value => {
 return value.city
 .replace("(", "")
 .replace(")", "")
 .match(regex);
 });
 },
 beautifulMessage: msg => {
 let { CityName,Imsaak,Sunrise,Noon,Sunset,Maghreb,Midnight,Today,TodayQamari} = msg;
 let text = `شهر : ${CityName}\nاذان صبح : ${Imsaak}\nطلوع آفتاب : ${Sunrise}\nظهر : ${Noon}\nغروب آفتاب : ${Sunset}\nاذان مغرب : ${Maghreb}\nنیمه شب : ${Midnight}\nتاریخ امروز : ${Today}\nتاریخ قمری : ${TodayQamari}\n`;
 return persianNumbers(text);
 }
};

خب حالا وارد index.js میشیم و تابعی که جدیدا نوشتیم ور فراخوانی میکنیم

 const beautifulMessage = functions.beautifulMessage;

بعد خط

  bot.sendMessage(chatId, JSON.stringify(response.data)); 

با خط

bot.sendMessage(chatId, beautifulMessage(response.data));

جایگزین میکنیم

و تاماممممممم ..... کدهارو هم تا چند روز دگ میتونید تو گیت هابم پیدا کنید.