7 of 103 min

Why this project lives not only in text

This project isn’t built as a text AI bot in a vacuum. It lives inside the Telegram environment, which means it works not only with lines of text but also with media: voice, audio, photo, video_note, sticker.

One of the basic nodes looks like this:

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 A separate table for media. So the project stores not only message text but also the types of media events.

• chat_id, msg_id, user_id Record where exactly the media appeared, in which message and from which user.

• media_type Shows what it was: a voice message, photo, sticker, video note, audio.

• file_id, file_unique_id Make it possible to refer to this object through Telegram again later.

• caption If the media had a caption, it’s saved too.

• UNIQUE(chat_id, msg_id, media_type) Protection against duplicates. The same media object shouldn’t be written to memory twice.

Now the moment of saving itself:

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(…) The function saves media into the project’s memory.

• INSERT OR IGNORE If such a record already exists, the database doesn’t create a duplicate. This matters for stable work in Telegram, where the same event can sometimes arrive twice.

• media_type, file_id, caption The project remembers not just “there was some media” but its type, identifier and context.

So the bot stores not only the conversation in words but also the conversation in forms.

And the project has one more layer, a publishing one:

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 This is the Stories state table.

• last_story_at When a Story was last published.

• last_source_chat_id, last_source_msg_id Where the source material was taken from.

• last_file_unique_id Which file has already been used.

• cooldown_until When publishing is allowed again.

So the project treats media not as dead attachments. It remembers them, tells them apart, links them to a source and manages their reappearance.

That’s why this project lives not only in text. It lives in material: in a voice, an image, a sticker, a video note, a caption, a publication, a file returning to the stream.

And in this sense it’s built not as a “chat with AI” but as a system embedded in the real media life of Telegram.

Series contents
  1. How the idea of a book gave rise to a space for memory, conversation and recirculation
  2. What this project is really made of
  3. Memory, profile and context: what the bot rests on
  4. The logic by which the bot answers people
  5. How I made a system that doesn't let content vanish
  6. How the project's philosophy is sewn into the code
  7. Why this project lives not only in text
  8. How the bot gathers and updates people's profiles
  9. How I protect the project from chaos, duplicates and chance
  10. Why I wrote this code not for the sake of automation