From bae876b086d1e16820e5cb90a95d7635bb950f1d Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Tue, 4 Nov 2025 23:06:46 +0700 Subject: [PATCH] Refactor bot client and add group management commands Replaced ScoreScrapeBotTelegramClient with StoreScrapeBotTelegramClient and updated related references. Added BaseStoreScraperBotCommand for command error handling. Implemented AddGroupCommand and GetGroupCommand for managing allowed groups via admin commands. Improved CouchbaseUtil to ensure scope and collection creation. Updated logging configuration and environment example. Minor model and repository adjustments for initialization and group handling. --- .env.example | 2 +- build.gradle.kts | 1 - .../java/com/miti99/storescraperbot/Main.java | 3 ++ .../bot/ScoreScrapeBotTelegramClient.java | 12 ----- .../storescraperbot/bot/StoreScrapeBot.java | 18 ++++++- .../bot/StoreScrapeBotTelegramClient.java | 41 ++++++++++++++ ...va => StoreScrapeBotUsernameSupplier.java} | 6 +-- .../bot/command/AddGroupCommand.java | 37 ++++++++++--- .../command/BaseStoreScraperBotCommand.java | 29 ++++++++++ .../bot/command/GetGroupCommand.java | 40 ++++++++++++++ .../storescraperbot/model/AbstractModel.java | 3 -- .../miti99/storescraperbot/model/Admin.java | 3 +- .../repository/AbstractRepository.java | 1 + .../repository/AdminRepository.java | 2 +- .../storescraperbot/util/CouchbaseUtil.java | 54 +++++++++++++++++++ src/main/resources/log4j2.xml | 4 +- 16 files changed, 225 insertions(+), 31 deletions(-) delete mode 100644 src/main/java/com/miti99/storescraperbot/bot/ScoreScrapeBotTelegramClient.java create mode 100644 src/main/java/com/miti99/storescraperbot/bot/StoreScrapeBotTelegramClient.java rename src/main/java/com/miti99/storescraperbot/bot/{ScoreScrapeBotUsernameSupplier.java => StoreScrapeBotUsernameSupplier.java} (56%) create mode 100644 src/main/java/com/miti99/storescraperbot/bot/command/BaseStoreScraperBotCommand.java create mode 100644 src/main/java/com/miti99/storescraperbot/bot/command/GetGroupCommand.java diff --git a/.env.example b/.env.example index f687753..ca95628 100644 --- a/.env.example +++ b/.env.example @@ -2,7 +2,7 @@ # cp .env.example .env # Couchbase configuration -COUCHBASE_CONNECTION_STRING=localhost:8091 +COUCHBASE_CONNECTION_STRING=couchbase://localhost COUCHBASE_USERNAME=admin COUCHBASE_PASSWORD=your_password_here COUCHBASE_BUCKET_NAME=store_scraper diff --git a/build.gradle.kts b/build.gradle.kts index 5ba9f7f..ca2127c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -23,7 +23,6 @@ configurations { dependencies { annotationProcessor("org.projectlombok:lombok:1.18.36") implementation("com.couchbase.client:java-client:3.4.11") - implementation("com.lmax:disruptor:4.0.0") implementation("org.apache.logging.log4j:log4j-1.2-api:2.24.3") implementation("org.apache.logging.log4j:log4j-core:2.24.3") implementation("org.apache.logging.log4j:log4j-slf4j2-impl:2.24.3") diff --git a/src/main/java/com/miti99/storescraperbot/Main.java b/src/main/java/com/miti99/storescraperbot/Main.java index 882ac9b..48a2ed1 100644 --- a/src/main/java/com/miti99/storescraperbot/Main.java +++ b/src/main/java/com/miti99/storescraperbot/Main.java @@ -2,6 +2,7 @@ package com.miti99.storescraperbot; import com.miti99.storescraperbot.bot.StoreScrapeBot; import com.miti99.storescraperbot.config.Config; +import com.miti99.storescraperbot.repository.AdminRepository; import lombok.extern.log4j.Log4j2; import org.telegram.telegrambots.longpolling.TelegramBotsLongPollingApplication; @@ -9,6 +10,8 @@ import org.telegram.telegrambots.longpolling.TelegramBotsLongPollingApplication; public class Main { public static void main(String[] args) { + AdminRepository.INSTANCE.init(); + try (var botsApplication = new TelegramBotsLongPollingApplication()) { botsApplication.registerBot(Config.TELEGRAM_BOT_TOKEN, StoreScrapeBot.INSTANCE); log.info("StoreScrapeBot successfully started!"); diff --git a/src/main/java/com/miti99/storescraperbot/bot/ScoreScrapeBotTelegramClient.java b/src/main/java/com/miti99/storescraperbot/bot/ScoreScrapeBotTelegramClient.java deleted file mode 100644 index 2e8373c..0000000 --- a/src/main/java/com/miti99/storescraperbot/bot/ScoreScrapeBotTelegramClient.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.miti99.storescraperbot.bot; - -import com.miti99.storescraperbot.config.Config; -import org.telegram.telegrambots.client.okhttp.OkHttpTelegramClient; - -public class ScoreScrapeBotTelegramClient extends OkHttpTelegramClient { - public static final ScoreScrapeBotTelegramClient INSTANCE = new ScoreScrapeBotTelegramClient(); - - public ScoreScrapeBotTelegramClient() { - super(Config.TELEGRAM_BOT_TOKEN); - } -} diff --git a/src/main/java/com/miti99/storescraperbot/bot/StoreScrapeBot.java b/src/main/java/com/miti99/storescraperbot/bot/StoreScrapeBot.java index dbf035a..df9d0f2 100644 --- a/src/main/java/com/miti99/storescraperbot/bot/StoreScrapeBot.java +++ b/src/main/java/com/miti99/storescraperbot/bot/StoreScrapeBot.java @@ -3,8 +3,10 @@ package com.miti99.storescraperbot.bot; import com.miti99.storescraperbot.bot.command.AddGroupCommand; import lombok.extern.log4j.Log4j2; import org.telegram.telegrambots.extensions.bots.commandbot.CommandLongPollingTelegramBot; +import org.telegram.telegrambots.meta.api.methods.commands.SetMyCommands; import org.telegram.telegrambots.meta.api.methods.send.SendMessage; import org.telegram.telegrambots.meta.api.objects.Update; +import org.telegram.telegrambots.meta.api.objects.commands.BotCommand; import org.telegram.telegrambots.meta.exceptions.TelegramApiException; @Log4j2 @@ -12,8 +14,22 @@ public class StoreScrapeBot extends CommandLongPollingTelegramBot { public static final StoreScrapeBot INSTANCE = new StoreScrapeBot(); StoreScrapeBot() { - super(ScoreScrapeBotTelegramClient.INSTANCE, true, ScoreScrapeBotUsernameSupplier.INSTANCE); + super(StoreScrapeBotTelegramClient.INSTANCE, true, StoreScrapeBotUsernameSupplier.INSTANCE); register(AddGroupCommand.INSTANCE); + setMyCommands(); + } + + private void setMyCommands() { + try { + var commands = + getRegisteredCommands().stream() + .map(cmd -> new BotCommand(cmd.getCommandIdentifier(), cmd.getDescription())) + .toList(); + var setMyCommands = SetMyCommands.builder().commands(commands).build(); + StoreScrapeBotTelegramClient.INSTANCE.execute(setMyCommands); + } catch (TelegramApiException e) { + log.error("register error", e); + } } @Override diff --git a/src/main/java/com/miti99/storescraperbot/bot/StoreScrapeBotTelegramClient.java b/src/main/java/com/miti99/storescraperbot/bot/StoreScrapeBotTelegramClient.java new file mode 100644 index 0000000..51718fe --- /dev/null +++ b/src/main/java/com/miti99/storescraperbot/bot/StoreScrapeBotTelegramClient.java @@ -0,0 +1,41 @@ +package com.miti99.storescraperbot.bot; + +import com.miti99.storescraperbot.config.Config; +import lombok.extern.log4j.Log4j2; +import org.telegram.telegrambots.client.okhttp.OkHttpTelegramClient; +import org.telegram.telegrambots.meta.api.methods.ParseMode; +import org.telegram.telegrambots.meta.api.methods.send.SendMessage; + +@Log4j2 +public class StoreScrapeBotTelegramClient extends OkHttpTelegramClient { + public static final StoreScrapeBotTelegramClient INSTANCE = new StoreScrapeBotTelegramClient(); + + public StoreScrapeBotTelegramClient() { + super(Config.TELEGRAM_BOT_TOKEN); + } + + public void sendMessage(long chatId, String text) { + try { + var sendMessage = + SendMessage.builder().parseMode(ParseMode.HTML).chatId(chatId).text(text).build(); + execute(sendMessage); + } catch (Exception e) { + log.error("sendMessage error", e); + } + } + + public void sendMessage(long chatId, int threadId, String text) { + try { + var sendMessage = + SendMessage.builder() + .parseMode(ParseMode.HTML) + .chatId(chatId) + .messageThreadId(threadId) + .text(text) + .build(); + execute(sendMessage); + } catch (Exception e) { + log.error("sendMessage error", e); + } + } +} diff --git a/src/main/java/com/miti99/storescraperbot/bot/ScoreScrapeBotUsernameSupplier.java b/src/main/java/com/miti99/storescraperbot/bot/StoreScrapeBotUsernameSupplier.java similarity index 56% rename from src/main/java/com/miti99/storescraperbot/bot/ScoreScrapeBotUsernameSupplier.java rename to src/main/java/com/miti99/storescraperbot/bot/StoreScrapeBotUsernameSupplier.java index 9f5eac6..30575ae 100644 --- a/src/main/java/com/miti99/storescraperbot/bot/ScoreScrapeBotUsernameSupplier.java +++ b/src/main/java/com/miti99/storescraperbot/bot/StoreScrapeBotUsernameSupplier.java @@ -3,9 +3,9 @@ package com.miti99.storescraperbot.bot; import com.miti99.storescraperbot.config.Config; import java.util.function.Supplier; -public class ScoreScrapeBotUsernameSupplier implements Supplier { - public static final ScoreScrapeBotUsernameSupplier INSTANCE = - new ScoreScrapeBotUsernameSupplier(); +public class StoreScrapeBotUsernameSupplier implements Supplier { + public static final StoreScrapeBotUsernameSupplier INSTANCE = + new StoreScrapeBotUsernameSupplier(); @Override public String get() { 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 05a6254..72294bf 100644 --- a/src/main/java/com/miti99/storescraperbot/bot/command/AddGroupCommand.java +++ b/src/main/java/com/miti99/storescraperbot/bot/command/AddGroupCommand.java @@ -1,23 +1,48 @@ package com.miti99.storescraperbot.bot.command; +import com.miti99.storescraperbot.bot.StoreScrapeBotTelegramClient; import com.miti99.storescraperbot.config.Config; -import org.telegram.telegrambots.extensions.bots.commandbot.commands.BotCommand; +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 AddGroupCommand extends BotCommand { +public class AddGroupCommand extends BaseStoreScraperBotCommand { public static final AddGroupCommand INSTANCE = new AddGroupCommand(); AddGroupCommand() { - super("addgroup", "Thêm group vào list group cho phép sử dụng bot"); + super("addgroup", ". Thêm group vào list group cho phép sử dụng bot"); } @Override - public void execute(TelegramClient telegramClient, User user, Chat chat, String[] arguments) { - if (!Config.ADMIN_IDS.contains(user.getId()) ) { + 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; } - // TODO + + 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; + } + + var admin = AdminRepository.INSTANCE.load(); + if (admin.getGroups().contains(groupId)) { + StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), "Group is already added"); + return; + } + + 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/BaseStoreScraperBotCommand.java b/src/main/java/com/miti99/storescraperbot/bot/command/BaseStoreScraperBotCommand.java new file mode 100644 index 0000000..8edfa27 --- /dev/null +++ b/src/main/java/com/miti99/storescraperbot/bot/command/BaseStoreScraperBotCommand.java @@ -0,0 +1,29 @@ +package com.miti99.storescraperbot.bot.command; + +import com.miti99.storescraperbot.bot.StoreScrapeBotTelegramClient; +import lombok.extern.log4j.Log4j2; +import org.telegram.telegrambots.extensions.bots.commandbot.commands.BotCommand; +import org.telegram.telegrambots.meta.api.objects.User; +import org.telegram.telegrambots.meta.api.objects.chat.Chat; +import org.telegram.telegrambots.meta.generics.TelegramClient; + +@Log4j2 +public abstract class BaseStoreScraperBotCommand extends BotCommand { + + public BaseStoreScraperBotCommand(String commandIdentifier, String description) { + super(commandIdentifier, description); + } + + @Override + public void execute(TelegramClient telegramClient, User user, Chat chat, String[] arguments) { + try { + executeCommand(telegramClient, user, chat, arguments); + } catch (Exception e) { + log.error("execute error", e); + StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), "Internal server error"); + } + } + + protected abstract void executeCommand( + TelegramClient telegramClient, User user, Chat chat, String[] arguments); +} diff --git a/src/main/java/com/miti99/storescraperbot/bot/command/GetGroupCommand.java b/src/main/java/com/miti99/storescraperbot/bot/command/GetGroupCommand.java new file mode 100644 index 0000000..38b8191 --- /dev/null +++ b/src/main/java/com/miti99/storescraperbot/bot/command/GetGroupCommand.java @@ -0,0 +1,40 @@ +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 GetGroupCommand extends BaseStoreScraperBotCommand { + public static final GetGroupCommand INSTANCE = new GetGroupCommand(); + + GetGroupCommand() { + super("getgroup", "Lấy danh sách group được phép sử dụng bot hiện tại"); + } + + @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 != 0) { + StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), "Invalid arguments"); + return; + } + + + var admin = AdminRepository.INSTANCE.load(); + var groups = admin.getGroups(); + var sb = new StringBuilder(); + sb.append("Groups:/n"); + for (var groupId : groups) { + sb.append("- ").append(groupId).append("\n"); + } + StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), sb.toString()); + } +} diff --git a/src/main/java/com/miti99/storescraperbot/model/AbstractModel.java b/src/main/java/com/miti99/storescraperbot/model/AbstractModel.java index e089d5f..44b3586 100644 --- a/src/main/java/com/miti99/storescraperbot/model/AbstractModel.java +++ b/src/main/java/com/miti99/storescraperbot/model/AbstractModel.java @@ -2,13 +2,10 @@ package com.miti99.storescraperbot.model; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; -import lombok.Setter; @Getter public abstract class AbstractModel { @JsonProperty("class") protected String clazz = getClass().getSimpleName(); - - @Setter protected K key; } diff --git a/src/main/java/com/miti99/storescraperbot/model/Admin.java b/src/main/java/com/miti99/storescraperbot/model/Admin.java index c1ea3b4..fec1850 100644 --- a/src/main/java/com/miti99/storescraperbot/model/Admin.java +++ b/src/main/java/com/miti99/storescraperbot/model/Admin.java @@ -1,5 +1,6 @@ package com.miti99.storescraperbot.model; +import java.util.ArrayList; import java.util.List; import lombok.Data; import lombok.Getter; @@ -8,5 +9,5 @@ import lombok.Setter; @Getter @Setter public class Admin extends AbstractModel { - List groups; + List groups = 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 17f7fcc..adffca0 100644 --- a/src/main/java/com/miti99/storescraperbot/repository/AbstractRepository.java +++ b/src/main/java/com/miti99/storescraperbot/repository/AbstractRepository.java @@ -19,6 +19,7 @@ public abstract class AbstractRepository> { protected AbstractRepository(String collectionName) { this.collectionName = collectionName.toLowerCase(); + CouchbaseUtil.createCollection(scopeName, collectionName); } public Collection collection() { diff --git a/src/main/java/com/miti99/storescraperbot/repository/AdminRepository.java b/src/main/java/com/miti99/storescraperbot/repository/AdminRepository.java index a4dc038..03f447f 100644 --- a/src/main/java/com/miti99/storescraperbot/repository/AdminRepository.java +++ b/src/main/java/com/miti99/storescraperbot/repository/AdminRepository.java @@ -6,7 +6,7 @@ import com.miti99.storescraperbot.model.Admin; * Đây là repository chỉ chứa 1 key duy nhất, key là "" (rỗng) */ public class AdminRepository extends AbstractRepository { - private static final AdminRepository INSTANCE = new AdminRepository(); + public static final AdminRepository INSTANCE = new AdminRepository(); protected AdminRepository() { super("admin"); diff --git a/src/main/java/com/miti99/storescraperbot/util/CouchbaseUtil.java b/src/main/java/com/miti99/storescraperbot/util/CouchbaseUtil.java index bc1f4f7..20487f6 100644 --- a/src/main/java/com/miti99/storescraperbot/util/CouchbaseUtil.java +++ b/src/main/java/com/miti99/storescraperbot/util/CouchbaseUtil.java @@ -8,8 +8,12 @@ import static com.miti99.storescraperbot.config.Config.COUCHBASE_USERNAME; import com.couchbase.client.java.Bucket; import com.couchbase.client.java.Cluster; import com.couchbase.client.java.ClusterOptions; +import com.couchbase.client.java.manager.collection.CollectionSpec; +import com.miti99.storescraperbot.config.Config; import java.time.Duration; +import lombok.extern.log4j.Log4j2; +@Log4j2 public class CouchbaseUtil { public static final Cluster CLUSTER; public static final Bucket BUCKET; @@ -24,4 +28,54 @@ public class CouchbaseUtil { BUCKET = CLUSTER.bucket(COUCHBASE_BUCKET_NAME); BUCKET.waitUntilReady(Duration.ofSeconds(10)); } + + public static void createScope(String scopeName) { + var collectionManager = BUCKET.collections(); + try { + boolean scopeExists = + collectionManager.getAllScopes().stream().anyMatch(s -> s.name().equals(scopeName)); + + if (!scopeExists) { + collectionManager.createScope(scopeName); + log.info("Scope created: {}", scopeName); + } else { + log.info("Scope existed: {}", scopeName); + } + } catch (Exception e) { + log.error("createScope error - scopeName: '{}'", scopeName, e); + } + } + + public static void createCollection(String scopeName, String collectionName) { + var collectionManager = BUCKET.collections(); + try { + var scopeSpecOpt = + collectionManager.getAllScopes().stream() + .filter(s -> s.name().equals(scopeName)) + .findFirst(); + if (scopeSpecOpt.isEmpty()) { + createScope(scopeName); + createCollection(scopeName, collectionName); + return; + } + + var scopeSpec = scopeSpecOpt.get(); + boolean collectionExists = + scopeSpec.collections().stream().anyMatch(c -> c.name().equals(collectionName)); + + if (!collectionExists) { + var spec = CollectionSpec.create(collectionName, scopeName); + collectionManager.createCollection(spec); + log.info("Collection created: {} in {}", collectionName, scopeName); + } else { + log.info("Collection existed: {} in {}", collectionName, scopeName); + } + } catch (Exception e) { + log.error( + "createCollection error - collectionName: '{}', scopeName: '{}'", + collectionName, + scopeName, + e); + } + } } diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml index b10433f..c52c9ad 100644 --- a/src/main/resources/log4j2.xml +++ b/src/main/resources/log4j2.xml @@ -14,8 +14,8 @@ - + - +