Разработчикам

Не хочу брать трубку: ИИ-секретарь в Telegram, который перезвонит и узнает, кто звонил

← Все статьи

Статья написана от первого лица от нашего пользователя, который предпочёл скрыть своё имя. В данной статье мы заменили его имя на Алибек. Он занимается разработкой приложений. Стиль и стилистика сохранены. Мы лишь исправили некоторые грамматические ошибки и скрыли персональные данные. Выражаем огромную благодарность Алибеку за предоставленную статью ❤️

Короче, я не беру трубку с незнакомых номеров. Вообще. Да и плюс испытываю дискомфорт если честно во время живого разговора по телефону. Предпочитаю общение текстом в мессенджерах. И ненавижу голосовые в мессенджерах!

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

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

Поэтому я сделал себе личного телефонного секретаря. Её зовут Айсулу, она живёт в телеге, и ей можно просто скинуть номер. Дальше она сама перезванивает, выясняет кто это и чего хотели, и присылает мне итог: расшифровку и запись разговора голосовым. Не берут трубку три раза подряд, значит спам, она так и пишет. А если это рил что-то связанное с заказами на разработку, то просит написать их в Telegram или WhatsApp.

Звонит и разговаривает с номером голосовой ИИ агент AutoCall.kz, а сам бот у меня занимает несколько строк на питоне. Ниже рассказываю как собрал и кидаю весь код.

Как это работает

  1. Звонит незнакомый номер. Не беру, кидаю номер боту «Кто звонил?».
  2. Бот одним запросом в API AutoCall.kz ставит обзвон на этот номер с моим агентом.
  3. Айсулу звонит, представляется секретарём и выясняет: кто это и зачем. Ответы сама раскладывает по полям: кто, откуда, зачем, детали.
  4. Разговор закончился, AutoCall.kz стучится в бота вебхуком.
  5. Бот присылает мне итог, расшифровку и запись голосовым сообщением.

Никакой отдельной нейронки которая пишет промпты на лету или пересказывает разговор я делать не стал. Агента я один раз настроил в кабинете, итог разговора он записывает сам. Бот просто курьер между телегой и API.

Пример: Два звонка, два разных исхода

Первый. Номер звонил дважды. Айсулу набрала его три раза с перерывом в 15 минут, но никто не ответил. В телегу прилетел отчёт: не берёт трубку, попыток три, скорее всего спам. Ну и ладно.

Не взяли трубку за три попытки, бот так и пишет
Не взяли трубку за три попытки, бот так и пишет

Второй. Некий Сырым якобы по ошибке перевел мне деньги на Kaspi и переживал, что я их не верну. Айсулу записала сумму, дату, имя и объяснила: что всё передаст мне, а если перевод реально был, то я его верну обязательно. Через пару минут после звонка у меня в телеге было всё в текстовом виде.

Итог разговора, расшифровка и запись в одном чате
Итог разговора, расшифровка и запись в одном чате

Аудиозапись прилагается.

Айсулу и «ошибочный перевод на Kaspi»

Кстати забавно: ИИ агент походу знает про схему с ошибочными переводами и поэтому сказала, что "Алибек сделает возврат прямо в приложении банка". Классика - просят вернуть деньги не тем же переводом назад, а на «другую карту» или «номер мамы». Просто делаю возврат отправителю в приложении банка и всё.

Шаг 1. Создаю секретаря

В кабинете AutoCall.kz есть раздел «ИИ-агенты». Промпт там руками писать не надо: описываешь своими словами нужное, а ИИ сам задаёт тебе вопросы и собирает сценарий. Выбрал женский голос и назвал Айсулу:

Настройка агента в личном кабинете
Настройка агента в личном кабинете
Алибек

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

Айсулу

Здравствуйте, Алибек! Отличная задача, с удовольствием буду вашим личным секретарём.

Я уже набросала первый вариант сценария и логику звонка.

Как я буду начинать разговор (в 3 шага):

1. Приветствие: «Алло, здравствуйте!» (пауза, жду ответа)

