TGViewer
Channel Public Channel
annFlow

annFlow

@annsflow

Automation • LLM agents • second brain • integrations • pipelines • bots • chat funnels • lead generation • CRM • ops/operations • websites •SEO•VPS• VPN • time tracking/timing•logging•metrics @arsenynn

Portfolio: https://portfolio.arsenynn.quest/it-ai/
Subscribers
33
Photos
30
Videos
10
Links
89

Showing posts older than #148 · Back to latest

Older Posts 17 shown
Post #147 99
Обычно проект работает в фоне, и я вообще не вижу, что происходит внутри браузера. Для этого видео специально показываю его в видимом режиме: как он находит вакансии, выбирает резюме и проходит форму отклика. Скорость работы здесь специально уменьшена, чтобы сам процесс можно было нормально рассмотреть и понять.

Оглавление темы 📌
Навигация по проектам канала 📍
Все проекты 🎶

#HHApply@annsFlow

💻 annFlow
  • 🦄 3
Post #146 43
Самое трудное в автоотклике, это не submit

Снаружи автоотклик выглядит обманчиво просто.

Открыл вакансию.
Вставил письмо.
Нажал submit.

На практике submit почти не проблема.

Проблема в другом: страница отклика на HH не является одной и той же страницей.
Она может означать совсем разные состояния системы:
не то резюме без возможности переключения,
уже отправленный отклик,
истёкшую сессию,
анкету,
radio,
checkbox,
select,
смешанные формы с дополнительным текстовым полем,
или просто состояние, которое нельзя автоматизировать безопасно.

Именно поэтому apply-flow в таком проекте начинается не с действия, а с распознавания реальности.


def classify_page_variant(snapshot: Dict[str, Any]) -> PageVariant:
if not isinstance(snapshot, dict):
return PageVariant.UNKNOWN

if snapshot.get("auth_required"):
return PageVariant.AUTH_REQUIRED

if snapshot.get("already_applied"):
return PageVariant.ALREADY_APPLIED

if snapshot.get("hh_resume_override"):
return PageVariant.FIXED_RESUME

questionnaire_required = bool(snapshot.get("questionnaire_required"))
if questionnaire_required:
items = list(snapshot.get("questionnaire_items") or [])
safe_types = ApplyFlow.SAFE_QUESTIONNAIRE_TEXT_TYPES
has_unsupported = any(
str(item.get("field_type") or "input").strip().lower() not in safe_types
or bool(item.get("requires_human"))
for item in items
if isinstance(item, dict)
)
if has_unsupported or not items:
return PageVariant.QUESTIONNAIRE_UNSUPPORTED
return PageVariant.QUESTIONNAIRE_TEXT

if snapshot.get("primary_submit_label") and not questionnaire_required:
return PageVariant.SIMPLE_PICKABLE

return PageVariant.UNKNOWN


classify_page_variant(…) нужен для первого решения: что за страница сейчас перед системой.
not isinstance(snapshot, dict) отсекает сломанный snapshot, чтобы проект не принимал решения на мусоре.
AUTH_REQUIRED останавливает pipeline, если вместо отклика открылась стена логина.
ALREADY_APPLIED не даёт системе делать дубли там, где отклик уже существует.
FIXED_RESUME ловит кейс, когда HH сам навязывает резюме и не даёт нормально переключиться.
questionnaire_required переводит страницу в другой класс сложности, где простой submit-path уже не работает.
items = list(…) собирает структурированное представление анкеты, без которого автоматизация становится слепой.
safe_types ограничивает набор полей, которые система вообще считает безопасными для обработки.
has_unsupported проверяет, не встретилась ли форма, в которой уверенности уже недостаточно.
field_type not in safe_types отсекает всё, что выходит за пределы текущего безопасного контура.
requires_human даёт форме право раньше вывести человека в контур, если автоматизм уже сомнителен.
QUESTIONNAIRE_UNSUPPORTED нужен, чтобы проект честно признал предел своей уверенности и не лез дальше.
QUESTIONNAIRE_TEXT фиксирует случай, где анкету ещё можно вести по контролируемому сценарию.
primary_submit_label проверяет, существует ли прямой путь до отклика без анкеты.
SIMPLE_PICKABLE обозначает happy path, где страницу можно обрабатывать почти линейно.
UNKNOWN оставляет системе право не действовать, если состояние страницы не распознано.

