mirror of
https://github.com/tiennm99/store-scraper-bot.git
synced 2026-10-14 08:18:36 +00:00
feat(core): add logic converted from original repo by Claude Code
This commit is contained in:
1 parent
f243bc9b88
commit
9b03eb007a
38 files changed
+2717
No files matched your search
@@ -0,0 +1,70 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
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/config"
|
||||
"github.com/miti99/store-scraper-bot-go/internal/repository"
|
||||
)
|
||||
|
||||
type AddAppleAppCommand struct {
|
||||
BaseCommand
|
||||
adminRepo *repository.AdminRepository
|
||||
groupRepo *repository.GroupRepository
|
||||
appleScraper *apple.AppleScraper
|
||||
}
|
||||
|
||||
func NewAddAppleAppCommand(
|
||||
cfg *config.Config,
|
||||
adminRepo *repository.AdminRepository,
|
||||
groupRepo *repository.GroupRepository,
|
||||
appleScraper *apple.AppleScraper,
|
||||
) *AddAppleAppCommand {
|
||||
return &AddAppleAppCommand{
|
||||
BaseCommand: BaseCommand{cfg: cfg},
|
||||
adminRepo: adminRepo,
|
||||
groupRepo: groupRepo,
|
||||
appleScraper: appleScraper,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AddAppleAppCommand) Execute(message *tgbotapi.Message) string {
|
||||
if !c.requireAdmin(message) {
|
||||
return "You are not authorized to use this command."
|
||||
}
|
||||
|
||||
groupID := message.Chat.ID
|
||||
hasGroup, err := c.adminRepo.HasGroup(groupID)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Failed to check group: %v", err)
|
||||
}
|
||||
if !hasGroup {
|
||||
return "This group is not registered. Please use /addgroup first."
|
||||
}
|
||||
|
||||
args := strings.Fields(message.CommandArguments())
|
||||
if len(args) == 0 {
|
||||
return "Usage: /addapple <appId> [country]\nExample: /addapple com.example.app vn"
|
||||
}
|
||||
|
||||
appID := args[0]
|
||||
country := "vn"
|
||||
if len(args) > 1 {
|
||||
country = args[1]
|
||||
}
|
||||
|
||||
// Verify app exists
|
||||
app, err := c.appleScraper.GetApp(appID, country)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Failed to fetch app from store: %v", err)
|
||||
}
|
||||
|
||||
if err := c.groupRepo.AddAppleApp(groupID, appID, country); err != nil {
|
||||
return fmt.Sprintf("Failed to add app: %v", err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Apple app added successfully:\n*%s*\nApp ID: %s\nCountry: %s\nScore: %.1f", app.Title, appID, country, app.Score)
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"github.com/miti99/store-scraper-bot-go/internal/api/google"
|
||||
"github.com/miti99/store-scraper-bot-go/internal/config"
|
||||
"github.com/miti99/store-scraper-bot-go/internal/repository"
|
||||
)
|
||||
|
||||
type AddGoogleAppCommand struct {
|
||||
BaseCommand
|
||||
adminRepo *repository.AdminRepository
|
||||
groupRepo *repository.GroupRepository
|
||||
googleScraper *google.GoogleScraper
|
||||
}
|
||||
|
||||
func NewAddGoogleAppCommand(
|
||||
cfg *config.Config,
|
||||
adminRepo *repository.AdminRepository,
|
||||
groupRepo *repository.GroupRepository,
|
||||
googleScraper *google.GoogleScraper,
|
||||
) *AddGoogleAppCommand {
|
||||
return &AddGoogleAppCommand{
|
||||
BaseCommand: BaseCommand{cfg: cfg},
|
||||
adminRepo: adminRepo,
|
||||
groupRepo: groupRepo,
|
||||
googleScraper: googleScraper,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AddGoogleAppCommand) Execute(message *tgbotapi.Message) string {
|
||||
if !c.requireAdmin(message) {
|
||||
return "You are not authorized to use this command."
|
||||
}
|
||||
|
||||
groupID := message.Chat.ID
|
||||
hasGroup, err := c.adminRepo.HasGroup(groupID)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Failed to check group: %v", err)
|
||||
}
|
||||
if !hasGroup {
|
||||
return "This group is not registered. Please use /addgroup first."
|
||||
}
|
||||
|
||||
args := strings.Fields(message.CommandArguments())
|
||||
if len(args) == 0 {
|
||||
return "Usage: /addgoogle <appId> [country]\nExample: /addgoogle com.example.app vn"
|
||||
}
|
||||
|
||||
appID := args[0]
|
||||
country := "vn"
|
||||
if len(args) > 1 {
|
||||
country = args[1]
|
||||
}
|
||||
|
||||
// Verify app exists
|
||||
app, err := c.googleScraper.GetApp(appID, country)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Failed to fetch app from store: %v", err)
|
||||
}
|
||||
|
||||
if err := c.groupRepo.AddGoogleApp(groupID, appID, country); err != nil {
|
||||
return fmt.Sprintf("Failed to add app: %v", err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Google app added successfully:\n*%s*\nApp ID: %s\nCountry: %s\nScore: %.1f", app.Title, appID, country, app.Score)
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"github.com/miti99/store-scraper-bot-go/internal/config"
|
||||
"github.com/miti99/store-scraper-bot-go/internal/repository"
|
||||
)
|
||||
|
||||
type AddGroupCommand struct {
|
||||
BaseCommand
|
||||
adminRepo *repository.AdminRepository
|
||||
groupRepo *repository.GroupRepository
|
||||
}
|
||||
|
||||
func NewAddGroupCommand(cfg *config.Config, adminRepo *repository.AdminRepository, groupRepo *repository.GroupRepository) *AddGroupCommand {
|
||||
return &AddGroupCommand{
|
||||
BaseCommand: BaseCommand{cfg: cfg},
|
||||
adminRepo: adminRepo,
|
||||
groupRepo: groupRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AddGroupCommand) Execute(message *tgbotapi.Message) string {
|
||||
if !c.requireAdmin(message) {
|
||||
return "You are not authorized to use this command."
|
||||
}
|
||||
|
||||
groupID := message.Chat.ID
|
||||
if err := c.adminRepo.AddGroup(groupID); err != nil {
|
||||
return fmt.Sprintf("Failed to add group: %v", err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Group %d has been added successfully.", groupID)
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
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/google"
|
||||
"github.com/miti99/store-scraper-bot-go/internal/config"
|
||||
"github.com/miti99/store-scraper-bot-go/internal/model"
|
||||
"github.com/miti99/store-scraper-bot-go/internal/repository"
|
||||
"github.com/miti99/store-scraper-bot-go/internal/util"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type CheckAppCommand struct {
|
||||
BaseCommand
|
||||
adminRepo *repository.AdminRepository
|
||||
groupRepo *repository.GroupRepository
|
||||
appleScraper *apple.AppleScraper
|
||||
googleScraper *google.GoogleScraper
|
||||
}
|
||||
|
||||
func NewCheckAppCommand(
|
||||
cfg *config.Config,
|
||||
adminRepo *repository.AdminRepository,
|
||||
groupRepo *repository.GroupRepository,
|
||||
appleScraper *apple.AppleScraper,
|
||||
googleScraper *google.GoogleScraper,
|
||||
) *CheckAppCommand {
|
||||
return &CheckAppCommand{
|
||||
BaseCommand: BaseCommand{cfg: cfg},
|
||||
adminRepo: adminRepo,
|
||||
groupRepo: groupRepo,
|
||||
appleScraper: appleScraper,
|
||||
googleScraper: googleScraper,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CheckAppCommand) Execute(message *tgbotapi.Message) string {
|
||||
if !c.requireAdmin(message) {
|
||||
return "You are not authorized to use this command."
|
||||
}
|
||||
|
||||
groupID := message.Chat.ID
|
||||
hasGroup, err := c.adminRepo.HasGroup(groupID)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Failed to check group: %v", err)
|
||||
}
|
||||
if !hasGroup {
|
||||
return "This group is not registered."
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer cancel()
|
||||
|
||||
group, err := c.groupRepo.Get(ctx, groupID)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Failed to get group: %v", err)
|
||||
}
|
||||
|
||||
if len(group.AppleApps) == 0 && len(group.GoogleApps) == 0 {
|
||||
return "No apps in this group."
|
||||
}
|
||||
|
||||
nonUpdatedApps := make([]model.NonUpdatedApp, 0)
|
||||
now := time.Now().In(c.cfg.VietnamLocation)
|
||||
|
||||
// Check Apple apps
|
||||
for _, appInfo := range group.AppleApps {
|
||||
app, err := c.appleScraper.GetApp(appInfo.AppID, appInfo.Country)
|
||||
if err != nil {
|
||||
c.cfg.Logger.Error("Failed to fetch Apple app",
|
||||
zap.String("appId", appInfo.AppID),
|
||||
zap.Error(err))
|
||||
continue
|
||||
}
|
||||
|
||||
updatedTime, err := time.Parse(time.RFC3339, app.Updated)
|
||||
if err != nil {
|
||||
c.cfg.Logger.Error("Failed to parse update time",
|
||||
zap.String("appId", appInfo.AppID),
|
||||
zap.String("updated", app.Updated),
|
||||
zap.Error(err))
|
||||
continue
|
||||
}
|
||||
|
||||
daysSinceUpdate := int(now.Sub(updatedTime).Hours() / 24)
|
||||
if daysSinceUpdate > c.cfg.NumDaysWarningNotUpdated {
|
||||
nonUpdatedApps = append(nonUpdatedApps, model.NonUpdatedApp{
|
||||
AppID: appInfo.AppID,
|
||||
Title: app.Title,
|
||||
Days: daysSinceUpdate,
|
||||
Updated: app.Updated[:10], // Just the date part
|
||||
Score: app.Score,
|
||||
Reviews: app.Reviews,
|
||||
Ratings: app.Ratings,
|
||||
IsApple: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Check Google apps
|
||||
for _, appInfo := range group.GoogleApps {
|
||||
app, err := c.googleScraper.GetApp(appInfo.AppID, appInfo.Country)
|
||||
if err != nil {
|
||||
c.cfg.Logger.Error("Failed to fetch Google app",
|
||||
zap.String("appId", appInfo.AppID),
|
||||
zap.Error(err))
|
||||
continue
|
||||
}
|
||||
|
||||
updatedTime := time.UnixMilli(app.Updated)
|
||||
daysSinceUpdate := int(now.Sub(updatedTime).Hours() / 24)
|
||||
|
||||
if daysSinceUpdate > c.cfg.NumDaysWarningNotUpdated {
|
||||
nonUpdatedApps = append(nonUpdatedApps, model.NonUpdatedApp{
|
||||
AppID: appInfo.AppID,
|
||||
Title: app.Title,
|
||||
Days: daysSinceUpdate,
|
||||
Updated: updatedTime.Format("2006-01-02"),
|
||||
Score: app.Score,
|
||||
Reviews: app.Reviews,
|
||||
Ratings: app.Ratings,
|
||||
IsApple: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if len(nonUpdatedApps) == 0 {
|
||||
return fmt.Sprintf("All apps are up to date (checked within %d days).", c.cfg.NumDaysWarningNotUpdated)
|
||||
}
|
||||
|
||||
// Build table
|
||||
var rows [][]string
|
||||
for _, app := range nonUpdatedApps {
|
||||
store := "Google"
|
||||
if app.IsApple {
|
||||
store = "Apple"
|
||||
}
|
||||
|
||||
rows = append(rows, []string{
|
||||
util.TruncateString(app.Title, 30),
|
||||
store,
|
||||
fmt.Sprintf("%d", app.Days),
|
||||
app.Updated,
|
||||
fmt.Sprintf("%.1f", app.Score),
|
||||
fmt.Sprintf("%v", app.Reviews),
|
||||
util.FormatNumber(app.Ratings),
|
||||
})
|
||||
}
|
||||
|
||||
headers := []string{"App", "Store", "Days", "Updated", "Score", "Reviews", "Ratings"}
|
||||
table := util.BuildTable(headers, rows)
|
||||
|
||||
var sb strings.Builder
|
||||
sb.WriteString(fmt.Sprintf("*Non-Updated Apps Report*\nGroup: %d\nApps not updated in >%d days: *%d*\n\n",
|
||||
groupID, c.cfg.NumDaysWarningNotUpdated, len(nonUpdatedApps)))
|
||||
sb.WriteString(table)
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
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/google"
|
||||
"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/util"
|
||||
)
|
||||
|
||||
type CheckAppScoresCommand struct {
|
||||
BaseCommand
|
||||
adminRepo *repository.AdminRepository
|
||||
groupRepo *repository.GroupRepository
|
||||
appleScraper *apple.AppleScraper
|
||||
googleScraper *google.GoogleScraper
|
||||
}
|
||||
|
||||
func NewCheckAppScoresCommand(
|
||||
cfg *config.Config,
|
||||
adminRepo *repository.AdminRepository,
|
||||
groupRepo *repository.GroupRepository,
|
||||
appleScraper *apple.AppleScraper,
|
||||
googleScraper *google.GoogleScraper,
|
||||
) *CheckAppScoresCommand {
|
||||
return &CheckAppScoresCommand{
|
||||
BaseCommand: BaseCommand{cfg: cfg},
|
||||
adminRepo: adminRepo,
|
||||
groupRepo: groupRepo,
|
||||
appleScraper: appleScraper,
|
||||
googleScraper: googleScraper,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CheckAppScoresCommand) Execute(message *tgbotapi.Message) string {
|
||||
if !c.requireAdmin(message) {
|
||||
return "You are not authorized to use this command."
|
||||
}
|
||||
|
||||
groupID := message.Chat.ID
|
||||
hasGroup, err := c.adminRepo.HasGroup(groupID)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Failed to check group: %v", err)
|
||||
}
|
||||
if !hasGroup {
|
||||
return "This group is not registered."
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
group, err := c.groupRepo.Get(ctx, groupID)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Failed to get group: %v", err)
|
||||
}
|
||||
|
||||
if len(group.AppleApps) == 0 && len(group.GoogleApps) == 0 {
|
||||
return "No apps in this group."
|
||||
}
|
||||
|
||||
var rows [][]string
|
||||
|
||||
// Check Apple apps
|
||||
for _, appInfo := range group.AppleApps {
|
||||
app, err := c.appleScraper.GetApp(appInfo.AppID, appInfo.Country)
|
||||
if err != nil {
|
||||
rows = append(rows, []string{
|
||||
util.TruncateString(appInfo.AppID, 30),
|
||||
"Apple",
|
||||
"Error",
|
||||
"0",
|
||||
"0",
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
rows = append(rows, []string{
|
||||
util.TruncateString(app.Title, 30),
|
||||
"Apple",
|
||||
fmt.Sprintf("%.1f", app.Score),
|
||||
fmt.Sprintf("%d", app.Reviews),
|
||||
util.FormatNumber(app.Ratings),
|
||||
})
|
||||
}
|
||||
|
||||
// Check Google apps
|
||||
for _, appInfo := range group.GoogleApps {
|
||||
app, err := c.googleScraper.GetApp(appInfo.AppID, appInfo.Country)
|
||||
if err != nil {
|
||||
rows = append(rows, []string{
|
||||
util.TruncateString(appInfo.AppID, 30),
|
||||
"Google",
|
||||
"Error",
|
||||
"0",
|
||||
"0",
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
rows = append(rows, []string{
|
||||
util.TruncateString(app.Title, 30),
|
||||
"Google",
|
||||
fmt.Sprintf("%.1f", app.Score),
|
||||
fmt.Sprintf("%d", app.Reviews),
|
||||
util.FormatNumber(app.Ratings),
|
||||
})
|
||||
}
|
||||
|
||||
headers := []string{"App", "Store", "Score", "Reviews", "Ratings"}
|
||||
table := util.BuildTable(headers, rows)
|
||||
|
||||
var sb strings.Builder
|
||||
sb.WriteString(fmt.Sprintf("*App Scores Report*\nGroup: %d\n\n", groupID))
|
||||
sb.WriteString(table)
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"github.com/miti99/store-scraper-bot-go/internal/config"
|
||||
)
|
||||
|
||||
type Command interface {
|
||||
Execute(message *tgbotapi.Message) string
|
||||
}
|
||||
|
||||
type BaseCommand struct {
|
||||
cfg *config.Config
|
||||
}
|
||||
|
||||
func (c *BaseCommand) isAdmin(userID int64) bool {
|
||||
return c.cfg.IsAdmin(userID)
|
||||
}
|
||||
|
||||
func (c *BaseCommand) requireAdmin(message *tgbotapi.Message) bool {
|
||||
if !c.isAdmin(message.From.ID) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"github.com/miti99/store-scraper-bot-go/internal/config"
|
||||
"github.com/miti99/store-scraper-bot-go/internal/repository"
|
||||
)
|
||||
|
||||
type DeleteAppleAppCommand struct {
|
||||
BaseCommand
|
||||
adminRepo *repository.AdminRepository
|
||||
groupRepo *repository.GroupRepository
|
||||
}
|
||||
|
||||
func NewDeleteAppleAppCommand(cfg *config.Config, adminRepo *repository.AdminRepository, groupRepo *repository.GroupRepository) *DeleteAppleAppCommand {
|
||||
return &DeleteAppleAppCommand{
|
||||
BaseCommand: BaseCommand{cfg: cfg},
|
||||
adminRepo: adminRepo,
|
||||
groupRepo: groupRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *DeleteAppleAppCommand) Execute(message *tgbotapi.Message) string {
|
||||
if !c.requireAdmin(message) {
|
||||
return "You are not authorized to use this command."
|
||||
}
|
||||
|
||||
groupID := message.Chat.ID
|
||||
hasGroup, err := c.adminRepo.HasGroup(groupID)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Failed to check group: %v", err)
|
||||
}
|
||||
if !hasGroup {
|
||||
return "This group is not registered."
|
||||
}
|
||||
|
||||
args := strings.Fields(message.CommandArguments())
|
||||
if len(args) == 0 {
|
||||
return "Usage: /deleteapple <appId>\nExample: /deleteapple com.example.app"
|
||||
}
|
||||
|
||||
appID := args[0]
|
||||
|
||||
if err := c.groupRepo.RemoveAppleApp(groupID, appID); err != nil {
|
||||
return fmt.Sprintf("Failed to remove app: %v", err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Apple app %s has been removed successfully.", appID)
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"github.com/miti99/store-scraper-bot-go/internal/config"
|
||||
"github.com/miti99/store-scraper-bot-go/internal/repository"
|
||||
)
|
||||
|
||||
type DeleteGoogleAppCommand struct {
|
||||
BaseCommand
|
||||
adminRepo *repository.AdminRepository
|
||||
groupRepo *repository.GroupRepository
|
||||
}
|
||||
|
||||
func NewDeleteGoogleAppCommand(cfg *config.Config, adminRepo *repository.AdminRepository, groupRepo *repository.GroupRepository) *DeleteGoogleAppCommand {
|
||||
return &DeleteGoogleAppCommand{
|
||||
BaseCommand: BaseCommand{cfg: cfg},
|
||||
adminRepo: adminRepo,
|
||||
groupRepo: groupRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *DeleteGoogleAppCommand) Execute(message *tgbotapi.Message) string {
|
||||
if !c.requireAdmin(message) {
|
||||
return "You are not authorized to use this command."
|
||||
}
|
||||
|
||||
groupID := message.Chat.ID
|
||||
hasGroup, err := c.adminRepo.HasGroup(groupID)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Failed to check group: %v", err)
|
||||
}
|
||||
if !hasGroup {
|
||||
return "This group is not registered."
|
||||
}
|
||||
|
||||
args := strings.Fields(message.CommandArguments())
|
||||
if len(args) == 0 {
|
||||
return "Usage: /deletegoogle <appId>\nExample: /deletegoogle com.example.app"
|
||||
}
|
||||
|
||||
appID := args[0]
|
||||
|
||||
if err := c.groupRepo.RemoveGoogleApp(groupID, appID); err != nil {
|
||||
return fmt.Sprintf("Failed to remove app: %v", err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Google app %s has been removed successfully.", appID)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"github.com/miti99/store-scraper-bot-go/internal/config"
|
||||
"github.com/miti99/store-scraper-bot-go/internal/repository"
|
||||
)
|
||||
|
||||
type DeleteGroupCommand struct {
|
||||
BaseCommand
|
||||
adminRepo *repository.AdminRepository
|
||||
groupRepo *repository.GroupRepository
|
||||
}
|
||||
|
||||
func NewDeleteGroupCommand(cfg *config.Config, adminRepo *repository.AdminRepository, groupRepo *repository.GroupRepository) *DeleteGroupCommand {
|
||||
return &DeleteGroupCommand{
|
||||
BaseCommand: BaseCommand{cfg: cfg},
|
||||
adminRepo: adminRepo,
|
||||
groupRepo: groupRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *DeleteGroupCommand) Execute(message *tgbotapi.Message) string {
|
||||
if !c.requireAdmin(message) {
|
||||
return "You are not authorized to use this command."
|
||||
}
|
||||
|
||||
groupID := message.Chat.ID
|
||||
|
||||
if err := c.adminRepo.RemoveGroup(groupID); err != nil {
|
||||
return fmt.Sprintf("Failed to remove group: %v", err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := c.groupRepo.Delete(ctx, groupID); err != nil {
|
||||
return fmt.Sprintf("Group removed from admin but failed to delete group data: %v", err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Group %d has been deleted successfully.", groupID)
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"github.com/miti99/store-scraper-bot-go/internal/config"
|
||||
)
|
||||
|
||||
type InfoCommand struct {
|
||||
BaseCommand
|
||||
}
|
||||
|
||||
func NewInfoCommand(cfg *config.Config) *InfoCommand {
|
||||
return &InfoCommand{
|
||||
BaseCommand: BaseCommand{cfg: cfg},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *InfoCommand) Execute(message *tgbotapi.Message) string {
|
||||
return fmt.Sprintf(`*Store Scraper Bot - Go Edition*
|
||||
|
||||
*Version:* 1.0.0
|
||||
*Environment:* %s
|
||||
*Source Commit:* %s
|
||||
*Go Version:* %s
|
||||
*Bot Username:* @%s
|
||||
|
||||
*Commands:*
|
||||
/addgroup - Add current group to monitoring
|
||||
/deletegroup - Remove current group
|
||||
/listgroup - List all monitored groups
|
||||
/addapple <appId> [country] - Add Apple app
|
||||
/deleteapple <appId> - Remove Apple app
|
||||
/addgoogle <appId> [country] - Add Google app
|
||||
/deletegoogle <appId> - Remove Google app
|
||||
/listapp - List apps in current group
|
||||
/checkapp - Check for non-updated apps
|
||||
/checkappscores - Check app scores
|
||||
/rawapple <appId> [country] - Get raw Apple data
|
||||
/rawgoogle <appId> [country] - Get raw Google data
|
||||
/info - Show this info`,
|
||||
c.cfg.Env,
|
||||
c.cfg.SourceCommit,
|
||||
runtime.Version(),
|
||||
c.cfg.TelegramBotUsername,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"github.com/miti99/store-scraper-bot-go/internal/config"
|
||||
"github.com/miti99/store-scraper-bot-go/internal/repository"
|
||||
)
|
||||
|
||||
type ListAppCommand struct {
|
||||
BaseCommand
|
||||
adminRepo *repository.AdminRepository
|
||||
groupRepo *repository.GroupRepository
|
||||
}
|
||||
|
||||
func NewListAppCommand(cfg *config.Config, adminRepo *repository.AdminRepository, groupRepo *repository.GroupRepository) *ListAppCommand {
|
||||
return &ListAppCommand{
|
||||
BaseCommand: BaseCommand{cfg: cfg},
|
||||
adminRepo: adminRepo,
|
||||
groupRepo: groupRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ListAppCommand) Execute(message *tgbotapi.Message) string {
|
||||
if !c.requireAdmin(message) {
|
||||
return "You are not authorized to use this command."
|
||||
}
|
||||
|
||||
groupID := message.Chat.ID
|
||||
hasGroup, err := c.adminRepo.HasGroup(groupID)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Failed to check group: %v", err)
|
||||
}
|
||||
if !hasGroup {
|
||||
return "This group is not registered."
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
group, err := c.groupRepo.Get(ctx, groupID)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Failed to get group: %v", err)
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
sb.WriteString("*Apps in this group:*\n\n")
|
||||
|
||||
if len(group.AppleApps) > 0 {
|
||||
sb.WriteString("*Apple Apps:*\n")
|
||||
for i, app := range group.AppleApps {
|
||||
sb.WriteString(fmt.Sprintf("%d. %s (%s)\n", i+1, app.AppID, app.Country))
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
|
||||
if len(group.GoogleApps) > 0 {
|
||||
sb.WriteString("*Google Apps:*\n")
|
||||
for i, app := range group.GoogleApps {
|
||||
sb.WriteString(fmt.Sprintf("%d. %s (%s)\n", i+1, app.AppID, app.Country))
|
||||
}
|
||||
}
|
||||
|
||||
if len(group.AppleApps) == 0 && len(group.GoogleApps) == 0 {
|
||||
return "No apps in this group."
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"github.com/miti99/store-scraper-bot-go/internal/config"
|
||||
"github.com/miti99/store-scraper-bot-go/internal/repository"
|
||||
)
|
||||
|
||||
type ListGroupCommand struct {
|
||||
BaseCommand
|
||||
adminRepo *repository.AdminRepository
|
||||
}
|
||||
|
||||
func NewListGroupCommand(cfg *config.Config, adminRepo *repository.AdminRepository) *ListGroupCommand {
|
||||
return &ListGroupCommand{
|
||||
BaseCommand: BaseCommand{cfg: cfg},
|
||||
adminRepo: adminRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ListGroupCommand) Execute(message *tgbotapi.Message) string {
|
||||
if !c.requireAdmin(message) {
|
||||
return "You are not authorized to use this command."
|
||||
}
|
||||
|
||||
groups, err := c.adminRepo.GetAllGroups()
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Failed to get groups: %v", err)
|
||||
}
|
||||
|
||||
if len(groups) == 0 {
|
||||
return "No groups found."
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
sb.WriteString(fmt.Sprintf("*Total groups: %d*\n\n", len(groups)))
|
||||
for i, groupID := range groups {
|
||||
sb.WriteString(fmt.Sprintf("%d. %d\n", i+1, groupID))
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
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/config"
|
||||
)
|
||||
|
||||
type RawAppleAppCommand struct {
|
||||
BaseCommand
|
||||
appleScraper *apple.AppleScraper
|
||||
}
|
||||
|
||||
func NewRawAppleAppCommand(cfg *config.Config, appleScraper *apple.AppleScraper) *RawAppleAppCommand {
|
||||
return &RawAppleAppCommand{
|
||||
BaseCommand: BaseCommand{cfg: cfg},
|
||||
appleScraper: appleScraper,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RawAppleAppCommand) Execute(message *tgbotapi.Message) string {
|
||||
if !c.requireAdmin(message) {
|
||||
return "You are not authorized to use this command."
|
||||
}
|
||||
|
||||
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 {
|
||||
country = args[1]
|
||||
}
|
||||
|
||||
app, err := c.appleScraper.GetApp(appID, country)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Failed to fetch app: %v", err)
|
||||
}
|
||||
|
||||
jsonData, err := json.MarshalIndent(app, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Failed to marshal JSON: %v", err)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"github.com/miti99/store-scraper-bot-go/internal/api/google"
|
||||
"github.com/miti99/store-scraper-bot-go/internal/config"
|
||||
)
|
||||
|
||||
type RawGoogleAppCommand struct {
|
||||
BaseCommand
|
||||
googleScraper *google.GoogleScraper
|
||||
}
|
||||
|
||||
func NewRawGoogleAppCommand(cfg *config.Config, googleScraper *google.GoogleScraper) *RawGoogleAppCommand {
|
||||
return &RawGoogleAppCommand{
|
||||
BaseCommand: BaseCommand{cfg: cfg},
|
||||
googleScraper: googleScraper,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RawGoogleAppCommand) Execute(message *tgbotapi.Message) string {
|
||||
if !c.requireAdmin(message) {
|
||||
return "You are not authorized to use this command."
|
||||
}
|
||||
|
||||
args := strings.Fields(message.CommandArguments())
|
||||
if len(args) == 0 {
|
||||
return "Usage: /rawgoogle <appId> [country]\nExample: /rawgoogle com.example.app vn"
|
||||
}
|
||||
|
||||
appID := args[0]
|
||||
country := "vn"
|
||||
if len(args) > 1 {
|
||||
country = args[1]
|
||||
}
|
||||
|
||||
app, err := c.googleScraper.GetApp(appID, country)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Failed to fetch app: %v", err)
|
||||
}
|
||||
|
||||
jsonData, err := json.MarshalIndent(app, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Failed to marshal JSON: %v", err)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
Reference in new issue
Block a user