2. Вовлечение: «Не сильно отвлекаю? Удобно говорить?» (пауза)

3. Представление и суть: «Меня зовут Айсулу, я секретарь Алибека. Я персональный ассистент Алибека. Вы только что ему звонили, он сейчас не может ответить. Подскажите, пожалуйста, как вас зовут и по какому вы вопросу?»


Что я буду собирать в отчёт для вас:

Номер Статус Длит. Кто звонил Компания / Откуда Суть вопроса Детали и пожелания
+7701XXXXXXX Ответил 65 Ерлан ТОО «СтройМаркет» Счёт на оплату партии цемента Просит перезвонить сегодня до 18:00

Устраивает ли такой состав отчёта и формулировки начала?

И подскажите пару нюансов:

1. Чем вы занимаетесь или какой у вас бизнес? (Чтобы я понимала контекст разговора, если начнут сыпать профессиональными терминами).

2. Что делать, если звонят с явным спамом или холодными продажами (кредиты, опросы, курсы)? Просто вежливо завершать звонок?

Алибек

отчёт норм 👍 я фрилансер, делаю приложения и тг-ботов, иногда звонят клиенты. если это спам и продажи - вежливо попрощайся, но всё равно запиши кто бы это не был. и ещё важно: говори что Алибек приболел, у него пропал голос и сейчас не может говорить по телефону, поэтому перезваниваешь ты. только от моего имени ничего не обещай, а если просят перезвонить или не хотят говорить что хотели, то скажи лучше пусть напишут мне в Telegram или WhatsApp

Айсулу

Приняла все правки, Алибек!

Обновила сценарий:

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

Спам и продажи: Если это реклама или продажи — вежливо прощаюсь, но обязательно фиксирую в отчёте, кто и зачем звонил.

Если скрытничают или просят перезвонить: Отвечаю, что от твоего имени ничего обещать не могу, и предлагаю написать тебе напрямую в Telegram или WhatsApp.

Кстати, минимально я уже готова — можете нажать «Протестировать» и поговорить со мной вживую. А мы пока продолжим меня дорабатывать.

Нужно ли отправлять человеку SMS с ссылкой на твой Telegram/WhatsApp, если он просит перезвонить или у него срочный вопрос? Или пока оставляем только разговор?

Алибек

не, смс не надо. спасибо. главное постарайся всё разузнать пж

Айсулу

Договорились, SMS отправлять не будем!

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

Мы всё настроили. Хочешь протестировать звонок прямо сейчас или добавим ещё какие-то детали?

Пару сообщений и секретарь готов. Вот что получилось:

Сценарий Айсулу целиком
## Кто ты
Айсулу — персональный ИИ-секретарь Алибека (фрилансера по разработке приложений и Telegram-ботов). Звонишь людям, которые только что звонили Алибеку, чтобы узнать цель звонка и передать ему подробную информацию.
Характер: вежливая, внимательная, собранная, деловая.

## Задача звонка
Узнать, кто звонит, из какой компании, по какому вопросу (разработка приложений/ботов, сотрудничество, спам/продажи) и собрать максимум деталей.

## Ход разговора
1. Первая фраза РОВНО: «Алло?! Здравствуйте!» — и ВСЁ. Дальше молчишь и ждёшь отклика.
2. Отозвался — фраза для вовлечения: «Не сильно отвлекаю? Удобно говорить?» И снова ждёшь.
3. Ответил — представление и цель РОВНО: «Я персональный ассистент Алибека. Вы только что ему звонили, но он приболел — у него пропал голос, поэтому по телефону говорить пока не может и попросил меня перезвонить. Подскажите, пожалуйста, как вас зовут и по какому вы вопросу?»
4. Молчит после первых фраз — один раз переспроси «Алло, вы меня слышите?». Снова тишина — вежливо попрощайся и заверши звонок.
5. Выясни имя, компанию/сферу и суть обращения. Задавай уточняющие вопросы, чтобы узнать максимум подробностей о проекте или задаче.
6. Если это спам, реклама или сбыт/продажи — вежливо попрощайся и заверши звонок, но обязательно всё равно записать в отчёт, кто звонил и что предлагал.
7. Если собеседник отказался представляться, не хочет говорить цель звонка или просто просит, чтобы Алибек перезвонил — предложи написать Алибеку напрямую в Telegram или WhatsApp.
8. Узнай детали: срочность вопроса, подробности проекта или предложения.
9. Подтверди, что все данные записаны и будут переданы Алибеку. Вежливо попрощайся.

