From 5554d75534998e736ee0a081a662746c8e79ad47 Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Wed, 5 Nov 2025 23:06:24 +0700 Subject: [PATCH] Add app and group management commands to bot Introduces commands for adding, deleting, listing, and checking Apple and Google apps per group, as well as group management commands. Refactors models for app and group storage, adds caching for app data, and updates repositories to support cache expiration. Improves bot initialization and command registration logic. --- .../api/apple/AppStoreScraper.java | 40 ++++++++-- .../api/google/GooglePlayScraper.java | 41 ++++++++-- .../storescraperbot/bot/StoreScrapeBot.java | 19 +++++ .../bot/command/AddAppleAppCommand.java | 45 +++++++++++ .../bot/command/AddGoogleAppCommand.java | 47 ++++++++++++ .../bot/command/AddGroupCommand.java | 18 +++-- .../bot/command/CheckAppCommand.java | 74 +++++++++++++++++++ .../bot/command/DeleteAppleAppCommand.java | 45 +++++++++++ .../bot/command/DeleteGoogleAppCommand.java | 46 ++++++++++++ .../bot/command/DeleteGroupCommand.java | 52 +++++++++++++ .../bot/command/InfoCommand.java | 29 ++++++++ .../bot/command/ListAppCommand.java | 47 ++++++++++++ ...roupCommand.java => ListGroupCommand.java} | 13 ++-- .../storescraperbot/constant/Constant.java | 2 + .../storescraperbot/model/AbstractModel.java | 8 +- .../miti99/storescraperbot/model/Admin.java | 9 +-- .../storescraperbot/model/AppleApp.java | 7 +- .../storescraperbot/model/GoogleApp.java | 7 +- .../miti99/storescraperbot/model/Group.java | 15 +--- .../repository/AbstractRepository.java | 27 +++++-- .../repository/AppleAppRepository.java | 6 ++ .../repository/GoogleAppRepository.java | 8 +- 22 files changed, 539 insertions(+), 66 deletions(-) create mode 100644 src/main/java/com/miti99/storescraperbot/bot/command/AddAppleAppCommand.java create mode 100644 src/main/java/com/miti99/storescraperbot/bot/command/AddGoogleAppCommand.java create mode 100644 src/main/java/com/miti99/storescraperbot/bot/command/CheckAppCommand.java create mode 100644 src/main/java/com/miti99/storescraperbot/bot/command/DeleteAppleAppCommand.java create mode 100644 src/main/java/com/miti99/storescraperbot/bot/command/DeleteGoogleAppCommand.java create mode 100644 src/main/java/com/miti99/storescraperbot/bot/command/DeleteGroupCommand.java create mode 100644 src/main/java/com/miti99/storescraperbot/bot/command/InfoCommand.java create mode 100644 src/main/java/com/miti99/storescraperbot/bot/command/ListAppCommand.java rename src/main/java/com/miti99/storescraperbot/bot/command/{GetGroupCommand.java => ListGroupCommand.java} (75%) diff --git a/src/main/java/com/miti99/storescraperbot/api/apple/AppStoreScraper.java b/src/main/java/com/miti99/storescraperbot/api/apple/AppStoreScraper.java index 1e7370b..a518cf1 100644 --- a/src/main/java/com/miti99/storescraperbot/api/apple/AppStoreScraper.java +++ b/src/main/java/com/miti99/storescraperbot/api/apple/AppStoreScraper.java @@ -2,6 +2,7 @@ package com.miti99.storescraperbot.api.apple; import com.miti99.storescraperbot.api.apple.request.AppleAppRequest; import com.miti99.storescraperbot.api.apple.response.AppleAppResponse; +import com.miti99.storescraperbot.repository.AppleAppRepository; import com.miti99.storescraperbot.util.GsonUtil; import java.net.URI; import java.net.http.HttpClient; @@ -9,8 +10,16 @@ import java.net.http.HttpClient.Redirect; import java.net.http.HttpRequest; import java.net.http.HttpRequest.BodyPublishers; import java.net.http.HttpResponse.BodyHandlers; +import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; import lombok.SneakyThrows; +import lombok.extern.log4j.Log4j2; +@Log4j2 +@NoArgsConstructor(access = AccessLevel.PRIVATE) public class AppStoreScraper { public static final String BASE_URL = "https://miti-app-store-scraper.vercel.app/"; @@ -24,13 +33,34 @@ public class AppStoreScraper { .POST(BodyPublishers.ofString(GsonUtil.toJson(request))) .build(); - var body = + try (var httpClient = HttpClient.newBuilder() .followRedirects(Redirect.NORMAL) // .connectTimeout(Duration.ofMillis(TIMEOUT)) - .build() - .send(httpRequest, BodyHandlers.ofString()) - .body(); - return GsonUtil.fromJson(body, AppleAppResponse.class); + .build()) { + var body = httpClient.send(httpRequest, BodyHandlers.ofString()).body(); + return GsonUtil.fromJson(body, AppleAppResponse.class); + } + } + + public static LocalDate getLastUpdateOfApp(String appId) { + boolean isInCache = AppleAppRepository.INSTANCE.exist(appId); + AppleAppResponse response = null; + if (isInCache) { + var app = AppleAppRepository.INSTANCE.load(appId); + response = app.getApp(); + } else { + response = app(new AppleAppRequest(appId)); + AppleAppRepository.INSTANCE.init(appId); + var app = AppleAppRepository.INSTANCE.load(appId); + app.setApp(response); + AppleAppRepository.INSTANCE.save(appId, app); + } + if (response != null) { + return LocalDate.ofInstant(Instant.parse(response.updated()), ZoneId.systemDefault()); + } else { + log.error("response is null"); + return LocalDate.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault()); + } } } diff --git a/src/main/java/com/miti99/storescraperbot/api/google/GooglePlayScraper.java b/src/main/java/com/miti99/storescraperbot/api/google/GooglePlayScraper.java index 02b3c09..159d466 100644 --- a/src/main/java/com/miti99/storescraperbot/api/google/GooglePlayScraper.java +++ b/src/main/java/com/miti99/storescraperbot/api/google/GooglePlayScraper.java @@ -2,6 +2,7 @@ package com.miti99.storescraperbot.api.google; import com.miti99.storescraperbot.api.google.request.GoogleAppRequest; import com.miti99.storescraperbot.api.google.response.GoogleAppResponse; +import com.miti99.storescraperbot.repository.GoogleAppRepository; import com.miti99.storescraperbot.util.GsonUtil; import java.net.URI; import java.net.http.HttpClient; @@ -9,8 +10,16 @@ import java.net.http.HttpClient.Redirect; import java.net.http.HttpRequest; import java.net.http.HttpRequest.BodyPublishers; import java.net.http.HttpResponse.BodyHandlers; +import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; import lombok.SneakyThrows; +import lombok.extern.log4j.Log4j2; +@Log4j2 +@NoArgsConstructor(access = AccessLevel.PRIVATE) public class GooglePlayScraper { public static final String BASE_URL = "https://miti-google-play-scraper.vercel.app/"; @@ -24,13 +33,35 @@ public class GooglePlayScraper { .POST(BodyPublishers.ofString(GsonUtil.toJson(request))) .build(); - var body = + try (var httpClient = HttpClient.newBuilder() // .connectTimeout(Duration.ofMillis(TIMEOUT)) .followRedirects(Redirect.NORMAL) - .build() - .send(httpRequest, BodyHandlers.ofString()) - .body(); - return GsonUtil.fromJson(body, GoogleAppResponse.class); + .build()) { + var body = httpClient.send(httpRequest, BodyHandlers.ofString()).body(); + return GsonUtil.fromJson(body, GoogleAppResponse.class); + } + } + + public static LocalDate getLastUpdateOfApp(String appId) { + boolean isInCache = GoogleAppRepository.INSTANCE.exist(appId); + GoogleAppResponse response = null; + if (isInCache) { + var app = GoogleAppRepository.INSTANCE.load(appId); + response = app.getApp(); + } else { + response = app(new GoogleAppRequest(appId)); + GoogleAppRepository.INSTANCE.init(appId); + var app = GoogleAppRepository.INSTANCE.load(appId); + app.setApp(response); + GoogleAppRepository.INSTANCE.save(appId, app); + } + long lastUpdateMillis = 0; + if (response != null) { + lastUpdateMillis = response.updated(); + } else { + log.error("response is null"); + } + return LocalDate.ofInstant(Instant.ofEpochMilli(lastUpdateMillis), ZoneId.systemDefault()); } } diff --git a/src/main/java/com/miti99/storescraperbot/bot/StoreScrapeBot.java b/src/main/java/com/miti99/storescraperbot/bot/StoreScrapeBot.java index df9d0f2..daf3dc6 100644 --- a/src/main/java/com/miti99/storescraperbot/bot/StoreScrapeBot.java +++ b/src/main/java/com/miti99/storescraperbot/bot/StoreScrapeBot.java @@ -1,6 +1,14 @@ package com.miti99.storescraperbot.bot; +import com.miti99.storescraperbot.bot.command.AddAppleAppCommand; +import com.miti99.storescraperbot.bot.command.AddGoogleAppCommand; import com.miti99.storescraperbot.bot.command.AddGroupCommand; +import com.miti99.storescraperbot.bot.command.CheckAppCommand; +import com.miti99.storescraperbot.bot.command.DeleteAppleAppCommand; +import com.miti99.storescraperbot.bot.command.DeleteGroupCommand; +import com.miti99.storescraperbot.bot.command.InfoCommand; +import com.miti99.storescraperbot.bot.command.ListAppCommand; +import com.miti99.storescraperbot.bot.command.ListGroupCommand; import lombok.extern.log4j.Log4j2; import org.telegram.telegrambots.extensions.bots.commandbot.CommandLongPollingTelegramBot; import org.telegram.telegrambots.meta.api.methods.commands.SetMyCommands; @@ -15,7 +23,18 @@ public class StoreScrapeBot extends CommandLongPollingTelegramBot { StoreScrapeBot() { super(StoreScrapeBotTelegramClient.INSTANCE, true, StoreScrapeBotUsernameSupplier.INSTANCE); + register(InfoCommand.INSTANCE); + register(AddGroupCommand.INSTANCE); + register(DeleteGroupCommand.INSTANCE); + register(ListGroupCommand.INSTANCE); + + register(AddAppleAppCommand.INSTANCE); + register(DeleteAppleAppCommand.INSTANCE); + register(AddGoogleAppCommand.INSTANCE); + register(DeleteAppleAppCommand.INSTANCE); + register(ListAppCommand.INSTANCE); + register(CheckAppCommand.INSTANCE); setMyCommands(); } diff --git a/src/main/java/com/miti99/storescraperbot/bot/command/AddAppleAppCommand.java b/src/main/java/com/miti99/storescraperbot/bot/command/AddAppleAppCommand.java new file mode 100644 index 0000000..631e3df --- /dev/null +++ b/src/main/java/com/miti99/storescraperbot/bot/command/AddAppleAppCommand.java @@ -0,0 +1,45 @@ +package com.miti99.storescraperbot.bot.command; + +import com.miti99.storescraperbot.bot.StoreScrapeBotTelegramClient; +import com.miti99.storescraperbot.repository.AdminRepository; +import com.miti99.storescraperbot.repository.GroupRepository; +import org.telegram.telegrambots.meta.api.objects.User; +import org.telegram.telegrambots.meta.api.objects.chat.Chat; +import org.telegram.telegrambots.meta.generics.TelegramClient; + +public class AddAppleAppCommand extends BaseStoreScraperBotCommand { + public static final AddAppleAppCommand INSTANCE = new AddAppleAppCommand(); + + AddAppleAppCommand() { + super("addapple", ". Thêm Apple app vào danh sách theo dõi của nhóm"); + } + + @Override + protected void executeCommand( + TelegramClient telegramClient, User user, Chat chat, String[] arguments) { + var admin = AdminRepository.INSTANCE.load(); + if (!admin.getGroups().contains(chat.getId())) { + StoreScrapeBotTelegramClient.INSTANCE.sendMessage( + chat.getId(), "Group is not allowed to use bot"); + return; + } + + if (arguments.length != 1) { + StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), "Invalid arguments"); + return; + } + + var appId = arguments[0]; + long groupId = chat.getId(); + var group = GroupRepository.INSTANCE.load(groupId); + + if (group.getAppleApps().contains(appId)) { + StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), "Apple app is already added"); + return; + } + + group.getAppleApps().add(appId); + GroupRepository.INSTANCE.save(groupId, group); + StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), "Apple app added successfully"); + } +} diff --git a/src/main/java/com/miti99/storescraperbot/bot/command/AddGoogleAppCommand.java b/src/main/java/com/miti99/storescraperbot/bot/command/AddGoogleAppCommand.java new file mode 100644 index 0000000..ed85769 --- /dev/null +++ b/src/main/java/com/miti99/storescraperbot/bot/command/AddGoogleAppCommand.java @@ -0,0 +1,47 @@ +package com.miti99.storescraperbot.bot.command; + +import com.miti99.storescraperbot.bot.StoreScrapeBotTelegramClient; +import com.miti99.storescraperbot.repository.AdminRepository; +import com.miti99.storescraperbot.repository.GroupRepository; +import org.telegram.telegrambots.meta.api.objects.User; +import org.telegram.telegrambots.meta.api.objects.chat.Chat; +import org.telegram.telegrambots.meta.generics.TelegramClient; + +public class AddGoogleAppCommand extends BaseStoreScraperBotCommand { + public static final AddGoogleAppCommand INSTANCE = new AddGoogleAppCommand(); + + AddGoogleAppCommand() { + super("addgoogle", ". Thêm Google app vào danh sách theo dõi của nhóm"); + } + + @Override + protected void executeCommand( + TelegramClient telegramClient, User user, Chat chat, String[] arguments) { + var admin = AdminRepository.INSTANCE.load(); + if (!admin.getGroups().contains(chat.getId())) { + StoreScrapeBotTelegramClient.INSTANCE.sendMessage( + chat.getId(), "Group is not allowed to use bot"); + return; + } + + if (arguments.length != 1) { + StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), "Invalid arguments"); + return; + } + + var appId = arguments[0]; + long groupId = chat.getId(); + var group = GroupRepository.INSTANCE.load(groupId); + + if (group.getGoogleApps().contains(appId)) { + StoreScrapeBotTelegramClient.INSTANCE.sendMessage( + chat.getId(), "Google app is already added"); + return; + } + + group.getGoogleApps().add(appId); + GroupRepository.INSTANCE.save(groupId, group); + StoreScrapeBotTelegramClient.INSTANCE.sendMessage( + chat.getId(), "Google app added successfully"); + } +} diff --git a/src/main/java/com/miti99/storescraperbot/bot/command/AddGroupCommand.java b/src/main/java/com/miti99/storescraperbot/bot/command/AddGroupCommand.java index 72294bf..1df5f84 100644 --- a/src/main/java/com/miti99/storescraperbot/bot/command/AddGroupCommand.java +++ b/src/main/java/com/miti99/storescraperbot/bot/command/AddGroupCommand.java @@ -3,6 +3,7 @@ package com.miti99.storescraperbot.bot.command; import com.miti99.storescraperbot.bot.StoreScrapeBotTelegramClient; import com.miti99.storescraperbot.config.Config; import com.miti99.storescraperbot.repository.AdminRepository; +import com.miti99.storescraperbot.repository.GroupRepository; import org.telegram.telegrambots.meta.api.objects.User; import org.telegram.telegrambots.meta.api.objects.chat.Chat; import org.telegram.telegrambots.meta.generics.TelegramClient; @@ -22,17 +23,21 @@ public class AddGroupCommand extends BaseStoreScraperBotCommand { return; } - if (arguments.length != 1) { + if (arguments.length > 1) { StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), "Invalid arguments"); return; } long groupId; - try { - groupId = Long.parseLong(arguments[0]); - } catch (NumberFormatException e) { - StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), "Invalid groupId"); - return; + if (arguments.length == 1) { + try { + groupId = Long.parseLong(arguments[0]); + } catch (NumberFormatException e) { + StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), "Invalid groupId"); + return; + } + } else { + groupId = chat.getId(); } var admin = AdminRepository.INSTANCE.load(); @@ -41,6 +46,7 @@ public class AddGroupCommand extends BaseStoreScraperBotCommand { return; } + GroupRepository.INSTANCE.init(groupId); admin.getGroups().add(groupId); AdminRepository.INSTANCE.save(admin); StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), "Group added successfully"); diff --git a/src/main/java/com/miti99/storescraperbot/bot/command/CheckAppCommand.java b/src/main/java/com/miti99/storescraperbot/bot/command/CheckAppCommand.java new file mode 100644 index 0000000..316a875 --- /dev/null +++ b/src/main/java/com/miti99/storescraperbot/bot/command/CheckAppCommand.java @@ -0,0 +1,74 @@ +package com.miti99.storescraperbot.bot.command; + +import com.miti99.storescraperbot.api.apple.AppStoreScraper; +import com.miti99.storescraperbot.api.google.GooglePlayScraper; +import com.miti99.storescraperbot.bot.StoreScrapeBotTelegramClient; +import com.miti99.storescraperbot.constant.Constant; +import com.miti99.storescraperbot.repository.AdminRepository; +import com.miti99.storescraperbot.repository.GroupRepository; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import org.telegram.telegrambots.meta.api.objects.User; +import org.telegram.telegrambots.meta.api.objects.chat.Chat; +import org.telegram.telegrambots.meta.generics.TelegramClient; + +public class CheckAppCommand extends BaseStoreScraperBotCommand { + public static final CheckAppCommand INSTANCE = new CheckAppCommand(); + + CheckAppCommand() { + super("checkapp", "Kiểm tra cập nhật các app"); + } + + @Override + protected void executeCommand( + TelegramClient telegramClient, User user, Chat chat, String[] arguments) { + var admin = AdminRepository.INSTANCE.load(); + if (!admin.getGroups().contains(chat.getId())) { + StoreScrapeBotTelegramClient.INSTANCE.sendMessage( + chat.getId(), "Group is not allowed to use bot"); + return; + } + + if (arguments.length != 0) { + StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), "Invalid arguments"); + return; + } + + long groupId = chat.getId(); + var group = GroupRepository.INSTANCE.load(groupId); + var now = LocalDate.now(); + + var sb = new StringBuilder(); + sb.append("Apple Apps:\n"); + sb.append("\n"); + sb.append("%-20s | %-10s | %-4s | %-2s\n".formatted("AppId", "Updated", "Days", "OK")); + sb.append("-".repeat(46)); + sb.append("\n"); + for (var appId : group.getAppleApps()) { + var updated = AppStoreScraper.getLastUpdateOfApp(appId); + long days = ChronoUnit.DAYS.between(updated, now); + boolean passed = days <= Constant.NUM_DAYS_WARNING_NOT_UPDATED; + sb.append( + "%-20s | %-10s | %-4s | %-2s\n".formatted(appId, updated, days, passed ? "✅" : "❌")); + } + sb.append("\n"); + sb.append("\n"); + sb.append("Google Apps:\n"); + sb.append("\n"); + sb.append("%-20s | %-10s | %-4s | %-2s\n".formatted("AppId", "Updated", "Date", "OK")); + sb.append("-".repeat(46)); + sb.append("\n"); + for (var appId : group.getGoogleApps()) { + var updated = GooglePlayScraper.getLastUpdateOfApp(appId); + long days = ChronoUnit.DAYS.between(updated, now); + boolean passed = days <= Constant.NUM_DAYS_WARNING_NOT_UPDATED; + sb.append( + "%-20s | %-10s | %-4s | %-2s\n".formatted(appId, updated, days, passed ? "✅" : "❌")); + } + sb.append(""); + sb.append("\n"); + + StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), sb.toString()); + } +} diff --git a/src/main/java/com/miti99/storescraperbot/bot/command/DeleteAppleAppCommand.java b/src/main/java/com/miti99/storescraperbot/bot/command/DeleteAppleAppCommand.java new file mode 100644 index 0000000..711d14e --- /dev/null +++ b/src/main/java/com/miti99/storescraperbot/bot/command/DeleteAppleAppCommand.java @@ -0,0 +1,45 @@ +package com.miti99.storescraperbot.bot.command; + +import com.miti99.storescraperbot.bot.StoreScrapeBotTelegramClient; +import com.miti99.storescraperbot.repository.AdminRepository; +import com.miti99.storescraperbot.repository.GroupRepository; +import org.telegram.telegrambots.meta.api.objects.User; +import org.telegram.telegrambots.meta.api.objects.chat.Chat; +import org.telegram.telegrambots.meta.generics.TelegramClient; + +public class DeleteAppleAppCommand extends BaseStoreScraperBotCommand { + public static final DeleteAppleAppCommand INSTANCE = new DeleteAppleAppCommand(); + + DeleteAppleAppCommand() { + super("delapple", ". Xoá Apple app khỏi danh sách theo dõi của nhóm"); + } + + @Override + protected void executeCommand( + TelegramClient telegramClient, User user, Chat chat, String[] arguments) { + var admin = AdminRepository.INSTANCE.load(); + if (!admin.getGroups().contains(chat.getId())) { + StoreScrapeBotTelegramClient.INSTANCE.sendMessage( + chat.getId(), "Group is not allowed to use bot"); + return; + } + + if (arguments.length != 1) { + StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), "Invalid arguments"); + return; + } + + var appId = arguments[0]; + long groupId = chat.getId(); + var group = GroupRepository.INSTANCE.load(groupId); + + if (!group.getAppleApps().contains(appId)) { + StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), "Apple app is not added"); + return; + } + + group.getAppleApps().remove(appId); + GroupRepository.INSTANCE.save(groupId, group); + StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), "Apple app deleted successfully"); + } +} diff --git a/src/main/java/com/miti99/storescraperbot/bot/command/DeleteGoogleAppCommand.java b/src/main/java/com/miti99/storescraperbot/bot/command/DeleteGoogleAppCommand.java new file mode 100644 index 0000000..0869a7f --- /dev/null +++ b/src/main/java/com/miti99/storescraperbot/bot/command/DeleteGoogleAppCommand.java @@ -0,0 +1,46 @@ +package com.miti99.storescraperbot.bot.command; + +import com.miti99.storescraperbot.bot.StoreScrapeBotTelegramClient; +import com.miti99.storescraperbot.repository.AdminRepository; +import com.miti99.storescraperbot.repository.GroupRepository; +import org.telegram.telegrambots.meta.api.objects.User; +import org.telegram.telegrambots.meta.api.objects.chat.Chat; +import org.telegram.telegrambots.meta.generics.TelegramClient; + +public class DeleteGoogleAppCommand extends BaseStoreScraperBotCommand { + public static final DeleteGoogleAppCommand INSTANCE = new DeleteGoogleAppCommand(); + + DeleteGoogleAppCommand() { + super("delgoogle", ". Xoá Google app khỏi danh sách theo dõi của nhóm"); + } + + @Override + protected void executeCommand( + TelegramClient telegramClient, User user, Chat chat, String[] arguments) { + var admin = AdminRepository.INSTANCE.load(); + if (!admin.getGroups().contains(chat.getId())) { + StoreScrapeBotTelegramClient.INSTANCE.sendMessage( + chat.getId(), "Group is not allowed to use bot"); + return; + } + + if (arguments.length != 1) { + StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), "Invalid arguments"); + return; + } + + var appId = arguments[0]; + long groupId = chat.getId(); + var group = GroupRepository.INSTANCE.load(groupId); + + if (!group.getGoogleApps().contains(appId)) { + StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), "Google app is not added"); + return; + } + + group.getGoogleApps().remove(appId); + GroupRepository.INSTANCE.save(groupId, group); + StoreScrapeBotTelegramClient.INSTANCE.sendMessage( + chat.getId(), "Google app deleted successfully"); + } +} diff --git a/src/main/java/com/miti99/storescraperbot/bot/command/DeleteGroupCommand.java b/src/main/java/com/miti99/storescraperbot/bot/command/DeleteGroupCommand.java new file mode 100644 index 0000000..8d986b0 --- /dev/null +++ b/src/main/java/com/miti99/storescraperbot/bot/command/DeleteGroupCommand.java @@ -0,0 +1,52 @@ +package com.miti99.storescraperbot.bot.command; + +import com.miti99.storescraperbot.bot.StoreScrapeBotTelegramClient; +import com.miti99.storescraperbot.config.Config; +import com.miti99.storescraperbot.repository.AdminRepository; +import org.telegram.telegrambots.meta.api.objects.User; +import org.telegram.telegrambots.meta.api.objects.chat.Chat; +import org.telegram.telegrambots.meta.generics.TelegramClient; + +public class DeleteGroupCommand extends BaseStoreScraperBotCommand { + public static final DeleteGroupCommand INSTANCE = new DeleteGroupCommand(); + + DeleteGroupCommand() { + super("delgroup", ". Xoá group khỏi list group cho phép sử dụng bot"); + } + + @Override + protected void executeCommand( + TelegramClient telegramClient, User user, Chat chat, String[] arguments) { + if (!Config.ADMIN_IDS.contains(user.getId())) { + StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), "You are not admin"); + return; + } + + if (arguments.length > 1) { + StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), "Invalid arguments"); + return; + } + + long groupId; + if (arguments.length == 1) { + try { + groupId = Long.parseLong(arguments[0]); + } catch (NumberFormatException e) { + StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), "Invalid groupId"); + return; + } + } else { + groupId = chat.getId(); + } + + var admin = AdminRepository.INSTANCE.load(); + if (!admin.getGroups().contains(groupId)) { + StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), "Group is not added"); + return; + } + + admin.getGroups().remove(groupId); + AdminRepository.INSTANCE.save(admin); + StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), "Group deleted successfully"); + } +} diff --git a/src/main/java/com/miti99/storescraperbot/bot/command/InfoCommand.java b/src/main/java/com/miti99/storescraperbot/bot/command/InfoCommand.java new file mode 100644 index 0000000..b2eb24c --- /dev/null +++ b/src/main/java/com/miti99/storescraperbot/bot/command/InfoCommand.java @@ -0,0 +1,29 @@ +package com.miti99.storescraperbot.bot.command; + +import com.miti99.storescraperbot.bot.StoreScrapeBotTelegramClient; +import com.miti99.storescraperbot.repository.AdminRepository; +import com.miti99.storescraperbot.repository.GroupRepository; +import org.telegram.telegrambots.meta.api.objects.User; +import org.telegram.telegrambots.meta.api.objects.chat.Chat; +import org.telegram.telegrambots.meta.generics.TelegramClient; + +public class InfoCommand extends BaseStoreScraperBotCommand { + public static final InfoCommand INSTANCE = new InfoCommand(); + + InfoCommand() { + super("info", "Lấy thông tin của nhóm (chatId, threadId,...)"); + } + + @Override + protected void executeCommand( + TelegramClient telegramClient, User user, Chat chat, String[] arguments) { + if (arguments.length != 0) { + StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), "Invalid arguments"); + return; + } + + long groupId = chat.getId(); + + StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), "Id của nhóm là %s\n".formatted(groupId)); + } +} diff --git a/src/main/java/com/miti99/storescraperbot/bot/command/ListAppCommand.java b/src/main/java/com/miti99/storescraperbot/bot/command/ListAppCommand.java new file mode 100644 index 0000000..49b5546 --- /dev/null +++ b/src/main/java/com/miti99/storescraperbot/bot/command/ListAppCommand.java @@ -0,0 +1,47 @@ +package com.miti99.storescraperbot.bot.command; + +import com.miti99.storescraperbot.bot.StoreScrapeBotTelegramClient; +import com.miti99.storescraperbot.repository.AdminRepository; +import com.miti99.storescraperbot.repository.GroupRepository; +import org.telegram.telegrambots.meta.api.objects.User; +import org.telegram.telegrambots.meta.api.objects.chat.Chat; +import org.telegram.telegrambots.meta.generics.TelegramClient; + +public class ListAppCommand extends BaseStoreScraperBotCommand { + public static final ListAppCommand INSTANCE = new ListAppCommand(); + + ListAppCommand() { + super("listapp", ". Thêm Google app vào danh sách theo dõi của nhóm"); + } + + @Override + protected void executeCommand( + TelegramClient telegramClient, User user, Chat chat, String[] arguments) { + var admin = AdminRepository.INSTANCE.load(); + if (!admin.getGroups().contains(chat.getId())) { + StoreScrapeBotTelegramClient.INSTANCE.sendMessage( + chat.getId(), "Group is not allowed to use bot"); + return; + } + + if (arguments.length != 0) { + StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), "Invalid arguments"); + return; + } + + long groupId = chat.getId(); + var group = GroupRepository.INSTANCE.load(groupId); + + var sb = new StringBuilder(); + sb.append("Apple Apps:\n"); + for (var appId : group.getAppleApps()) { + sb.append("- %s\n".formatted(appId)); + } + sb.append("\nGoogle Apps:\n"); + for (var appId : group.getGoogleApps()) { + sb.append("- %s\n".formatted(appId)); + } + + StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), sb.toString()); + } +} diff --git a/src/main/java/com/miti99/storescraperbot/bot/command/GetGroupCommand.java b/src/main/java/com/miti99/storescraperbot/bot/command/ListGroupCommand.java similarity index 75% rename from src/main/java/com/miti99/storescraperbot/bot/command/GetGroupCommand.java rename to src/main/java/com/miti99/storescraperbot/bot/command/ListGroupCommand.java index 38b8191..1b69b23 100644 --- a/src/main/java/com/miti99/storescraperbot/bot/command/GetGroupCommand.java +++ b/src/main/java/com/miti99/storescraperbot/bot/command/ListGroupCommand.java @@ -7,11 +7,11 @@ import org.telegram.telegrambots.meta.api.objects.User; import org.telegram.telegrambots.meta.api.objects.chat.Chat; import org.telegram.telegrambots.meta.generics.TelegramClient; -public class GetGroupCommand extends BaseStoreScraperBotCommand { - public static final GetGroupCommand INSTANCE = new GetGroupCommand(); +public class ListGroupCommand extends BaseStoreScraperBotCommand { + public static final ListGroupCommand INSTANCE = new ListGroupCommand(); - GetGroupCommand() { - super("getgroup", "Lấy danh sách group được phép sử dụng bot hiện tại"); + ListGroupCommand() { + super("listgroup", "Lấy danh sách group được phép sử dụng bot hiện tại"); } @Override @@ -27,13 +27,12 @@ public class GetGroupCommand extends BaseStoreScraperBotCommand { return; } - var admin = AdminRepository.INSTANCE.load(); var groups = admin.getGroups(); var sb = new StringBuilder(); - sb.append("Groups:/n"); + sb.append("Groups:\n"); for (var groupId : groups) { - sb.append("- ").append(groupId).append("\n"); + sb.append("- %s\n".formatted(groupId)); } StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), sb.toString()); } diff --git a/src/main/java/com/miti99/storescraperbot/constant/Constant.java b/src/main/java/com/miti99/storescraperbot/constant/Constant.java index 4a7fee3..24b3445 100644 --- a/src/main/java/com/miti99/storescraperbot/constant/Constant.java +++ b/src/main/java/com/miti99/storescraperbot/constant/Constant.java @@ -2,4 +2,6 @@ package com.miti99.storescraperbot.constant; public class Constant { public static final String APP_NAME = "store_scraper_bot"; + public static final long APP_CACHE_SECONDS = 600; + public static final long NUM_DAYS_WARNING_NOT_UPDATED = 30; } diff --git a/src/main/java/com/miti99/storescraperbot/model/AbstractModel.java b/src/main/java/com/miti99/storescraperbot/model/AbstractModel.java index 6659b30..3d430c8 100644 --- a/src/main/java/com/miti99/storescraperbot/model/AbstractModel.java +++ b/src/main/java/com/miti99/storescraperbot/model/AbstractModel.java @@ -2,12 +2,14 @@ package com.miti99.storescraperbot.model; import com.google.gson.annotations.SerializedName; import lombok.Getter; -import lombok.RequiredArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Setter; @Getter -@RequiredArgsConstructor +@NoArgsConstructor +@Setter public abstract class AbstractModel { - final K key; + protected K key; @SerializedName("class") protected String clazz = getClass().getSimpleName(); diff --git a/src/main/java/com/miti99/storescraperbot/model/Admin.java b/src/main/java/com/miti99/storescraperbot/model/Admin.java index 49fd54b..17be171 100644 --- a/src/main/java/com/miti99/storescraperbot/model/Admin.java +++ b/src/main/java/com/miti99/storescraperbot/model/Admin.java @@ -2,16 +2,9 @@ package com.miti99.storescraperbot.model; import java.util.ArrayList; import java.util.List; -import lombok.Data; import lombok.Getter; -import lombok.Setter; @Getter -@Setter public class Admin extends AbstractModel { - List groups = new ArrayList<>(); - - public Admin(String key) { - super(key); - } + final List groups = new ArrayList<>(); } diff --git a/src/main/java/com/miti99/storescraperbot/model/AppleApp.java b/src/main/java/com/miti99/storescraperbot/model/AppleApp.java index 3463b99..4b1cbaa 100644 --- a/src/main/java/com/miti99/storescraperbot/model/AppleApp.java +++ b/src/main/java/com/miti99/storescraperbot/model/AppleApp.java @@ -7,10 +7,5 @@ import lombok.Setter; @Getter @Setter public class AppleApp extends AbstractModel { - long cacheTime; - AppleAppResponse rawResponse; - - public AppleApp(String key) { - super(key); - } + AppleAppResponse app; } diff --git a/src/main/java/com/miti99/storescraperbot/model/GoogleApp.java b/src/main/java/com/miti99/storescraperbot/model/GoogleApp.java index a6d7903..de9b993 100644 --- a/src/main/java/com/miti99/storescraperbot/model/GoogleApp.java +++ b/src/main/java/com/miti99/storescraperbot/model/GoogleApp.java @@ -7,10 +7,5 @@ import lombok.Setter; @Getter @Setter public class GoogleApp extends AbstractModel { - long cacheTime; - GoogleAppResponse rawResponse; - - public GoogleApp(String key) { - super(key); - } + GoogleAppResponse app; } diff --git a/src/main/java/com/miti99/storescraperbot/model/Group.java b/src/main/java/com/miti99/storescraperbot/model/Group.java index 609aea5..a15b016 100644 --- a/src/main/java/com/miti99/storescraperbot/model/Group.java +++ b/src/main/java/com/miti99/storescraperbot/model/Group.java @@ -1,22 +1,13 @@ package com.miti99.storescraperbot.model; -import com.miti99.storescraperbot.type.AppType; +import java.util.ArrayList; import java.util.List; -import lombok.Data; import lombok.Getter; import lombok.Setter; @Getter @Setter public class Group extends AbstractModel { - List apps; - - public Group(Long key) { - super(key); - } - - public static class App { - String appId; - AppType type; - } + List appleApps = new ArrayList<>(); + List googleApps = new ArrayList<>(); } diff --git a/src/main/java/com/miti99/storescraperbot/repository/AbstractRepository.java b/src/main/java/com/miti99/storescraperbot/repository/AbstractRepository.java index 5be7b34..161eddf 100644 --- a/src/main/java/com/miti99/storescraperbot/repository/AbstractRepository.java +++ b/src/main/java/com/miti99/storescraperbot/repository/AbstractRepository.java @@ -10,12 +10,13 @@ import java.lang.reflect.ParameterizedType; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.log4j.Log4j2; +import redis.clients.jedis.params.SetParams; @Log4j2 @NoArgsConstructor(access = AccessLevel.PROTECTED) public abstract class AbstractRepository> { public static final String SEPARATOR = ":"; - protected final Class classK = getKeyClass(); + // protected final Class classK = getKeyClass(); protected final Class classV = getDataClass(); protected final String prefix = String.join( @@ -24,10 +25,10 @@ public abstract class AbstractRepository> { Config.ENV.name().toLowerCase(), CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, classV.getSimpleName())); - protected Class getKeyClass() { - return (Class) - ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; - } + // protected Class getKeyClass() { + // return (Class) + // ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; + // } /** * Lấy ra class của V. Khi tạo 1 abstract class extends AbstractRepository mà không phải final thì @@ -41,12 +42,20 @@ public abstract class AbstractRepository> { ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[1]; } + /** + * @return expire seconds. <= 0 mean never expire. + */ + protected long getExpireSeconds() { + return 0; + } + public void init(K key) { try { if (exist(key)) { return; } - V data = classV.getDeclaredConstructor(classK).newInstance(key); + V data = classV.getDeclaredConstructor().newInstance(); + data.setKey(key); save(key, data); } catch (Exception e) { log.error("Error while initializing data", e); @@ -61,7 +70,11 @@ public abstract class AbstractRepository> { var databaseKey = getDatabaseKey(key); try (var jedis = RedisUtil.getJedis()) { var json = GsonUtil.toJson(data); - jedis.set(databaseKey, json); + if (getExpireSeconds() <= 0) { + jedis.set(databaseKey, json); + } else { + jedis.set(databaseKey, json, SetParams.setParams().ex(getExpireSeconds())); + } } catch (Exception e) { log.error("save error - key {}, databaseKey {}", key, databaseKey, e); } diff --git a/src/main/java/com/miti99/storescraperbot/repository/AppleAppRepository.java b/src/main/java/com/miti99/storescraperbot/repository/AppleAppRepository.java index d8b0ae4..733d935 100644 --- a/src/main/java/com/miti99/storescraperbot/repository/AppleAppRepository.java +++ b/src/main/java/com/miti99/storescraperbot/repository/AppleAppRepository.java @@ -1,7 +1,13 @@ package com.miti99.storescraperbot.repository; +import com.miti99.storescraperbot.constant.Constant; import com.miti99.storescraperbot.model.AppleApp; public class AppleAppRepository extends AbstractRepository { public static final AppleAppRepository INSTANCE = new AppleAppRepository(); + + @Override + protected long getExpireSeconds() { + return Constant.APP_CACHE_SECONDS; + } } diff --git a/src/main/java/com/miti99/storescraperbot/repository/GoogleAppRepository.java b/src/main/java/com/miti99/storescraperbot/repository/GoogleAppRepository.java index 8c2186f..92835fb 100644 --- a/src/main/java/com/miti99/storescraperbot/repository/GoogleAppRepository.java +++ b/src/main/java/com/miti99/storescraperbot/repository/GoogleAppRepository.java @@ -1,7 +1,13 @@ package com.miti99.storescraperbot.repository; +import com.miti99.storescraperbot.constant.Constant; import com.miti99.storescraperbot.model.GoogleApp; public class GoogleAppRepository extends AbstractRepository { - private static final GoogleAppRepository INSTANCE = new GoogleAppRepository(); + public static final GoogleAppRepository INSTANCE = new GoogleAppRepository(); + + @Override + protected long getExpireSeconds() { + return Constant.APP_CACHE_SECONDS; + } }