В этом и заключается настоящая сложность.

Здесь проект не “нажимает кнопку”.
Здесь он сначала интерпретирует состояние страницы и только потом выбирает один из путей:
автоматический submit,
questionnaire path,
handoff человеку,
пропуск,
или остановку.

В какой-то момент стало ясно, что анкеты, это не частный случай, а отдельная машина внутри проекта.
Система должна не просто вставлять текст, а различать типы форм, понимать границы своей уверенности и не действовать там, где страница уже не ясна.

То есть автоотклик оказался не задачей “нажать кнопку вместо человека”.

Он оказался задачей “понять, что за форма перед тобой, до того как сделать хоть одно действие”.

Оглавление темы 📌
Навигация по проектам канала 📍
Все проекты 🎶

#HHApply@annsFlow

💻 annFlow
Telegram annFlow Как я построил систему автооткликов, или Как превратить поиск работы в форму порядка ● Автоматизация отклика как ответ на асимметрию рынка ● Как бот сравнивает меня с вакансией и собирает отклик под нее ● Почему закрытие соискательского API не убило проект…
Post #145 31
Как я сохранил мозг системы и заменил ей транспорт

После закрытия соискательского API стало ясно, на чём вообще держится проект.

Если бы его логика была пришита к одному способу доступа к HH, систему пришлось бы выбросить.
Но к этому моменту у проекта уже был собственный внутренний каркас. Поэтому менять пришлось не смысл, а только транспорт.

Это хорошо видно по моделям, через которые проект описывает свою работу:



class VacancyCard:
vacancy_id: str
title: str
company: str
url: str

class VacancyDetails:
vacancy_id: str
title: str
company: str
description_text: str
apply_available: bool

class ApplyDraft:
vacancy_id: str
resume_id: str
cover_letter: str

class ApplyResult:
vacancy_id: str
success: bool
status: str

class PrepareApplyOutcome(str, Enum):
PREPARED_DRAFT = "PREPARED_DRAFT"
NEEDS_HUMAN = "NEEDS_HUMAN"
SKIP = "SKIP"
RETRYABLE_TRANSIENT = "RETRYABLE_TRANSIENT"
RATE_LIMITED_EXTERNAL = "RATE_LIMITED_EXTERNAL"


VacancyCard - это краткая модель вакансии на уровне поисковой выдачи.
vacancy_id - внутренний опорный идентификатор, через который система связывает поиск, отклик, логи и антидубли.
title - название вакансии, с которым дальше работает классификация и логика релевантности.
company - работодатель, чтобы проект понимал контекст и фиксировал, куда именно идет отклик.
url - ссылка на вакансию, через которую транспорт открывает карточку уже в браузере.

VacancyDetails - это та же вакансия, но уже после полного открытия и извлечения содержимого.
description_text - полный текст вакансии, на котором строятся анализ, письмо и ответы на вопросы.
apply_available - флаг, который сразу отделяет живую вакансию от уже недоступной для отклика.

ApplyDraft - это уже не вакансия, а подготовленный черновик конкретного отклика.
resume_id - указание, каким именно резюме система должна отвечать на эту вакансию.
cover_letter - готовый текст сопроводительного, собранный под конкретную позицию, а не взятый из шаблона.

ApplyResult - это финальная фиксация того, чем закончилась попытка отклика.
success - бинарный итог, который позволяет быстро отделить удачный проход от неудачного.
status - более точный слой результата, чтобы система различала не просто успех и провал, а конкретный исход.