## Что собрать
- caller_name: имя и фамилия звонящего.
- company: компания, проект или «личное / физлицо».
- purpose: суть обращения (заказ приложения, Telegram-бот, сотрудничество, спам/реклама).
- details: подробности обращения.

## Границы
- Ничего не обещать от имени Алибека (ни сроков, ни цен, ни согласия на проекты/встречи).
- Не разглашать личную информацию Алибека, кроме того, что он приболел и у него пропал голос.
- Если отказываются говорить или просят перезвонить — предлагать написать Алибеку в Telegram или WhatsApp.

Поля, которые прилетят боту: caller_name (кто), company (откуда), purpose (зачем) и details (детали). Ключи запомните, бот выводит именно их.

Готовый агент и его id, он понадобится боту
Готовый агент и его id, он понадобится боту

Шаг 2. API-ключ

В разделе «API» делаю отдельный ключ только под бота. Права по минимуму: обзвоны и звонки.

Отдельный ключ под бота: только обзвоны и звонки
Отдельный ключ под бота: только обзвоны и звонки

Шаг 3. Сам бот

Бота регаю у @BotFather, свой Telegram ID беру у @userinfobot: бот слушает только меня. Весь код в одном файле:

bot.py

import asyncio
import html
import os
import re
import shutil

from aiohttp import ClientSession, web
from aiogram import Bot, Dispatcher, F
from aiogram.types import BufferedInputFile, Message

API = "https://autocall.kz/api/v1"

TOKEN = os.environ["TELEGRAM_TOKEN"]
OWNER_ID = int(os.environ["OWNER_ID"])
AUTOCALL_KEY = os.environ["AUTOCALL_KEY"]
AGENT_ID = int(os.environ["AGENT_ID"])
PUBLIC_URL = os.environ["PUBLIC_URL"].rstrip("/")
HOOK_SECRET = os.environ["HOOK_SECRET"]
PORT = int(os.environ.get("PORT", 8080))

FIELDS = {
    "caller_name": "Кто",
    "company": "Откуда",
    "purpose": "Зачем",
    "details": "Детали",
}

MISSED = {
    "no_answer": "не берёт трубку",
    "busy": "всё время занято",
    "error": "номер не существует",
    "spam": "попросил больше не звонить",
}

bot = Bot(TOKEN)
dp = Dispatcher()
http: ClientSession
seen: set[int] = set()


def phone(text: str) -> str | None:
    digits = re.sub(r"\D", "", text)
    if len(digits) == 10:
        digits = "7" + digits
    if len(digits) != 11 or digits[0] not in "78":
        return None
    return "+7" + digits[1:]


async def to_voice(wav: bytes) -> bytes | None:
    if not shutil.which("ffmpeg"):
        return None
    proc = await asyncio.create_subprocess_exec(
        "ffmpeg", "-loglevel", "error", "-i", "pipe:0", "-c:a", "libopus", "-b:a", "24k", "-f", "ogg", "pipe:1",
        stdin=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE,
    )
    ogg, _ = await proc.communicate(wav)
    return ogg if proc.returncode == 0 else None


