Lazy Termin: অ্যাপয়েন্টমেন্টের নোটিফিকেশন, যা কখনো দুবার আসে না
একটি Selenium চেকার, যা জার্মানির একটি অ্যাপয়েন্টমেন্ট বুকিং পেজ নজরে রাখে এবং PostgreSQL আউটবক্সের মাধ্যমে যাচাই করা সাবস্ক্রাইবারদের টেলিগ্রামে জানায়।
সারসংক্ষেপ
- ভূমিকা
- একক ডেভেলপার
- সময়কাল
- আগ ২০২৬ – বর্তমান
- প্রযুক্তি
- Python · Selenium · PostgreSQL · python-telegram-bot · Amazon SES · Docker Compose · GitHub Actions
41
আসল PostgreSQL-এর বিপরীতে ইউনিট টেস্ট
≤1
প্রতি কুলডাউনে সাবস্ক্রাইবারপ্রতি নোটিফিকেশন
ডিফল্ট কুলডাউন ৬০ মিনিট
3
অ্যাডমিনকে জানানোর আগে ডেলিভারির চেষ্টা
সমস্যা
জার্মানিতে অনেক সরকারি অফিস অনলাইন বুকিং পেজের মাধ্যমে অ্যাপয়েন্টমেন্ট (Termine) দেয়। খালি স্লট আসে অনিশ্চিতভাবে আর দ্রুত শেষ হয়ে যায়, তাই একটি পেতে হলে একই বহুধাপের বুকিং প্রক্রিয়া বারবার পার হতে হয়। Lazy Termin এই খোঁজার কাজটা করে দেয় এবং স্লট খালি হওয়ামাত্র জানিয়ে দেয়। বুকিং মানুষ নিজেই করেন অফিসিয়াল সাইটে।
সীমাবদ্ধতা
- ভদ্রভাবে চলা। চেকার সাধারণ বুকিং প্রক্রিয়াই অনুসরণ করে, প্রতিটি ধাপের মাঝে এলোমেলোভাবে এক থেকে তিন সেকেন্ড অপেক্ষা করে, এবং কখনো CAPTCHA, রেট লিমিট বা লগইন এড়িয়ে যায় না। নিজে কখনো কিছু বুক করে না। নির্ধারিত রান হয় কর্মদিবসে অফিস সময়ে প্রতি ১৫ মিনিটে (Europe/Berlin)।
- কোনো নোটিফিকেশন হারাবে না, দুবারও যাবে না। চেকার আর টেলিগ্রাম বট আলাদা প্রসেস, আর যেকোনোটি যেকোনো সময় রিস্টার্ট হতে পারে।
- স্প্যাম নয়। প্রতিটি সাবস্ক্রাইবার প্রতি কুলডাউনে সর্বোচ্চ একটি নোটিফিকেশন পান, ডিফল্টভাবে ৬০ মিনিট।
- ছোট ও আসল ব্যবহারকারী। সাবস্ক্রাইব করতে অনুমোদিত ডোমেইনের একটি ইমেইল ঠিকানা লাগে, যা Amazon SES-এ পাঠানো এককালীন কোড দিয়ে যাচাই হয়। সাবস্ক্রিপশনের সংখ্যা সীমিত, আর পাঁচ দিন পর মেয়াদ শেষ হয়।
- যত কম সম্ভব তথ্য। মেসেজ টেলিমেট্রিতে থাকে শুধু সময়, টেলিগ্রাম ইউজার আইডি আর মেসেজের ধরন; মেসেজের লেখা কখনো নয়।
পদ্ধতি ও আর্কিটেকচার
Docker Compose-এ cron
শিডিউল
প্রতি ১৫ মিনিটে, কর্মদিবসে ০৭:০০–১৬:৪৫, Europe/Berlin
GitHub Actions
শিডিউল
কর্মদিবসের নির্দিষ্ট সময়ে, অথবা হাতে চালু করে
Selenium বুকিং প্রক্রিয়া
চেকার
হেডলেস Chrome · প্রতিটি ধাপের মাঝে এলোমেলো ১–৩ সেকেন্ড বিরতি
- স্লট পাওয়া গেছে → INSERT + NOTIFY
notification_outbox
PostgreSQL
একসঙ্গে সর্বোচ্চ একটি অপেক্ষমাণ APPOINTMENT_FOUND সারি
- LISTEN, অথবা প্রতি ২০ সেকেন্ডে খোঁজ
SKIP LOCKED দিয়ে সারি নেওয়া
বট
কুলডাউনের বাইরে থাকা সাবস্ক্রাইবার বাছাই
প্রত্যেক যোগ্য সাবস্ক্রাইবারকে জানানো
টেলিগ্রাম
প্রতিটি ডেলিভারি alert_delivery-তে রেকর্ড · ৩ বার ব্যর্থ → অ্যাডমিন
ডায়াগ্রামের বর্ণনা
একটি শিডিউলার Selenium চেকার চালু করে, Docker Compose-এর cron দিয়ে অথবা GitHub Actions দিয়ে। চেকার খালি স্লট পেলে নোটিফিকেশন আউটবক্সে একটি APPOINTMENT_FOUND সারি যোগ করে, যদি আগে থেকে কোনোটি অপেক্ষায় না থাকে, এবং NOTIFY পাঠায়। টেলিগ্রাম বট নোটিফিকেশন পেয়ে অথবা প্রতি ২০ সেকেন্ডে খোঁজ নিয়ে জেগে ওঠে, SKIP LOCKED দিয়ে অপেক্ষমাণ সারি নিজের করে নেয়, এবং কুলডাউনের বাইরে থাকা প্রত্যেক সাবস্ক্রাইবারকে মেসেজ পাঠায়, প্রতিটি ডেলিভারি রেকর্ড করে। তিনবার ব্যর্থ হলে সারিটি ব্যর্থ হিসেবে চিহ্নিত হয় এবং অ্যাডমিনকে জানানো হয়।
/subscribe
টেলিগ্রাম
নাম, তারপর একটি ইমেইল ঠিকানা
ডোমেইন ও ধারণক্ষমতা যাচাই
বট
শুধু অনুমোদিত ডোমেইন, সক্রিয় সাবস্ক্রিপশন সীমিত
ইমেইলে এককালীন কোড
Amazon SES
/confirm
টেলিগ্রাম
পাঁচ দিনের জন্য সাবস্ক্রাইবড, তারপর নিজে থেকেই মেয়াদ শেষ
ডায়াগ্রামের বর্ণনা
ব্যবহারকারী টেলিগ্রাম বটে /subscribe পাঠিয়ে একটি নাম ও ইমেইল ঠিকানা দেন। বট যাচাই করে ডোমেইনটি অনুমোদিত কি না এবং সাবস্ক্রিপশনের সীমা পূর্ণ হয়েছে কি না, তারপর Amazon SES দিয়ে একটি এককালীন কোড পাঠায়। ব্যবহারকারী কোড নিশ্চিত করলে সাবস্ক্রিপশন পাঁচ দিনের জন্য চালু থাকে।
সিদ্ধান্ত
PostgreSQL-এ একটি ট্রানজ্যাকশনাল আউটবক্স রাখা
চেকার শুধু একটি APPOINTMENT_FOUND সারি যোগ করে (যদি আগে থেকে কোনোটি অপেক্ষায় না থাকে), আর ডেলিভারির দায়িত্ব বটের। একটি প্রসেস ব্যর্থ হলেও অন্যটির কিছু যায় আসে না।
অন্য যা ভাবা হয়েছিল
- চেকার থেকে সরাসরি টেলিগ্রাম API কল করাবট বন্ধ থাকার সময় বা লুপের মাঝে ক্র্যাশ হলে নোটিফিকেশন হারিয়ে যায় বা দুবার যায়।
সিদ্ধান্ত
LISTEN/NOTIFY দিয়ে বট জাগানো, বিকল্প হিসেবে প্রতি ২০ সেকেন্ডে খোঁজ
NOTIFY-এর কারণে ডেলিভারি প্রায় সঙ্গে সঙ্গে হয়, আর সংযোগ বিচ্ছিন্ন হয়ে কিছু ছুটে গেলে খোঁজের লুপ তা ধরে ফেলে।
অন্য যা ভাবা হয়েছিল
- Redis বা RabbitMQ-এর মতো মেসেজ ব্রোকারএকটিমাত্র কিউয়ের জন্য আরেকটি সার্ভিস চালাতে হতো, যা PostgreSQL-এই আছে।
সিদ্ধান্ত
SKIP LOCKED দিয়ে সারি নেওয়া এবং প্রতিটি ডেলিভারি রেকর্ড করা
প্রতিটি ডেলিভারি alert_delivery টেবিলে লেখা হয়, তাই আবার চেষ্টা করা সারি আগে জানানো সাবস্ক্রাইবারদের বাদ দেয়। তিনবার ব্যর্থ হলে সারিটি failed হিসেবে চিহ্নিত হয় এবং অ্যাডমিনকে জানানো হয়।
অন্য যা ভাবা হয়েছিল
- ব্যর্থ হলে পুরো পাঠানোর কাজ আবার করাযাঁরা আগেই নোটিফিকেশন পেয়েছেন, তাঁরা আবার পেতেন।
সিদ্ধান্ত
Selenium দিয়ে আসল ব্রাউজার চালানো
ছবি বন্ধ রাখা হেডলেস Chrome মেমরির ব্যবহার কম রাখে। কোনো ক্লিক আটকে গেলে জাভাস্ক্রিপ্ট ক্লিকের বিকল্প প্রক্রিয়াটি চালু রাখে।
অন্য যা ভাবা হয়েছিল
- সাধারণ HTTP রিকোয়েস্টে বুকিং প্রক্রিয়া নকল করাপ্রক্রিয়াটি কয়েকটি পেজজুড়ে চলে, নকল করতে হলে এর অবস্থা হাতে হাতে বানাতে হতো।
ফলাফল
41
আসল PostgreSQL ডেটাবেসের বিপরীতে ইউনিট টেস্ট
আউটবক্স থেকে সারি নেওয়া, পুনরায় চেষ্টা, আটকে থাকা সারি উদ্ধার, কুলডাউন, সাবস্ক্রিপশন ও Selenium-এর বিকল্প
≤1
প্রতি কুলডাউনে সাবস্ক্রাইবারপ্রতি নোটিফিকেশন
alert_delivery টেবিল দিয়ে নিশ্চিত করা
30 দিন
পর আউটবক্স ও টেলিমেট্রির সারি মুছে যায়
প্রতি রাতে বট পরিষ্কার করে
ভার্সন 1.0.0 GitHub-এ প্রকাশিত। এটি হার্টবিট হেলথ চেকসহ Docker Compose-এ চলে, অথবা GitHub Actions-এ নির্ধারিত সময়ে।
এখন হলে যা অন্যভাবে করতাম
- বুকিং প্রক্রিয়া ভেঙে গেলে সেটিকে আলাদা নোটিফিকেশন হিসেবে ধরতাম। সিলেক্টর টার্গেট পেজের ওপর নির্ভর করে, আর লেআউটের নীরব পরিবর্তন যেন কখনো "আজ কোনো অ্যাপয়েন্টমেন্ট নেই"-এর মতো না দেখায়।
- নোটিফিকেশন পৌঁছানোর সময় মাপতাম, স্লট দেখা দেওয়া থেকে মেসেজ পৌঁছানো পর্যন্ত। সাবস্ক্রাইবারদের কাছে এটাই আসল সংখ্যা।