PrepareApplyOutcome - это самая важная часть, потому что здесь система различает типы состояний до финального submit.
PREPARED_DRAFT - черновик отклика собран и система может двигаться дальше.
NEEDS_HUMAN - страница или форма требуют ручного решения и не должны ломать весь pipeline.
SKIP - вакансия осознанно отбрасывается как нерелевантная или неподходящая.
RETRYABLE_TRANSIENT - произошёл временный технический сбой, после которого можно пробовать снова.
RATE_LIMITED_EXTERNAL - внешний контур HH начал ограничивать процесс, и системе нужен cooldown, а не агрессия.

Вся ценность этого кода в том, что он описывает не интеграцию, а внутренний мир проекта: сущности, этапы и исходы.
Поэтому после закрытия API проект не пришлось пересобирать с нуля — старый транспорт исчез, но логика, модели и state остались.

Оглавление темы 📌
Навигация по проектам канала 📍
Все проекты 🎶

#HHApply@annsFlow

💻 annFlow
Telegram annFlow Как я построил систему автооткликов, или Как превратить поиск работы в форму порядка ● Автоматизация отклика как ответ на асимметрию рынка ● Как бот сравнивает меня с вакансией и собирает отклик под нее ● Почему закрытие соискательского API не убило проект…
Post #143 40
Почему закрытие соискательского API не убило проект, а заставило его повзрослеть

Изначально это был API-first проект в прямом смысле слова.

Он не просто “что-то искал через API”.
Через официальный соискательский контур HH у меня был собран почти весь рабочий цикл: OAuth-авторизация, access token, поисковые конфиги, получение вакансий, чтение деталей, генерация сопроводительных, отправка откликов, логирование результатов, обновление статусов и работа с историей через /negotiations.

Причем эти поиски уже тогда были не хаотичными.
В проекте были отдельные конфиги под разные направления, с собственными фильтрами по регионам, зарплате и привязанным резюме.
То есть система уже умела не просто искать вакансии, а искать их по разным профессиональным сценариям.

Это была уже не заготовка, а нормальная система откликов, построенная на официальном канале.

Потом HH этот канал закрыл.

Они написали это прямо:
«Прекращаем поддержку соискательского API»,
«С 15 декабря доступ к соискательскому API hh будет закрыт»,
«К сожалению, альтернативных форматов работы с соискательским API нет».

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

И здесь для меня важнее всего была одна вещь:
API у работодателей и их интеграций остался, а соискательский API просто исчез.

То есть одна сторона рынка продолжает работать через автоматизацию, интеграции и машинные контуры.
Другая осталась без официального программного канала вообще.

И в этот момент проект столкнулся с архитектурным ударом.

До этого модель была такой:

API search
API details
API apply
API statuses


После этого пришлось собирать другую:

browser search
browser details
browser apply
Telegram-first handoff
state, retry, safety


Именно здесь выяснилось, есть ли у проекта запас прочности.
Может ли он пережить исчезновение официального пути, не потеряв собственную логику.

Это был момент, когда проект либо умирает вместе с закрытым API, либо взрослеет.

Я выбрал второе.

Оглавление темы 📌
Навигация по проектам канала 📍
Все проекты 🎶

#HHApply@annsFlow

💻 annFlow
Telegram annFlow Как я построил систему автооткликов, или Как превратить поиск работы в форму порядка ● Автоматизация отклика как ответ на асимметрию рынка ● Как бот сравнивает меня с вакансией и собирает отклик под нее ● Почему закрытие соискательского API не убило проект…
Post #141 36
Как бот сравнивает меня с вакансией и собирает отклик под нее

Одна из самых важных вещей в этом проекте в том, что он не отвечает на все вакансии одинаково.

Он не берёт одно универсальное резюме, не вставляет один и тот же шаблон и не надеется, что где-то это случайно сработает.

Сначала система решает, какой профессиональной версией меня вообще нужно отвечать на конкретный рынок.

Для этого у неё есть не один общий поиск, а набор сценариев: с разными направлениями, регионами, условиями и привязанными резюме.
То есть ещё до отклика проект выбирает не только, что искать, но и от какого резюме на это выходить.

Это выглядит примерно так:


