Files
store-scraper-bot/cmd/bot/main.go
T
tiennm99 e695d9b223 feat: port logic from Java original to align with current Java behavior
- Switch Telegram parse mode to HTML to match Java client.
- Rename command identifiers to Java's: delgroup/delapple/delgoogle/
  checkappscore/rawappleapp/rawgoogleapp.
- Move admin singleton to "common" collection at _id="admin".
- Store group _id as string-of-int64 to match Java AbstractModel schema.
- Add `class` discriminator field on persisted models.
- Expand AppleAppResponse (42 fields) and GoogleAppResponse (63 fields,
  with nested Category/Feature) to match Java records.
- Add AppleAppRequest (track id or bundle id) / GoogleAppRequest types.
- Implement Raw* commands as Telegram document attachments instead of
  truncated text.
- Group-authorization gate (admin.HasGroup(chatId)) on non-admin commands.
- Cache TTL via millis timestamp; round score to 1 decimal; weekend silent
  send in scheduler.
- Match Java table renderer: " │ " separators, "─┼─" rows every 5 lines.
- Prefer MONGODB_CONNECTION_STRING env (Java parity); auto-extract DB
  name from URI with fallback to "store-scraper-bot".

Note: This is an AI-assisted port (not human-verified). Behavior parity
with the Java implementation has not been tested end-to-end; treat as a
starting point.
2026-04-25 20:26:25 +07:00

77 lines
2.2 KiB
Go

package main
import (
"log"
"os"
"os/signal"
"syscall"
"github.com/miti99/store-scraper-bot-go/internal/api/apple"
"github.com/miti99/store-scraper-bot-go/internal/api/google"
"github.com/miti99/store-scraper-bot-go/internal/bot"
"github.com/miti99/store-scraper-bot-go/internal/config"
"github.com/miti99/store-scraper-bot-go/internal/repository"
"github.com/miti99/store-scraper-bot-go/internal/scheduler"
"go.uber.org/zap"
)
func main() {
// Load configuration
cfg, err := config.Load()
if err != nil {
log.Fatalf("Failed to load configuration: %v", err)
}
defer cfg.Logger.Sync()
cfg.Logger.Info("Starting Store Scraper Bot",
zap.String("env", string(cfg.Env)),
zap.String("commit", cfg.SourceCommit))
// Initialize MongoDB
if err := repository.InitMongoDB(cfg); err != nil {
cfg.Logger.Fatal("Failed to initialize MongoDB", zap.Error(err))
}
defer repository.Close()
// Initialize repositories
adminRepo := repository.NewAdminRepository()
groupRepo := repository.NewGroupRepository()
appleAppRepo := repository.NewAppleAppRepository()
googleAppRepo := repository.NewGoogleAppRepository()
// Java parity: ensure the singleton "common/admin" document exists.
if err := adminRepo.Init(); err != nil {
cfg.Logger.Fatal("Failed to init admin singleton", zap.Error(err))
}
// Initialize scrapers
appleScraper := apple.NewAppleScraper(appleAppRepo, cfg)
googleScraper := google.NewGoogleScraper(googleAppRepo, cfg)
// Initialize bot
telegramBot, err := bot.NewBot(cfg, adminRepo, groupRepo, appleScraper, googleScraper)
if err != nil {
cfg.Logger.Fatal("Failed to initialize bot", zap.Error(err))
}
// Initialize and start scheduler
sched := scheduler.NewScheduler(cfg, telegramBot, adminRepo, groupRepo, appleScraper, googleScraper)
if err := sched.Start(); err != nil {
cfg.Logger.Fatal("Failed to start scheduler", zap.Error(err))
}
defer sched.Stop()
// Start bot in a goroutine
go func() {
cfg.Logger.Info("Starting Telegram bot polling")
telegramBot.Start()
}()
// Wait for interrupt signal
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
<-sigChan
cfg.Logger.Info("Received shutdown signal, stopping bot...")
}