Official Telegram bot of PowerKids Kindergarten. Handles communication to parents from the school.
- Admins/Teachers can send broadcast messages or weekly pictures (from the convenience of their own mobile devices) and select relevant student names
- The bot will match the each selected student to their parents' Telegram account via the school's internal database
- Parents will receive those pictures, sent by the bot, instantly notified by Telegram's mobile push notifications
- Users can effortlessly authenticate their unique identity & role with deeplinks
- No need for school staff to pass around a single company phone for communication
- Parallel communication increases efficiency
- School staff can handle their tasks according to their own schedule, not based on the common phone's availability
- Database replaces the manual record of parents' contacts to students' names
- Enforces two-way anonymity and privacy because personal devices are obscured by the database layer,
- Reduces chance for human error, sending message to the wrong parent
- Centralizes single source of data
- Ensures freshest data from database
- Instant push notifications on parents' mobile phones
- Quicker time-to-notification
- Flexible target listing
- Old system with WhatsApp was to either manually select contacts, or to send into a fixed group chat
- Now, any custom combination of targets can be selected per-message
- Install Telegram on mobile or Web
- Enter your unique (deeplink) URL in your web browser, as provided by your Administrator
- As a parent, you will now automatically receive messages and pictures for your child(ren) by the school from the chatbot
- As a school staff, you can now use
/sendmsg
to write out a message/attach a picture, select students, and the parents of those selected students will receive your message.
- Administrators trigger manual Flow in Directus
- Uses a custom Operation extension
- Plaintext includes:
- single-char prefix to identify between parent or admin auth
- unique identifying field of the record
- local secret (explained below)
- Plaintext is encrypted for the payload, to prevent malicious users from authenticating
- initialization vector, if exists, may be prefixed before the ciphertext in the payload
- Stores deeplink into an
auth_deeplinks
field for each record - Administrator can distribute the deeplinks according to a CMS view or export with the necessary fields
- Telegram has an inbuilt deeplink feature
- We don't want bad actors to spam the
/start
route with random payloads, causing excessive CMS API calls and DB access- We use a
LOCAL_SECRET
, shared between Directus CMS and the Bot - When generating the deeplinks, we include the
LOCAL_SECRET
in the plaintext - Later, when the bot receives a deeplink payload, it can locally decrypt and validate the
LOCAL_SECRET
segment before ever calling the CMS API - Basically ensures that the deeplink payload is genuinely created by us, even if the other segments of the plaintext are invalid
- We use a
- Telegram's deeplinks only allow a
base64url
-charset & maximum 64-character payload- This severely limits the entropy of our payload, and thus limits the length of our plaintext
- Have to be very economical to be able to derive a unique record and enforce tight security, while adhering to the 64-character payload limit
- Invalidating deeplinks is just a matter of regenerating the
LOCAL_SECRET
value- This invalidates all deeplinks, globally
- Current implementation has not enough entropy to fit Telegram's payload limitations while having a unique Initialization Vector field for each user, in order to invalidate a specific user's link β this is currently outside MVP, may be enhanced later
Note: This isn't the exact implementation, certain details are tweaked for obscurity purposely
- Admin initiates the flow with
/sendmsg
command- Checks for admin status
- Admin attaches a message or picture with caption
- Admin selects a list of students
- Bot communicates with the database to pull records of the parents' Telegram ID of the queried student
- Bot gives error feedback if:
- if query is not found in database
- if student has no parents who have already registered
- Admin sends
/done
to finish the input - The bot forwards the message to each target on the list
- The bot returns feedback on the operation
- by simply forwarding the message, we don't need to store the message in memory
- each child may have multiple parents, and each parent may have multiple children
- when selecting a child, it should send to both parents
- when selecting siblings, it should deduplicate calls
- Frontend:
- Mobile Client: Telegram
- Backend (bot):
- Template: bot-base/telegram-bot-template
- Infrastructure: Vercel
- Backend (db):
Follow these steps to set up and run your bot using this template:
-
Clone New Repository
-
Environment Variables Setup
Create an environment variables file by copying the provided example file:
cp .env.example .env
Open the newly created
.env
file and set theBOT_TOKEN
environment variable. -
Launching the Bot
You can run your bot in both development and production modes.
Development Mode:
Install the required dependencies:
pnpm install
Start the bot in watch mode (auto-reload when code changes):
pnpm run dev
Production Mode:
Install only production dependencies (no development dependencies):
pnpm install --only=prod
Set the
NODE_ENV
environment variable to "production" in your.env
file. Also, make sure to updateBOT_WEBHOOK
with the actual URL where your bot will receive updates.NODE_ENV=production BOT_WEBHOOK=<your_webhook_url>
Start the bot in production mode:
pnpm start # or pnpm run start:force # if you want to skip type checking
pnpm run lint
β Lint source code.pnpm run format
β Format source code.pnpm run typecheck
β Run type checking.pnpm run dev
β Start the bot in development mode.pnpm run start
β Start the bot.pnpm run start:force
β Starts the bot without type checking.
project-root/
βββ api
β βββ server # Serverless entry point
βββ locales # Localization files (currently unused)
βββ src
βββ bot # Contains the code related to the bot
β βββ callback-data # Callback data builders
β βββ features # Implementations of bot features
β β βββ (feature)
β β βββ composer.ts # entry point for the directory
β β βββ conversation.ts # conversation handler
β βββ handlers # Update handlers
β βββ helpers # Utility functions
β βββ keyboards # Keyboard builders (currently unused)
β βββ middlewares # Middleware functions
β βββ i18n.ts # Internationalization setup
β βββ context.ts # Context object definition
β βββ index.ts # Bot entry point
β
βββ server # Contains the code related to the web server
β βββ index.ts # Web server entry point
βββ lib # Utility modules
β βββ directus # Directus SDK abstractions
β βββ * # Whatever other utility modules
βββ config.ts # Application config
βββ logger.ts # Logging setup
βββ main.ts # Application entry point (local)
Variable | Type | Description |
---|---|---|
NODE_ENV | String | Specifies the application environment. (development or production ) |
BOT_TOKEN | String | Telegram Bot API token obtained from @BotFather. |
LOG_LEVEL | String |
Optional.
Specifies the application log level. For example, use info for general logging. View the Pino documentation for more log level options. Defaults to info .
|
BOT_MODE | String |
Optional.
Specifies method to receive incoming updates. (polling or webhook )
Defaults to polling . (You should use webhook in production environment)
|
BOT_WEBHOOK | String |
Optional in polling mode.
Webhook endpoint URL, used to configure webhook in production environment.
|
BOT_SERVER_HOST | String |
Optional. Specifies the server hostname. Defaults to 0.0.0.0 .
|
BOT_SERVER_PORT | Number |
Optional. Specifies the server port. Defaults to 80 .
|
BOT_ALLOWED_UPDATES | Array of String |
Optional. A JSON-serialized list of the update types you want your bot to receive. See Update for a complete list of available update types. Defaults to an empty array (all update types except chat_member ).
|
DIRECTUS_STATIC_TOKEN | String | Static token from the Telegram dummy user in Directus, configured with appropriate limited permissions |
DIRECTUS_URL | String | Public base URL to access the Directus API |
ENCRYPTION_METHOD | String | An algorithm from Web Cryptography API: SubtleCrypt supported algorithms |
ENCRYPTION_KEY | String | Shared decryption key between bot and CMS to decode deeplink payloads |
LOCAL_SECRET | String | Shared secret between bot and CMS to validate deeplink payloads (locally) |
- Select a group of students with a filter defined in Directus Presets
- Screening system for principals to vet quality of messages before they go out
- Send more than one message, a series of messages
- Accountability logs in CMS
- messages sent out
- error logs