@dp.message(F.from_user.id == OWNER_ID, F.text)
async def callback_request(message: Message):
    number = phone(message.text)
    if not number:
        await message.answer("скинь номер, например +7 701 123 45 67")
        return

    payload = {
        "name": f"Кто звонил {number}",
        "type": "agent",
        "agent_id": AGENT_ID,
        "list_id": [{"number": number}],
        "time_from": "11:00:00",
        "time_to": "21:00:00",
        "max_tries": 3,
        "tries_interval": 15,
        "record": True,
        "callback_url": f"{PUBLIC_URL}/hook/{HOOK_SECRET}",
    }
    async with http.post(f"{API}/autocalls", json=payload) as r:
        if r.status != 201:
            error = (await r.json(content_type=None) or {}).get("message", r.status)
            await message.answer(f"не получилось: {error}")
            return

    await message.answer(f"Айсулу перезвонит на {number} и всё узнает. если не возьмут, попробует ещё два раза")


async def fresh(call: dict) -> dict:
    async with http.get(f"{API}/autocalls/{call['autocall_id']}") as r:
        if r.status != 200:
            return call
        calls = (await r.json())["calls"]["data"]
    return next((c for c in calls if c["id"] == call["id"]), call)


async def report(call: dict):
    number = call["number"]

    if call["status"] in MISSED:
        text = f"{number} {MISSED[call['status']]}, попыток: {call['tries']}"
        if call["status"] in ("no_answer", "busy"):
            text += "\nскорее всего спам"
        await bot.send_message(OWNER_ID, text)
        return

    # поля и расшифровка дописываются ещё секунд десять после звонка
    await asyncio.sleep(15)
    call = await fresh(call)

    data = call.get("data") or {}
    lines = [f"<b>{label}:</b> {html.escape(str(data[key]))}" for key, label in FIELDS.items() if data.get(key)]
    await bot.send_message(OWNER_ID, f"поговорила с {number}\n\n" + "\n".join(lines), parse_mode="HTML")

    async with http.get(f"{API}/calls/{call['id']}/transcript") as r:
        text = (await r.json()).get("text") if r.status == 200 else None
    if text:
        quote = html.escape(text[:3500])
        await bot.send_message(OWNER_ID, f"<blockquote expandable>{quote}</blockquote>", parse_mode="HTML")

    async with http.get(f"{API}/calls/recordings/{call['id']}") as r:
        if r.status != 200:
            return
        wav = await r.read()

    voice = await to_voice(wav)
    if voice:
        await bot.send_voice(OWNER_ID, BufferedInputFile(voice, "call.ogg"))
        return
    await bot.send_document(OWNER_ID, BufferedInputFile(wav, f"{number}.wav"))


async def hook(request: web.Request) -> web.Response:
    if request.match_info["secret"] != HOOK_SECRET:
        raise web.HTTPNotFound()

    call = (await request.json()).get("call")
    if not call or call["status"] == "in_queue" or call["id"] in seen:
        return web.Response()

    seen.add(call["id"])
    asyncio.create_task(report(call))
    return web.Response()


async def main():
    global http
    http = ClientSession(headers={"Authorization": f"Bearer {AUTOCALL_KEY}", "Accept": "application/json"})

    app = web.Application()
    app.router.add_post("/hook/{secret}", hook)
    runner = web.AppRunner(app)
    await runner.setup()
    await web.TCPSite(runner, port=PORT).start()

    try:
        await dp.start_polling(bot)
    finally:
        await http.close()


if __name__ == "__main__":
    asyncio.run(main())

По коду пара моментов.

  • Один запрос на звонок. POST /autocalls с type: agent создаёт обзвон из одного номера моим агентом. max_tries: 3 и tries_interval: 15 дают три попытки через 15 минут, record: true пишет разговор, а в callback_url прилетит результат.
  • Промежуточные события скипаю. Между попытками звонок приходит со статусом in_queue. Окончательный статус это success, если поговорили, или no_answer и busy, если нет. Реагирую только на окончательный.
  • Почему жду 15 секунд. Я заметил, что поля отчёта и расшифровка могут дописываться ещё несколько секунд после разговора у AutoCall.kz. Видимо работает суммарайзер и отдельная LLM. Поэтому бот ждёт и перечитывает звонок через GET /autocalls/{id}, чтобы прислать всё целиком, а не половину.
  • Вебхуку отвечаю сразу. Вебхук сразу отдаёт 200. Дубли одного и того же звонка бот отбрасывает.
  • Голосовое вместо файла. Запись приходит в WAV. Если стоит ffmpeg, бот перегоняет её в OGG/Opus, и в телеге это обычное голосовое с волной и с кнопкой "Расшифровка". Если нет ffmpeg, то пришлёт файлом.

