simbiot-harness/scripts/needs_survey.py

143 lines
No EOL
6.9 KiB
Python
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python
"""Хардас — сборщик потребностей клиентов студии (t-261002072440-c482ca).
Мандат Alex (голосовое m-516173358): поставить хардас клиентам студии,
которые его ждут; потребностиclients собирать самим, дёргая Alex
поменьше. Этот скрипт — двигалка фазы «сбор»:
survey — отправить опросник потребностей клиенту в его чат
(Telegram через штатный tg_inbox — тот же бот, что и реплаи;
ключ идемпотентности задаётся --key, повтор = SKIP без дубля);
collect — пройтись по недавним чатам (TG-инбокс стора) и выписать
сигналы потребностей в docs/hardass/needs/needs-<period>.md
(дедуп: повторный запуск не плодит копий — перезаписывает
период); потребности агрегируются, сырье остаётся в сторе.
Использование:
./venv/bin/python scripts/needs_survey.py survey --key needs-2026-10-w1
./venv/bin/python scripts/needs_survey.py collect
Опросник сформулирован по фактическому составу поставки (см.
docs/hardass/INSTALL.md, PACKAGE-SIMBIOT-PRESALE.md): спрашиваем о том,
что харнес реально умеет сейчас.
"""
import argparse
import json
import os
import sys
from datetime import datetime, timezone
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, ROOT)
NEEDS_DIR = os.path.join(ROOT, "docs", "hardass", "needs")
SURVEY_TEXT = """🤖 Хардас подключён — опрос потребностей (1 мин, можно голосом)
Я — ваш личный хардас Simbiot: живу тактами, сам выполняю задачи и
докладываю по графику. Чтобы настроить меня под вас, ответьте на 4
коротких блока — можно в одном сообщении или голосовыми:
1️⃣ РУТИНА: какие 2-3 повторяющихся дела хочется отдать хардасу
(например: отчёты, мониторинг, файлы, письма, посты).
2️⃣ ЧАТ И РИТМ: как удобнее общаться — здесь (Telegram-бот) или
на портале (страница чата)? Прислать дневной итог работы в удобное время?
3️⃣ ЗНАНИЯ: что хардасу стоит знать о вашем деле (ссылки/доки/описание
процессов) — пришлите что есть, я проиндексирую и буду отвечать по ним.
4️⃣ БОЛЬ ПРЯМО СЕЙЧАС: что сегодня делается вручную и раздражает больше
всего? С этого начну — первую автоматизацию возьму в работу сам.
Ответы копятся в базе потребностей — реализация идёт по ним без
лишних вопросов. 🚀"""
SURVEY_KEY = "needs-survey-v1"
def send_survey(key: str) -> str:
"""Отправка опросника: идемпотентно через deliver_artifact."""
from harness import commands, tg_inbox, es
es.init_db()
aid = f"msg:{key or SURVEY_KEY}"
def _send():
return bool(tg_inbox.notify_chat(SURVEY_TEXT))
ok = commands.deliver_artifact(aid, kind="message", send=_send,
owner="needs-survey", caption="needs-survey")
return ("OK: опросник отправлен" if ok
else "SKIP: уже доставлен (или некому отправить / чужой захват)")
def _signals_from_inbox(limit_per_chat: int = 12):
"""Свежие входящие из стора по всем чатам (без сырья наружу — тезисы)."""
from harness import es
model = es.run_projections()
out = {}
for mid, m in sorted((model.get("inbox") or {}).items(),
key=lambda kv: str(kv[1].get("date") or ""))[-60:]:
if not isinstance(m, dict):
continue
chat = str(m.get("chat_id") or "?")
text = str(m.get("transcript") or m.get("text") or "").strip()
if not text:
continue
out.setdefault(chat, []).append({
"mid": mid,
"who": str(m.get("first_name") or m.get("username") or "")[:40],
"date": str(m.get("date") or "")[:19],
"kind": str(m.get("kind") or "text"),
"text": text[:600],
})
return {c: v[-limit_per_chat:] for c, v in out.items()}
def collect() -> str:
"""Агрегация сигналов потребностей в docs/hardass/needs/."""
os.makedirs(NEEDS_DIR, exist_ok=True)
period = datetime.now(timezone.utc).strftime("%Y-%m-%d_%H")
path = os.path.join(NEEDS_DIR, f"needs-{period}.md")
signals = _signals_from_inbox()
if not signals:
return "нет свежих входящих в сторе — collect сделан (пустой период)"
lines = [
f"# Потребности клиентов — {period} (UTC)",
"",
"Автосборка needs_survey.py (хардас, t-261002072440). Источник: "
"входящие чатов харнеса (TG/портал). Сырёё — тезисы, полные тексты "
"в ES-сторе (MessageReceived).",
"",
]
n = 0
for chat, msgs in signals.items():
lines.append(f"## Чат {chat}")
for m in msgs:
lines.append(f"- `{m['mid']}` {m['date']} {m['who'] or '—'} "
f"({m['kind']}): {m['text'][:300]}")
n += 1
lines.append("")
body = "\n".join(lines)
# перезапись того же периода — дедуп по построению (периодный файл)
tmp = path + ".tmp"
with open(tmp, "w", encoding="utf-8") as f:
f.write(body)
os.replace(tmp, path)
return f"OK: {n} сигналов из {len(signals)} чатов → {path}"
def main():
ap = argparse.ArgumentParser(description="Сбор потребностей клиентов хардаса")
ap.add_argument("cmd", choices=["survey", "collect"])
ap.add_argument("--key", default=SURVEY_KEY,
help="ключ идемпотентности отправки (повтор = SKIP)")
args = ap.parse_args()
if args.cmd == "survey":
print(send_survey(args.key))
else:
print(collect())
if __name__ == "__main__":
main()