Files
store-scraper-bot/internal/model/google_app.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

92 lines
5.6 KiB
Go

package model
// Category mirrors Java GoogleAppResponse.Category nested record.
type Category struct {
Name string `bson:"name" json:"name"`
ID string `bson:"id" json:"id"`
}
// Feature mirrors Java GoogleAppResponse.Feature nested record.
type Feature struct {
Title string `bson:"title" json:"title"`
Description string `bson:"description" json:"description"`
}
// GoogleAppResponse mirrors Java GoogleAppResponse record (api/google/response).
type GoogleAppResponse struct {
Title string `bson:"title" json:"title"`
Description string `bson:"description" json:"description"`
DescriptionHTML string `bson:"descriptionHTML" json:"descriptionHTML"`
Summary string `bson:"summary" json:"summary"`
Installs string `bson:"installs" json:"installs"`
MinInstalls int64 `bson:"minInstalls" json:"minInstalls"`
MaxInstalls int64 `bson:"maxInstalls" json:"maxInstalls"`
Score float64 `bson:"score" json:"score"`
ScoreText string `bson:"scoreText" json:"scoreText"`
Ratings int64 `bson:"ratings" json:"ratings"`
Reviews int64 `bson:"reviews" json:"reviews"`
Histogram map[string]int64 `bson:"histogram" json:"histogram"`
Price float64 `bson:"price" json:"price"`
Free bool `bson:"free" json:"free"`
Currency string `bson:"currency" json:"currency"`
PriceText string `bson:"priceText" json:"priceText"`
OffersIAP bool `bson:"offersIAP" json:"offersIAP"`
IAPRange string `bson:"IAPRange" json:"IAPRange"`
AndroidVersion string `bson:"androidVersion" json:"androidVersion"`
AndroidVersionText string `bson:"androidVersionText" json:"androidVersionText"`
AndroidMaxVersion string `bson:"androidMaxVersion" json:"androidMaxVersion"`
Developer string `bson:"developer" json:"developer"`
DeveloperID string `bson:"developerId" json:"developerId"`
DeveloperEmail string `bson:"developerEmail" json:"developerEmail"`
DeveloperWebsite string `bson:"developerWebsite" json:"developerWebsite"`
DeveloperAddress string `bson:"developerAddress" json:"developerAddress"`
DeveloperLegalName string `bson:"developerLegalName" json:"developerLegalName"`
DeveloperLegalEmail string `bson:"developerLegalEmail" json:"developerLegalEmail"`
DeveloperLegalAddress string `bson:"developerLegalAddress" json:"developerLegalAddress"`
DeveloperLegalPhoneNumber string `bson:"developerLegalPhoneNumber" json:"developerLegalPhoneNumber"`
PrivacyPolicy string `bson:"privacyPolicy" json:"privacyPolicy"`
DeveloperInternalID string `bson:"developerInternalID" json:"developerInternalID"`
Genre string `bson:"genre" json:"genre"`
GenreID string `bson:"genreId" json:"genreId"`
Categories []Category `bson:"categories" json:"categories"`
Icon string `bson:"icon" json:"icon"`
HeaderImage string `bson:"headerImage" json:"headerImage"`
Screenshots []string `bson:"screenshots" json:"screenshots"`
Video string `bson:"video" json:"video"`
VideoImage string `bson:"videoImage" json:"videoImage"`
PreviewVideo string `bson:"previewVideo" json:"previewVideo"`
ContentRating string `bson:"contentRating" json:"contentRating"`
ContentRatingDescription string `bson:"contentRatingDescription" json:"contentRatingDescription"`
AdSupported bool `bson:"adSupported" json:"adSupported"`
Released string `bson:"released" json:"released"`
Updated int64 `bson:"updated" json:"updated"` // ms since epoch
Version string `bson:"version" json:"version"`
RecentChanges string `bson:"recentChanges" json:"recentChanges"`
Comments []string `bson:"comments" json:"comments"`
Preregister bool `bson:"preregister" json:"preregister"`
EarlyAccessEnabled bool `bson:"earlyAccessEnabled" json:"earlyAccessEnabled"`
IsAvailableInPlayPass bool `bson:"isAvailableInPlayPass" json:"isAvailableInPlayPass"`
EditorsChoice bool `bson:"editorsChoice" json:"editorsChoice"`
Features []Feature `bson:"features" json:"features"`
AppID string `bson:"appId" json:"appId"`
URL string `bson:"url" json:"url"`
}
type GoogleApp struct {
AbstractModel `bson:",inline"`
App GoogleAppResponse `bson:"app" json:"app"`
Millis int64 `bson:"millis" json:"millis"`
}
func NewGoogleApp(appID string, response GoogleAppResponse, millis int64) *GoogleApp {
return &GoogleApp{
AbstractModel: AbstractModel{ID: appID, Class: "GoogleApp"},
App: response,
Millis: millis,
}
}
func (g *GoogleApp) IsExpired(nowMillis, cacheMillis int64) bool {
return nowMillis-g.Millis > cacheMillis
}