7 из 103 мин

Почему этот проект живёт не только в тексте

Этот проект устроен не как текстовый AI-бот в вакууме. Он живёт внутри Telegram-среды, а значит работает не только с репликами, но и с медиа: voice, audio, photo, video_note, sticker.

Один из базовых узлов выглядит так:

c.execute("""
CREATE TABLE IF NOT EXISTS ann_message_media(
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  chat_id INTEGER NOT NULL,
  msg_id INTEGER NOT NULL,
  user_id INTEGER,
  media_type TEXT NOT NULL,
  file_id TEXT,
  file_unique_id TEXT,
  mime_type TEXT,
  caption TEXT,
  created_at TEXT NOT NULL,
  UNIQUE(chat_id, msg_id, media_type)
)
""")

• ann_message_media
Отдельная таблица для медиа.
То есть проект хранит не только текст сообщений, но и сами типы медиа-событий.

• chat_id, msg_id, user_id
Фиксируют, где именно появилось медиа, в каком сообщении и от какого пользователя.

• media_type
Показывает, что это было: голосовое, фото, стикер, кружок, аудио.

• file_id, file_unique_id
Позволяют потом снова обратиться к этому объекту через Telegram.

• caption
Если у медиа была подпись, она тоже сохраняется.

• UNIQUE(chat_id, msg_id, media_type)
Защита от дублей.
Один и тот же медиа-объект не должен записываться в память дважды.

Теперь сам момент сохранения:

def ann_save_media(chat_id: int, msg_id: int, user_id: int | None, media_type: str,
                   file_id: str | None, file_unique_id: str | None,
                   mime_type: str | None = None, caption: str | None = None):
    c.execute("""
      INSERT OR IGNORE INTO ann_message_media
      (chat_id, msg_id, user_id, media_type, file_id, file_unique_id, mime_type, caption, created_at)
      VALUES(?,?,?,?,?,?,?,?,?)
    """, (...))

• ann_save_media(…)
Функция сохраняет медиа в память проекта.

• INSERT OR IGNORE
Если такая запись уже есть, база не создаёт дубль.
Это важно для стабильной работы в Telegram, где одно и то же событие иногда может прийти повторно.

• media_type, file_id, caption
Проект запоминает не просто “было какое-то медиа”, а его тип, идентификатор и контекст.

То есть бот хранит не только разговор в словах, но и разговор в формах.

А дальше у проекта есть ещё один слой, уже публикационный:

c.execute("""
CREATE TABLE IF NOT EXISTS ann_story_state(
  id INTEGER PRIMARY KEY CHECK (id=1),
  last_story_at TEXT,
  last_source_chat_id INTEGER,
  last_source_msg_id INTEGER,
  last_file_unique_id TEXT,
  cooldown_until TEXT,
  note TEXT
)
""")

• ann_story_state
Это таблица состояния Stories.

• last_story_at
Когда Story публиковалась в последний раз.

• last_source_chat_id, last_source_msg_id
Откуда был взят исходный материал.

• last_file_unique_id
Какой именно файл уже использовался.

• cooldown_until
Когда снова можно публиковать.

То есть проект работает с медиа не как с мёртвыми вложениями.
Он помнит их, различает, связывает с источником и управляет их повторным появлением.

Именно поэтому этот проект живет не только в тексте.
Он живет в материале: в голосе, изображении, стикере, кружке, подписи, публикации, повторном возвращении файла в поток.

И в этом смысле он устроен не как “чат с AI”, а как система, встроенная в реальную медиа-жизнь Telegram.

Оглавление серии
  1. Как из идеи книги родилось пространство для памяти, разговора и рециркуляции
  2. Из чего на самом деле состоит этот проект
  3. Память, профиль и контекст: на чем держится бот
  4. По какой логике бот отвечает людям
  5. Как я сделал систему, которая не даёт контенту исчезать
  6. Как философия проекта вшита в код
  7. Почему этот проект живёт не только в тексте
  8. Как бот собирает и обновляет профили людей
  9. Как я защищаю проект от хаоса, дублей и случайности
  10. Почему я писал этот код не ради автоматизации