Files
store-scraper-bot/internal/repository/apple_app_repository.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

62 lines
1.8 KiB
Go

package repository
import (
"context"
"fmt"
"time"
"github.com/miti99/store-scraper-bot-go/internal/config"
"github.com/miti99/store-scraper-bot-go/internal/model"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
// AppleAppRepository caches Apple app responses in the "apple_app" collection.
// Java schema stores _id=appId, app=AppleAppResponse, millis=cache timestamp.
type AppleAppRepository struct {
collection *mongo.Collection
}
func NewAppleAppRepository() *AppleAppRepository {
return &AppleAppRepository{collection: GetCollection("apple_app")}
}
func (r *AppleAppRepository) Get(ctx context.Context, appID string) (*model.AppleApp, error) {
app := &model.AppleApp{}
err := r.collection.FindOne(ctx, bson.M{"_id": appID}).Decode(app)
if err != nil {
if err == mongo.ErrNoDocuments {
return nil, nil
}
return nil, fmt.Errorf("failed to get apple app: %w", err)
}
return app, nil
}
func (r *AppleAppRepository) Save(ctx context.Context, app *model.AppleApp) error {
opts := options.Replace().SetUpsert(true)
_, err := r.collection.ReplaceOne(ctx, bson.M{"_id": app.ID}, app, opts)
if err != nil {
return fmt.Errorf("failed to save apple app: %w", err)
}
return nil
}
// GetCached returns a cached entry if it exists and has not expired (per
// AppCacheSeconds). Returns (nil, nil) on cache miss.
func (r *AppleAppRepository) GetCached(appID string) (*model.AppleApp, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
app, err := r.Get(ctx, appID)
if err != nil || app == nil {
return nil, err
}
cacheMillis := int64(config.GlobalConfig.AppCacheSeconds) * 1000
if app.IsExpired(time.Now().UnixMilli(), cacheMillis) {
return nil, nil
}
return app, nil
}