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.
This commit is contained in:
2025-11-04 23:06:46 +07:00
parent 774355ee2b
commit bae876b086
16 changed files with 225 additions and 31 deletions
+1 -1
View File
@@ -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
-1
View File
@@ -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")
@@ -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!");
@@ -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);
}
}
@@ -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
@@ -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);
}
}
}
@@ -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<String> {
public static final ScoreScrapeBotUsernameSupplier INSTANCE =
new ScoreScrapeBotUsernameSupplier();
public class StoreScrapeBotUsernameSupplier implements Supplier<String> {
public static final StoreScrapeBotUsernameSupplier INSTANCE =
new StoreScrapeBotUsernameSupplier();
@Override
public String get() {
@@ -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", "<groupId>. 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");
}
}
@@ -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);
}
@@ -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("<b>Groups:</b>/n");
for (var groupId : groups) {
sb.append("- ").append(groupId).append("\n");
}
StoreScrapeBotTelegramClient.INSTANCE.sendMessage(chat.getId(), sb.toString());
}
}
@@ -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<K> {
@JsonProperty("class")
protected String clazz = getClass().getSimpleName();
@Setter protected K key;
}
@@ -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<String> {
List<Long> groups;
List<Long> groups = new ArrayList<>();
}
@@ -19,6 +19,7 @@ public abstract class AbstractRepository<K, V extends AbstractModel<K>> {
protected AbstractRepository(String collectionName) {
this.collectionName = collectionName.toLowerCase();
CouchbaseUtil.createCollection(scopeName, collectionName);
}
public Collection collection() {
@@ -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<String, Admin> {
private static final AdminRepository INSTANCE = new AdminRepository();
public static final AdminRepository INSTANCE = new AdminRepository();
protected AdminRepository() {
super("admin");
@@ -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);
}
}
}
+2 -2
View File
@@ -14,8 +14,8 @@
</Console>
</Appenders>
<Loggers>
<AsyncRoot level="warn">
<Root level="${env:LOG_LEVEL:-debug}">
<AppenderRef ref="ConsoleAppender"/>
</AsyncRoot>
</Root>
</Loggers>
</Configuration>