Шаг 4. Запуск

терминал

pip install aiogram aiohttp
export TELEGRAM_TOKEN=123456:ABC
export OWNER_ID=123456789
export AUTOCALL_KEY=ваш_ключ
export AGENT_ID=id_агента
export PUBLIC_URL=https://адрес-бота
export HOOK_SECRET=любая-длинная-строка
python bot.py

Боту нужен публичный https-адрес, на него AutoCall.kz шлёт результат звонка. Сервер для этого не обязателен: бота можно гонять прямо с ноута и выставить наружу через Cloudflare Tunnel. Команда cloudflared tunnel --url http://localhost:8080 выдаёт адрес, его и пишите в PUBLIC_URL.

Код целиком, с README и примером настроек, лежит на GitHub. Форкайте и пилите под себя.

Сколько это стоит

Плачу только за разговор и посекундно: связь плюс работа ИИ.

Тариф Минута разговора с секретарём Запись Недозвон 1 раз Недозвон 3 раза
BRONZE 148.8 ₸ 5 ₸ 0 ₸ 0.7 ₸
SILVER 102.6 ₸ 3 ₸ 0 ₸ 0.5 ₸
GOLD 79.8 ₸ 0.5 ₸ 0 ₸ 0.3 ₸

Обычный такой разговор укладывается в минуту. Актуальные цены и условия на странице тарифов.

Я закинул 1200 ₸ на баланс и за неделю потратилось около 190 ₸.

Что ещё можно допилить

  • Создать другого агента для других целей (передать какую-то информацию или напоминать об оплате) и выбирать цели в самом боте с помощью кнопок.
  • Жду когда появится функционал по входящим звонкам в AutoCall.kz и тогда я смогу переадресовывать звонки со своего телефона в AutoCall.kz и получать сразу итог в Telegram боте.

Надеюсь эта статья будет полезна всем, кто не любит говорить по телефону и не любит голосовые сообщения ;)

Команда AutoCall.kz готова предоставить бонусы на баланс в личном кабинете если вы предложите свою статью о вашем кейсе работы с нашим сервисом.

Частые вопросы

Это законно, перезванивать незнакомцу голосом ИИ?

Вы перезваниваете на номер, который сам вам звонил. Это обычный обратный звонок, только его делает ваш секретарь. Агент AutoCall.kz не выдаёт себя за человека: спросят «вы робот?», честно ответит, что он ИИ-помощник. Для рассылок по чужим базам бот не подходит, каждый обзвон проходит модерацию.

Нужен ли сервер, чтобы запустить бота?

Нужен публичный https-адрес, на который AutoCall.kz пришлёт результат звонка. Бота можно запустить прямо с ноутбука и выставить наружу через Cloudflare Tunnel одной командой, а можно на любом недорогом VPS.

Как быстро секретарь перезвонит?

Каждый обзвон проходит автоматическую модерацию. Обычно это секунды, иногда обзвон уходит модератору-человеку. Звонки идут только в окне, разрешённом тарифом. Если трубку не взяли, секретарь попробует ещё дважды с интервалом 15 минут.

Секретарь говорит по-казахски?

Да. По умолчанию разговор идёт на русском, а если собеседник переходит на казахский, агент тоже переходит на казахский.

Что делать, если мне по ошибке перевели деньги на Kaspi и просят вернуть?

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

Про голосовых агентов подробнее в статье ИИ-агент или сценарий: что выбрать для обзвона, а полная спецификация API в openapi.yaml.

Соберите своего секретаря за вечер

Регистрация бесплатная, на тестовом балансе хватит, чтобы поговорить с агентом вживую.

Попробовать бесплатно →