package main

import (
	"context"
	"database/sql"
	"embed"
	"log"
	"log/slog"
	"os"
	"os/signal"

	"github.com/firebase/genkit/go/ai"
	"github.com/firebase/genkit/go/genkit"
	"github.com/firebase/genkit/go/plugins/compat_oai/openai"
	"github.com/jackc/pgx/v5/pgxpool"
	"github.com/jackc/pgx/v5/stdlib"
	"github.com/openai/openai-go/option"
	"github.com/pressly/goose/v3"

	"mimi/internal/bot"
	ghscraper "mimi/internal/provider/github/scraper"
	tgscraper "mimi/internal/provider/telegram/scraper"
)

//go:embed prompts
var prompts embed.FS

//go:embed sql/migrations/*.sql
var embedMigrations embed.FS

const (
	logseqGraphEnv      = "LOGSEQ_GRAPH_PATH"
	telegramBotTokenEnv = "TELEGRAM_BOT_API_TOKEN"
	openrouterApiKeyEnv = "OPENROUTER_API_KEY"
	openrouterApiUrlEnv = "OPENROUTER_API_URL"
)

func main() {
	ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
	defer cancel()

	var missingEnvVars []string
	tgBotToken := os.Getenv(telegramBotTokenEnv)
	if tgBotToken == "" {
		missingEnvVars = append(missingEnvVars, telegramBotTokenEnv)
	}

	logseqPath := os.Getenv(logseqGraphEnv)
	if logseqPath == "" {
		missingEnvVars = append(missingEnvVars, logseqGraphEnv)
	}

	openrouterApiKey := os.Getenv(openrouterApiKeyEnv)
	if openrouterApiKey == "" {
		missingEnvVars = append(missingEnvVars, openrouterApiKeyEnv)
	}

	openrouterBaseURL := os.Getenv(openrouterApiUrlEnv)
	if openrouterBaseURL == "" {
		missingEnvVars = append(missingEnvVars, openrouterApiUrlEnv)
	}

	if len(missingEnvVars) > 0 {
		log.Fatalf("env variables %#v are missing", missingEnvVars)
	}

	oai := &openai.OpenAI{
		APIKey: openrouterApiKey,
		Opts: []option.RequestOption{
			option.WithBaseURL(openrouterBaseURL),
		},
	}

	g := genkit.Init(ctx,
		genkit.WithPlugins(oai),
		genkit.WithPromptFS(prompts),
		genkit.WithPromptDir("prompts"),
		genkit.WithDefaultModel("openai/anthropic/claude-sonnet-4.5"),
	)
	oai.DefineModel("google/gemini-2.5-flash", ai.ModelOptions{
		Label:    "Gemini 2.5 Flash Preview 04-17",
		Versions: []string{"google/gemini-2.5-flash"},
		Supports: &ai.ModelSupports{
			Multiturn:   true,
			Tools:       true,
			ToolChoice:  true,
			SystemRole:  true,
			Media:       true,
			Constrained: ai.ConstrainedSupportNone,
		},
		Stage: ai.ModelStageStable,
	})
	oai.DefineModel("perplexity/sonar-pro", ai.ModelOptions{
		Label:    "Perplexity Sonar Pro",
		Versions: []string{},
		Supports: &ai.ModelSupports{
			Multiturn:   true,
			Tools:       true,
			ToolChoice:  true,
			SystemRole:  true,
			Media:       true,
			Constrained: ai.ConstrainedSupportNone,
		},
		Stage: ai.ModelStageStable,
	})

	pool, err := pgxpool.New(ctx, os.Getenv("DATABASE_URL"))
	if err != nil {
		log.Fatalf("failed to connect to postgres with: %s", err)
	}

	// Run database migrations
	RunMigrations(pool)

	go func() {
		err := tgscraper.Run(ctx, pool, g)
		if err != nil {
			log.Fatalf("Telegram scraper exited with %s", err)
		} else {
			slog.Info("Telegram scraper exited without an error")
		}
	}()
	go func() {
		err := ghscraper.Run(ctx, pool)
		if err != nil {
			log.Fatalf("GitHub scraper exited with %s", err)
		} else {
			slog.Info("GitHub scraper exited without an error")
		}
	}()
	go func() {
		err := bot.Start(ctx, tgBotToken, logseqPath, g)
		if err != nil {
			log.Fatalf("Telegram bot exited with %s", err)
		} else {
			slog.Info("Telegram bot exited without an error")
		}
	}()

	<-ctx.Done()
}

func RunMigrations(pool *pgxpool.Pool) {
	// Goose needs *sql.DB type so we need to transform *pgxpool.Pool into it
	config := pool.Config().ConnConfig
	connStr := stdlib.RegisterConnConfig(config)
	db, err := sql.Open("pgx", connStr)
	if err != nil {
		log.Fatalf("failed to open database for migrations: %v", err)
	}
	defer db.Close()

	goose.SetBaseFS(embedMigrations)
	if err := goose.SetDialect("postgres"); err != nil {
		log.Fatalf("failed to set goose dialect: %v", err)
	}

	if err := goose.Up(db, "sql/migrations"); err != nil {
		log.Fatalf("failed to run migrations: %v", err)
	}

	slog.Info("Migrations completed successfully")
}

Homonyms

cyberia/research/mimi/cmd/telegram-session/main.go
cyberia/research/mimi/cmd/fetch-github-projects/main.go
cyberia/research/mimi/cmd/scraper/telegram/main.go

Graph