search_mode = 7
mode_7 -> [config_5]

config_5:
text = "Prompt Engineer / AI Developer"
area = [1, 2]
salary_min = "configured threshold"
resume_name = "automation_resume"

resumes["automation_resume"]:
resume_id = "..."
portfolio_url = "..."


search_mode выбирает рабочий сценарий.
config_5 задаёт рамку поиска: направление, регионы и финансовый порог.
resume_name привязывает этот сценарий к нужному резюме.
resume_id переводит всё это в конкретный документ, с которым проект реально выходит на рынок.
portfolio_url продолжает резюме наружу и даёт системе не только текст, но и доказательный материал.

Но это только первый слой.

Дальше проект открывает вакансию и разбирает её уже как текстовую задачу.
Он вытаскивает из описания ключевые требования, стек, роль, ограничения, проверочные формулировки и повторяющиеся сигналы, по которым вакансию потом будут читать и люди, и автоматические фильтры.

После этого начинается самое важное: система сравнивает эти сигналы с моим резюме, опытом и портфолио.
То есть она не “придумывает красивое письмо”, а собирает карту реальных совпадений между вакансией и мной.

И уже на основе этого пишет сопроводительное.

Поэтому письмо здесь не шаблон и не декоративный текст.
Оно строится из трёх слоёв:
• что именно требует вакансия;
• что у меня реально есть по опыту и материалам;
• как сформулировать это так, чтобы отклик был читаемым и для человека, и для первичного HR-фильтра.

Именно поэтому задача сопроводительного здесь не “обмануть систему”.
Задача в другом: сделать релевантность явной, не потерять ключевые сигналы вакансии и не отвалиться ещё до живого просмотра.

В этом для меня и была одна из главных границ проекта.

Я не хотел бота, который просто ускоряет спам.
Я хотел систему, которая сначала понимает, кем именно я сейчас выхожу на рынок, потом понимает, что именно ищет работодатель, и только после этого собирает отклик из реальных совпадений.

Именно в этот момент автоматизация перестаёт быть механикой.

Она становится формой дисциплины:
не отвечать всем одинаково,
не врать о себе,
и не терять релевантность даже там, где первый отсев уже частично делает машина.

Оглавление темы 📌
Навигация по проектам канала 📍
Все проекты 🎶

#HHApply@annsFlow

💻 annFlow
Telegram annFlow Как я построил систему автооткликов, или Как превратить поиск работы в форму порядка ● Автоматизация отклика как ответ на асимметрию рынка ● Как бот сравнивает меня с вакансией и собирает отклик под нее ● Почему закрытие соискательского API не убило проект…
Post #137 55
Автоматизация отклика как ответ на асимметрию рынка

Я начинал не с платформы и не с красивой архитектуры.

Я просто устал от унизительной механики поиска работы на HH.

Открываешь вакансию.
Читаешь описание.
Пытаешься понять, живая она вообще или висит как витрина.
Подбираешь резюме.
Пишешь сопроводительное.
Потом тебе добавляют ещё вопросы, анкету, тест, дополнительные поля.
Ты тратишь время.
А в ответ часто либо тишина, либо отказ, либо вообще непонятно, видел ли кто-то твой отклик.

И в этом для меня была главная несправедливость.

У работодателя уже давно есть всё: ATS, авторазбор откликов, автоматический поиск кандидатов, фильтры, воронки, AI-инструменты.
У соискателя в большинстве случаев до сих пор только руки, глаза и бесконечное ручное повторение одного и того же ритуала.

С одной стороны стоит система.
С другой, человек, который вручную доказывает, что вообще достоин быть замеченным.

Мне это казалось перекошенной конструкцией.

Если работодателю можно усиливать себя автоматизацией, то почему соискатель должен оставаться в доцифровом положении и тратить часы на то, что можно превратить в систему?

Именно из этого раздражения всё и началось.

Я не хотел схитрить и заспамить рынок.
Я хотел выровнять силы в этой бесконечной борьбе.

