BAFU Calendar Bot — Bản PHP + MySQL + Cron

Tự tìm bài lịch tuần mới trên bafu.edu.vn, theo dõi thêm/sửa/hoãn, gửi tóm tắt tuần, bản tin ngày và nhắc trước 24 giờ, 1 giờ qua Telegram. Dùng chat riêng, chỉ người quản trị cấp quyền mới xem lịch qua bot.

1. Yêu cầu hosting

2. Tạo bot Telegram

  1. Trong Telegram, mở tài khoản chính thức @BotFather.
  2. Gửi /newbot; đặt tên, ví dụ Trợ lý lịch tuần BAFU.
  3. Đặt username còn trống, kết thúc bằng bot, ví dụ lich_bafu_canhan_bot.
  4. Sao chép token được cấp vào config.php. Giữ kín token.
  5. Mở bot vừa tạo và bấm Start. Bot sẽ trả lời sau khi Cron đã chạy.

3. Tạo database và upload mã nguồn

  1. Trong cPanel/DirectAdmin: tạo database, tạo user MySQL và cấp user quyền trên database đó (ALL PRIVILEGES). Lưu đủ tên có tiền tố tài khoản hosting.
  2. Giải nén thư mục bafu-telegram. Ví dụ upload vào /home/USER/public_html/lich-bafu/.
  3. Thấy đủ app, bin, public, schema.sql, .htaccess. Bật “Show Hidden Files” để thấy .htaccess.
  4. Sao chép config.example.php thành config.php, nằm ngang hàng thư mục app. Chỉnh:
<?php
return [
 'db' => [
  'host' => 'localhost',
  'port' => 3306,
  'name' => 'USER_bafubot',
  'user' => 'USER_bafuuser',
  'pass' => 'MAT_KHAU_MYSQL_CUA_BAN',
 ],
 'setup_key' => 'CHUOI_NGAU_NHIEN_TU_32_KY_TU_DO_BAN_TU_TAO',
 'telegram_token' => 'TOKEN_BOTFATHER_CUA_BAN',
 'timezone' => 'Asia/Ho_Chi_Minh',
];

setup_key là khóa cài đặt riêng, khác mật khẩu MySQL và token Telegram. Dùng chuỗi ngẫu nhiên từ 32 ký tự, không để nguyên ví dụ. Nếu mật khẩu có dấu nháy đơn, phải escape đúng trong chuỗi PHP; tốt nhất dùng mật khẩu ngẫu nhiên không chứa nháy cho cấu hình ban đầu.

Apache/LiteSpeed: có thể truy cập https://tenmiencuaban.vn/lich-bafu/public/ với .htaccess đi kèm. Không để config.php hoặc các thư mục nội bộ truy cập từ web. Quyền thông thường: thư mục 755, file 644; config.php dùng 600/640 nếu hosting hỗ trợ, không đặt 777.

Nginx hoặc cấu hình subdomain riêng: đặt Document Root trỏ vào /home/USER/.../bafu-telegram/public, các thư mục app/bin/config ở ngoài Document Root. Nginx không đọc .htaccess; bắt buộc bố trí như trên. Khi đó mở https://lich.tenmiencuaban.vn/. Đọc hướng dẫn này từ file tải về.

4. Cài đặt và đăng nhập

  1. Mở https://tenmiencuaban.vn/lich-bafu/public/install.php.
  2. Trang cài đặt kiểm tra phiên bản PHP và extension.
  3. Nhập setup_key đã đặt trong config.php và tạo mật khẩu quản trị ít nhất 12 ký tự.
  4. Bấm Cài đặt: hệ thống tự tạo bảng, không cần import SQL thủ công. schema.sql chỉ để tham khảo/khôi phục.
  5. Đăng nhập trang quản trị. Sau khi cài, install.php tự khóa, không thể cài lại ghi đè mật khẩu.

5. Cấu hình Cron Jobs — bước bắt buộc

Trong cPanel → Advanced → Cron Jobs → chọn Once Per Minute. Điền năm ô thời gian là * * * * *. Ô Command điền:

/usr/local/bin/php -q /home/USER/public_html/lich-bafu/bin/cron.php >> /home/USER/public_html/lich-bafu/var/cron.log 2>&1

Thay USER, đường dẫn thực và đường dẫn PHP theo hosting của bạn. Không dán năm dấu * vào ô Command nếu đã chọn thời gian ở năm ô phía trên. Nếu sửa crontab qua SSH, dòng đầy đủ là:

* * * * * /usr/local/bin/php -q /home/USER/public_html/lich-bafu/bin/cron.php >> /home/USER/public_html/lich-bafu/var/cron.log 2>&1

Một số hosting dùng /usr/local/bin/ea-php83 hoặc /opt/alt/php83/usr/bin/php. Hỏi nhà cung cấp hoặc kiểm tra Select PHP Version. Khi Cron không ghi được log, kiểm tra chủ sở hữu thư mục var, không mở quyền 777. Có thể bỏ phần chuyển hướng log để nhận lỗi qua email Cron trong lúc chẩn đoán. Chủ động xoay/xóa cron.log định kỳ; nhật ký database được dọn tự động.

Một Cron thực hiện toàn bộ: nhận lệnh Telegram → kiểm tra website nếu đã qua 5 phút → tạo các nhắc đến hạn → gửi hàng đợi. Nếu tác vụ cũ còn chạy, khóa MySQL ngăn tác vụ mới chạy chồng. Thời gian phản hồi lệnh thường trong chu kỳ Cron kế tiếp. Đừng thêm Cron web bằng curl/wget: bin/cron.php chỉ cho phép CLI.

6. Cấp quyền người nhận

  1. Chờ Cron chạy lần đầu. Trong quản trị, nếu bot từng dùng webhook, bấm Chuyển bot sang Cron.
  2. Trên Telegram, mở bot → Start → gửi /id.
  3. Bot trả Chat ID dạng số. Nếu chưa có phản hồi, kiểm tra Cron, token và kết nối Telegram.
  4. Quản trị → Người nhận thông báo → Thêm/cập nhật: nhập tên, Chat ID, bộ lọc, nhắc 1440,60, bản tin 06:30.
  5. Bấm Gửi thử để xác nhận kết nối; gửi /start lần nữa để hiện các nút.
  6. Bấm Kiểm tra website để tải lịch. Tin tuần mới chờ Cron gửi trong lượt tiếp theo. Nếu thêm người sau khi đã tải lịch, dùng /week để xem; bot không gửi lại bản tin tuần cũ tự động.

Muốn sửa người nhận: điền lại đúng Chat ID vào form, nhập các cấu hình mới rồi lưu. Bản đầu hỗ trợ Telegram cá nhân; chưa có nhóm/kênh, Google Calendar, nhắc 19h hôm trước, lịch xe hoặc giao diện nhập lịch thủ công.

7. Lệnh Telegram

LệnhChức năng
/todayLịch hôm nay
/tomorrowLịch ngày mai
/weekTuần hiện tại (thứ Hai–Chủ nhật theo ngày thực tế)
/filter allTheo dõi toàn trường
/filter Tên bạn;Tên đơn vị;toàn thểCó một từ khóa xuất hiện trong thành phần/chủ trì là được chọn
/remind 1440,60Nhắc trước 24 giờ và 1 giờ
/remind 60Chỉ trước 1 giờ
/remind offTắt nhắc từng lịch
/morning 06:30Bản tin hằng ngày, giờ Việt Nam
/morning offTắt bản tin ngày
/off và /onTắt/bật toàn bộ thông báo tự động, vẫn có thể xem lịch bằng lệnh
/start hoặc nút Cài đặtXem hướng dẫn và cấu hình đang dùng
/idXem Chat ID, không cấp quyền tự động

