Files
2dph/bin/chats/sync_cmd.go
T
eSlider 6847233183 bin/chats: Phase 1 MVP — Telegram sync/import/index/facts/apply
- bin/chats/ — nested Go module (как bin/kbsearch/)
  - sync telegram — MCP JSON-RPC клиент, 31 личный чат, 922 сообщения
  - import — конвертация JSONL → MD с YAML frontmatter
  - index — делегирует bin/kb/index --corpus (132 leafs в brain)
  - facts — regex extraction phone/email/linkedin с валидацией
    (исключены: даты, суммы, номера карт, инвойсы)
  - apply — oo CLI cross-check + dry-run
- Source interface для будущих WhatsApp/LinkedIn
- 4 system tests (import, facts, empty, roundtrip) — синтетические данные
- bin/chat — build+exec wrapper
- docs/chat-import-plan.md — прогресс, пути к env (без секретов)

Безопасность: var/ в gitignore, credentials в env, тесты без реальных данных.
2026-08-12 23:59:29 +01:00

109 lines
3.1 KiB
Go

package main
import (
"context"
"flag"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"time"
)
func runSyncTelegram(args []string) int {
fs := flag.NewFlagSet("chats sync telegram", flag.ContinueOnError)
limit := fs.Int("limit", 0, "max messages per chat (0 = all)")
phone := fs.String("phone", "", "phone number (default env TELEGRAM_PHONE)")
help := fs.Bool("help", false, "")
fs.SetOutput(os.Stderr)
if err := fs.Parse(args); err != nil {
return 2
}
if *help {
fmt.Fprintln(os.Stderr, "usage: chats sync telegram [--limit N] [--phone PHONE]")
return 0
}
apiIDStr := envVar("TELEGRAM_API_ID", "")
apiHash := envVar("TELEGRAM_API_HASH", "")
sessionStr := envVar("TELEGRAM_SESSION_STRING", "")
phoneNum := *phone
if phoneNum == "" {
phoneNum = envVar("TELEGRAM_PHONE", "")
}
if apiIDStr == "" || apiHash == "" || phoneNum == "" {
fmt.Fprintln(os.Stderr, "chats: need TELEGRAM_API_ID, TELEGRAM_API_HASH, TELEGRAM_PHONE in env")
return 2
}
apiID, err := strconv.Atoi(apiIDStr)
if err != nil {
fmt.Fprintf(os.Stderr, "chats: invalid TELEGRAM_API_ID %q\n", apiIDStr)
return 2
}
// Locate telegram-mcp directory
mcpDirs := []string{
envVar("TELEGRAM_MCP_DIR", ""),
"/mnt/8TB/projects/eslider/mcp-servers/telegram-mcp",
filepath.Join(os.Getenv("HOME"), "projects", "eSlider", "mcp-servers", "telegram-mcp"),
}
var mcpDir string
for _, d := range mcpDirs {
if d != "" {
if _, err := os.Stat(filepath.Join(d, "main.py")); err == nil {
mcpDir = d
break
}
}
}
if mcpDir == "" {
fmt.Fprintln(os.Stderr, "chats: telegram-mcp not found; set TELEGRAM_MCP_DIR")
return 1
}
if sessionStr == "" {
// Fallback: try to read from telegram-mcp .env
envPath := filepath.Join(mcpDir, ".env")
if data, err := os.ReadFile(envPath); err == nil {
for _, line := range strings.Split(string(data), "\n") {
if strings.HasPrefix(line, "TELEGRAM_SESSION_STRING=") {
sessionStr = strings.TrimPrefix(line, "TELEGRAM_SESSION_STRING=")
sessionStr = strings.Trim(sessionStr, "\"'")
break
}
}
}
}
if sessionStr == "" {
// Also try the env file at /mnt/8TB/projects/eslider/mcp-servers/telegram.env
envPath := "/mnt/8TB/projects/eslider/mcp-servers/telegram.env"
if data, err := os.ReadFile(envPath); err == nil {
for _, line := range strings.Split(string(data), "\n") {
if strings.HasPrefix(line, "TELEGRAM_SESSION_STRING=") {
sessionStr = strings.TrimPrefix(line, "TELEGRAM_SESSION_STRING=")
sessionStr = strings.Trim(sessionStr, "\"'")
break
}
}
}
}
if sessionStr == "" {
fmt.Fprintln(os.Stderr, "chats: TELEGRAM_SESSION_STRING not found in env or .env files")
return 1
}
src := NewTelegramMCPSource(apiID, apiHash, phoneNum, sessionStr, mcpDir)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancel()
start := time.Now()
if err := src.Sync(ctx, chatsDir(), *limit); err != nil {
fmt.Fprintf(os.Stderr, "chats sync telegram: %v\n", err)
return 1
}
fmt.Printf("chats sync telegram: completed in %s\n", time.Since(start).Round(time.Millisecond))
return 0
}