Потому что по сути весь отклик давно превратился в один и тот же ручной конвейер:
вакансия → резюме → сопроводительное → анкета → отправка или ручной тупик.

Сначала это была попытка снять с себя рутину.
Потом из этого вырос проект, который ищет вакансии, подбирает нужное резюме, анализирует текст вакансии, автоматически заполняет анкеты и вопросы на основе моих реальных данных, а каждое сопроводительное пишет индивидуально, через сравнение вакансии с моим резюме, опытом и портфолио. То есть не генерирует текст в пустоту, а собирает отклик из реальных совпадений.

В итоге это уже не просто бот для откликов, а система, которая соединяет поиск, анализ, выбор резюме, индивидуальное письмо, questionnaire-автоматизацию и память о каждом действии в один управляемый процесс.

Но в основе всего была не любовь к автоматизации.

В основе была злость на рынок, где у одной стороны уже есть инструменты, а у другой все ещё отнимают время вручную.

Оглавление темы 📌
Навигация по проектам канала 📍
Все проекты 🎶

#HHApply@annsFlow

💻 annFlow
Telegram annFlow Как я построил систему автооткликов, или Как превратить поиск работы в форму порядка ● Автоматизация отклика как ответ на асимметрию рынка ● Как бот сравнивает меня с вакансией и собирает отклик под нее ● Почему закрытие соискательского API не убило проект…
Post #136 58
Post #135 41
Post #134 39

This post (sticker, poll or similar) has no web preview. Open in Telegram

  • ❤ 1
  • 🤩 1
Post #133 42
Почему я писал этот код не ради автоматизации

Такие проекты обычно описывают через удобство: бот отвечает, что-то сохраняет, что-то публикует, что-то ищет. Но в моём случае задача изначально была другой.

Я писал этот код не для того, чтобы ускорить рутину.
Я писал его для того, чтобы материал не исчезал, разговор не обнулялся после каждой реплики, а философия существовала не только в тексте, но и в поведении системы.

То же самое видно в логике ответа:


messages = [
{"role": "system", "content": system_prompt},
{"role": "system", "content": profile_block},
{"role": "user", "content": ctx},
]


Ответ строится не из одной последней реплики.
Система собирает ситуацию разговора: рамку, профиль человека, накопленный контекст.
То есть она не просто реагирует, а удерживает длительность.

Но самый важный момент в том, что проект живёт не только в ответах.


queue_profile_update(int(user_id))
picked = pick_relevant_content(query_text, top_k=CONTENT_TOP_K)
ANN_PHILOSOPHY_STYLE = style


Что здесь важно:

• queue_profile_update(...)
Система продолжает работать и между сообщениями. Она обновляет представление о человеке не мгновенно, а по мере накопления материала.

• pick_relevant_content(...)
Материал не лежит мёртвым архивом. Он возвращается в разговор по смыслу.

• ANN_PHILOSOPHY_STYLE = style
Философия здесь не приложена рядом. Она встроена в поведение и влияет на сам принцип речи.

Поэтому это не просто бот, а среда.
Разговор здесь не обнуляется, материал не исчезает, а философия работает как часть поведения системы.
«Бессмысленность и скоротечность» — среда, где эта логика живёт в действии.

Оглавление темы 📌
Навигация по проектам канала 📍
Все проекты 🎶

#annBot@annsFlow

💻 annFlow
Telegram Бессмысленность и скоротечность #shorts #chat @arsenynn
  • 👨‍💻 1
Post #127 24
Как я защищаю проект от хаоса, дублей и случайности

Самая неприятная вещь в таких проектах не ошибки, а тихий накапливающийся хаос.

Когда одно и то же событие приходит дважды.
Когда медиа сохраняется повторно.
Когда система постепенно начинает противоречить самой себе.

Снаружи это почти незаметно.
Но именно так проект и теряет форму.

Первое, что пришлось сделать, это остановить дубли на самом низком уровне:

UNIQUE(chat_id, msg_id, media_type)


База просто не даёт записать одно и то же дважды.
Без дополнительной логики и без надежды, что это потом кто-то разберёт вручную.

