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.
This commit is contained in:
tiennm99 committed 2026-04-25 20:26:25 +07:00
1 parent cfc8d67641
commit e695d9b223
34 files changed
+1132 -1149

No files matched your search

+24 -34
View File
@@ -1,58 +1,48 @@
package command
import (
"encoding/json"
"fmt"
"strings"
"strconv"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
"github.com/miti99/store-scraper-bot-go/internal/api/apple"
"github.com/miti99/store-scraper-bot-go/internal/api/apple/request"
"github.com/miti99/store-scraper-bot-go/internal/config"
)
// /rawappleapp <id|appId> [country=vn] — Java RawAppleAppCommand.
// Sends the raw upstream JSON as a Telegram document attachment.
type RawAppleAppCommand struct {
BaseCommand
appleScraper *apple.AppleScraper
cfg *config.Config
scraper *apple.AppleScraper
}
func NewRawAppleAppCommand(cfg *config.Config, appleScraper *apple.AppleScraper) *RawAppleAppCommand {
return &RawAppleAppCommand{
BaseCommand: BaseCommand{cfg: cfg},
appleScraper: appleScraper,
}
func NewRawAppleAppCommand(cfg *config.Config, scraper *apple.AppleScraper) *RawAppleAppCommand {
return &RawAppleAppCommand{cfg: cfg, scraper: scraper}
}
func (c *RawAppleAppCommand) Execute(message *tgbotapi.Message) string {
if !c.requireAdmin(message) {
return "You are not authorized to use this command."
func (c *RawAppleAppCommand) Execute(msg *tgbotapi.Message, sender Sender) {
args := splitArgs(msg.CommandArguments())
if len(args) < 1 || len(args) > 2 {
_ = sender.SendMessage(msg.Chat.ID, "Invalid arguments")
return
}
args := strings.Fields(message.CommandArguments())
if len(args) == 0 {
return "Usage: /rawapple <appId> [country]\nExample: /rawapple com.example.app vn"
}
appID := args[0]
country := "vn"
if len(args) > 1 {
if len(args) == 2 {
country = args[1]
}
app, err := c.appleScraper.GetApp(appID, country)
if err != nil {
return fmt.Sprintf("Failed to fetch app: %v", err)
var req request.AppleAppRequest
if trackID, err := strconv.ParseInt(args[0], 10, 64); err == nil {
req = request.ByTrackID(trackID, country)
} else {
req = request.ByBundleID(args[0], country)
}
jsonData, err := json.MarshalIndent(app, "", " ")
if err != nil {
return fmt.Sprintf("Failed to marshal JSON: %v", err)
raw, err := c.scraper.RawApp(req)
if err != nil || raw == "" {
_ = sender.SendMessage(msg.Chat.ID, "Error when request app info")
return
}
// Telegram has a message size limit, so we might need to truncate
jsonStr := string(jsonData)
if len(jsonStr) > 4000 {
jsonStr = jsonStr[:4000] + "\n...(truncated)"
}
return fmt.Sprintf("```json\n%s\n```", jsonStr)
_ = sender.SendDocument(msg.Chat.ID, fmt.Sprintf("%s.json", args[0]), raw)
}