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
/sendmsgto 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_deeplinksfield 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
/startroute 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_SECRETin the plaintext - Later, when the bot receives a deeplink payload, it can locally decrypt and validate the
LOCAL_SECRETsegment 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_SECRETvalue- 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
/sendmsgcommand- 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
/doneto 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
.envfile and set theBOT_TOKENenvironment 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_ENVenvironment variable to "production" in your.envfile. Also, make sure to updateBOT_WEBHOOKwith 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