Следом идёт второй слой:

INSERT OR IGNORE INTO ann_message_media


Если событие уже было, оно просто не проходит дальше.
Лишнее состояние не создаётся.

Но дубли это только часть проблемы.

Есть и другая форма хаоса: повторные действия.
Когда система отвечает лишний раз, публикует лишний раз или снова запускает то, что уже не нужно запускать.

Здесь появляется:
cooldown_until


Система хранит момент, раньше которого она не должна ничего делать.
Это уже не про функциональность, а про дисциплину поведения.

Следующий принцип касается тяжёлых операций.
Их нельзя выполнять прямо в момент ответа.
Если пересчитывать профиль на каждую реплику, система становится избыточно чувствительной и начинает шуметь.

Поэтому часть логики вынесена в фон:
очереди, отложенные обновления, накопление материала перед действием.

И, наконец, сбои.

Если внешний слой падает, проект не должен превращаться ни в тишину, ни в мусор.
Поэтому здесь есть паузы, fallback-сценарии, восстановление состояния, а база работает через WAL, чтобы запись не блокировала систему целиком.

В итоге защита строится по слоям:

• база не пускает дубли
• код не усиливает повтор
• cooldown сдерживает лишнюю активность
• тяжёлая логика вынесена из диалога
• сбой не разрушает весь поток работы

И только после этого вообще можно говорить о качестве ответов.

Оглавление темы 📌
Навигация по проектам канала 📍
Все проекты 🎶

#annBot@annsFlow

💻 annFlow
Telegram annFlow «Бессмысленность и скоротечность» как философия, встроенная в память, разговор и рециркуляцию. ● Как из идеи книги родилось пространство для памяти, разговора и рециркуляции ● Из чего на самом деле состоит этот проект ● Память, профиль и контекст: на…
Post #125 26
Как бот собирает и обновляет профили людей

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

Здесь профиль не выглядит как анкета, заполненная один раз. Это обновляемая модель присутствия: она уточняется по мере общения и не пересобирается на каждую реплику.

Ключевой узел выглядит так:

if n >= PROFILE_UPDATE_EVERY:
c.execute("UPDATE ann_profile_counters SET since_last_update=0 WHERE user_id=?", (int(user_id),))
queue_profile_update(int(user_id))


• PROFILE_UPDATE_EVERY
Задаёт порог обновления.
Профиль не дёргается после каждого нового сообщения.

• since_last_update
Счётчик показывает, сколько нового материала накопилось с прошлого обновления.

• queue_profile_update(int(user_id))
Пересборка профиля выносится отдельно.
Это значит, что система сначала накапливает наблюдения, а потом обновляет профиль как самостоятельную задачу.

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

Сам профиль хранится отдельно:

CREATE TABLE IF NOT EXISTS ann_profiles(...)


А основания для него ещё в одном слое:

CREATE TABLE IF NOT EXISTS ann_profile_evidence(...)


• ann_profiles
Хранит текущее описание человека внутри системы.

• ann_profile_evidence
Хранит, на чём это описание вообще основано.

Это важный момент.
Проект не просто “формирует впечатление” о пользователе. Он хранит ещё и следы, из которых это впечатление выросло.

Поэтому бот работает не с разовым образом собеседника, а с длительно уточняемым представлением о человеке.

Именно это делает профиль в проекте не декоративной персонализацией, а отдельным операционным слоем системы.

Оглавление темы 📌
Навигация по проектам канала 📍
Все проекты 🎶

#annBot@annsFlow

💻 annFlow
Telegram Бессмысленность и скоротечность #shorts #chat @arsenynn
  • 👍 1
Post #124 23
Почему этот проект живёт не только в тексте

Этот проект устроен не как текстовый 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.

Оглавление темы 📌
Навигация по проектам канала 📍
Все проекты 🎶

#annBot@annsFlow

💻 annFlow
Telegram Бессмысленность и скоротечность #shorts #chat @arsenynn
Older posts →
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →