Make bot multi-tenant with colorful welcome and per-owner isolation
The bot is now public: anyone connects it to their own Telegram Business account and gets antidelete for their own private chats. On connection (business_connection enabled) and on /start it sends a colorful welcome describing features, how to connect, and limitations. - connections.js: resolve a connection's owner from business_connection_id (memo -> DB -> getBusinessConnection). Business messages have no outgoing flag, so the owner's own messages are filtered by comparing from.id; if the owner can't be resolved the message is not cached. - Strict per-owner isolation: captures scoped by owner_id; the panel shows each user only their own feed; notifications go to the owner's chat. - db.js: multi-tenant schema (connections table; messages keyed by (conn_id, chat_id, msg_id); captures/counts scoped by owner_id). - media.js: key cached-media filenames by (connId, chatId, msgId) to prevent one tenant overwriting another's encrypted media. - panel.js: drop the owner-only barrier; /start sends welcome + own feed. - config.js: OWNER_ID is now optional (service logs only, grants no access). - Docs: README/.env.example rewritten for the multi-tenant model and the shared-key privacy caveat. - Stop tracking .claude/settings.local.json; restore the Hcrgram/ ignore. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+9
-7
@@ -16,13 +16,18 @@ const num = (name) => {
|
||||
* Конфиг бота. Работает через официальное бизнес-подключение Telegram —
|
||||
* никакой строки сессии, только токен бота от @BotFather. Всё чувствительное
|
||||
* (токен, ключ шифрования) живёт только в .env и никуда не уходит.
|
||||
*
|
||||
* Мультитенант: любой может подключить бота к своему бизнес-аккаунту. Доступ
|
||||
* к перехватам изолирован по владельцу (см. db.js), поэтому OWNER_ID больше
|
||||
* ничего не открывает и не обязателен.
|
||||
*/
|
||||
export const config = {
|
||||
// Бот-панель (@BotFather). К нему же владелец подключает бизнес-аккаунт
|
||||
// (Настройки → Telegram для бизнеса → Чат-боты). Единственный UI.
|
||||
// Бот (@BotFather, Business Mode). К нему пользователи подключают свои
|
||||
// бизнес-аккаунты (Настройки → Telegram для бизнеса → Чат-боты).
|
||||
botToken: () => req('BOT_TOKEN'),
|
||||
// Telegram id владельца: только он управляет ботом и получает перехваты.
|
||||
ownerId: () => num('OWNER_ID'),
|
||||
// Необязательный id «оператора» бота — только для служебных логов, доступ
|
||||
// к чужим данным он НЕ даёт (строгая изоляция). null, если не задан.
|
||||
ownerId: () => (process.env.OWNER_ID ? num('OWNER_ID') : null),
|
||||
|
||||
// Ключ шифрования локального хранилища (32+ байта, hex или ascii).
|
||||
encryptionKey: () => req('ENCRYPTION_KEY'),
|
||||
@@ -45,11 +50,8 @@ export const mediaPath = () => fileURLToPath(config.mediaDir);
|
||||
/** Проверяем всё, что нужно для старта, одним махом — понятная ошибка вместо падения в рантайме. */
|
||||
export function assertConfig() {
|
||||
config.botToken();
|
||||
config.ownerId();
|
||||
const key = config.encryptionKey();
|
||||
if (Buffer.from(key, key.match(/^[0-9a-f]+$/i) ? 'hex' : 'utf8').length < 32) {
|
||||
throw new Error('ENCRYPTION_KEY должен быть не короче 32 байт (64 hex-символа).');
|
||||
}
|
||||
}
|
||||
|
||||
export const isOwner = (id) => Number(id) === config.ownerId();
|
||||
|
||||
Reference in New Issue
Block a user