Bộ lọc giữ nguyên dấu tiếng Việt và tìm theo chuỗi, không phân biệt hoa/thường; không tự suy ra tên viết tắt hoặc nhóm của bạn. Hãy thêm các cách gọi phù hợp, ví dụ tên thường gọi, tên đơn vị, “toàn thể”, “viên chức”. Để trống là theo dõi toàn trường. Bộ lọc không tìm trong tiêu đề cuộc họp để hạn chế gửi lịch không thuộc thành phần của bạn.

8. Cách phát hiện bài mới và thay đổi

9. Chống gửi trùng và xử lý trễ

10. Kiểm tra sau cài đặt

  1. Trang quản trị hiển thị Cron gần nhất trong vòng 1–2 phút, không còn cảnh báo.
  2. Gửi thử tới Telegram thành công; các lệnh /id, /today, /week hoạt động.
  3. Nguồn tuần 42 có 10 lịch tại thời điểm kiểm tra 18/10/2026; số lượng thực tế có thể đổi sau đó. Không có lịch xe trong bảng công tác.
  4. Chọn bộ lọc cụ thể, kiểm tra lịch hiện ra khớp thành phần/chủ trì.
  5. Kiểm tra hàng đợi có trạng thái sent khi đã gửi. Không cần chỉnh website thật để thử thay đổi.

11. Xử lý lỗi thường gặp

Hiện tượngCách kiểm tra
HTTP 500PHP ≥8.1, bật extension, kiểm tra Error Log hosting, config.php thiếu dấu phẩy/nháy. Nếu “Options not allowed here”, xóa riêng dòng Options -Indexes trong .htaccess; giữ quy tắc chặn nội bộ.
MySQL không kết nốiTên DB và user có tiền tố cPanel, gán quyền, mật khẩu đúng, host/port đúng nhà cung cấp.
Cron không chạyKiểm tra đường dẫn file PHP và cron.php tuyệt đối, PHP CLI extension, log var/cron.log, phút = mỗi phút.
Telegram không trả lờiĐã Start bot, token đúng, Cron chạy, kết nối ra Telegram, bấm Chuyển bot sang Cron; dừng các chương trình khác dùng cùng bot.
getUpdates lỗi 409Còn webhook hoặc bot đang polling nơi khác. Chỉ chạy một bản cài.
Gửi lỗi 403Người nhận đã chặn bot/chưa bắt đầu chat; mở bot và Start lại.
Website chặn/timeoutKiểm tra hosting tải bafu.edu.vn được không. Không tắt SSL verification; nhờ hosting kiểm tra DNS, firewall, CA.
Không thấy lịch cá nhânThử /filter all. Bộ lọc không tự hiểu tên gọi khác hoặc chữ viết tắt.
Tuần này trống nhưng đã có tuần sau/week luôn dùng tuần hiện tại. Dùng /tomorrow hoặc xem trang quản trị; bản tin tuần mới vẫn thông báo khi bài được phát hiện.
Lịch đang chờ duyệtVào Nguồn lịch, so sánh bản đã lưu với bản website và duyệt đúng phiên bản đang hiển thị.

12. Sao lưu, bảo trì và kiểm thử

Sao lưu MySQL và config.php an toàn; không công khai token/backup. Bộ mã không cần quyền ghi vào thư mục app/public; chỉ var/cron.log cần ghi nếu bật log. Dọn log định kỳ. Dữ liệu lịch và phiên bản lưu trong DB; các log DB cũ hơn 30 ngày và tin đã xử lý cũ hơn 60 ngày được dọn tự động.

php tests/run.php

Kiểm thử độc lập gồm đọc bảng, rowspan, không đoán giờ, hoãn, qua năm mới, đối chiếu giờ/tiêu đề, bộ lọc, loại nhắc trùng và kiểm tra nguồn hợp lệ. tests/integration.php chỉ chạy trên database thử nghiệm trống có tên kết thúc _test; không chạy trên dữ liệu thật.

Mã nguồn gồm app (nghiệp vụ), public (quản trị), bin/cron.php (tác vụ định kỳ), schema.sql và config.example.php. Không có mã/mật khẩu mặc định hoặc token thật trong gói.