Add change books

This commit is contained in:
2025-02-07 19:40:51 +03:00
parent 3e8a43807c
commit 9fae772099
6 changed files with 171 additions and 119 deletions
@@ -1,5 +1,6 @@
package ru.cathub.telegabot.bot;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Component;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboardMarkup;
@@ -65,14 +66,21 @@ public class KeyboardHelper {
return keyboardMarkup;
}
public static InlineKeyboardMarkup getBookListKeyboard(int currentPage, boolean hasNext) {
public static InlineKeyboardMarkup getBookListKeyboard(int currentPage, boolean hasNext, Page<Book> books, boolean forEdit) {
List<InlineKeyboardRow> rows = new ArrayList<>();
List<InlineKeyboardRow> rows;
// Кнопки навигации
InlineKeyboardRow navRow = new InlineKeyboardRow();
if (!books.isEmpty() && forEdit) {
rows = getBookEditListKeyboard(books);
}
else {
rows = new ArrayList<>();
}
if (currentPage > 0) {
navRow.add(InlineKeyboardButton.builder()
.text("← Назад")
@@ -93,15 +101,14 @@ public class KeyboardHelper {
return new InlineKeyboardMarkup(rows);
}
public static InlineKeyboardMarkup getBookEditListKeyboard(BotUser user, List<Book> books) {
public static List<InlineKeyboardRow> getBookEditListKeyboard(Page<Book> books) {
List<InlineKeyboardRow> rows = new ArrayList<>();
InlineKeyboardRow navRow = new InlineKeyboardRow();
int counter = 1;
for (Book book : books) {
navRow.add(InlineKeyboardButton.builder()
.text(book.getTitle())
.text(String.valueOf((char) ('0' + counter)) + "\uFE0F\u20E3")
.callbackData("BOOK_EDIT_ID_" + book.getId())
.build());
if (counter % 5 == 0) {
@@ -115,7 +122,7 @@ public class KeyboardHelper {
rows.add(navRow);
}
return new InlineKeyboardMarkup(rows);
return rows;
}
@@ -126,7 +133,7 @@ public class KeyboardHelper {
// Первый ряд кнопок
navRow.add(
InlineKeyboardButton.builder()
.text("Название ✏️")
.text("Название \uFE0F")
.callbackData("BOOK_EDIT_TITLE")
.build()
);
@@ -144,29 +151,17 @@ public class KeyboardHelper {
.callbackData("BOOK_EDIT_RATING")
.build()
);
navRow.add(
InlineKeyboardButton.builder()
.text("Отмена ❌")
.callbackData("BOOK_EDIT_CANCEL")
.build()
);
// navRow.add(
// InlineKeyboardButton.builder()
// .text("Отмена ❌")
// .callbackData("BOOK_EDIT_CANCEL")
// .build()
// );
rows.add(navRow);
return new InlineKeyboardMarkup(rows);
}
public String getWelcomeMessage() {
return "✨ *Добро пожаловать в BookBot!* 📚\n" +
"_Ваш персональный помощник для управления библиотекой_\n\n" +
"Чтобы начать, отправьте:\n" +
"👉 `/start` — активировать бота и открыть меню\n\n" +
"**С нами вы сможете:**\n" +
"🔸 Добавлять книги с рейтингом\n" +
"🔸 Просматривать свою коллекцию\n" +
"🔸 Редактировать записи\n" +
"🔸 Сортировать по рейтингу\n\n" +
"📌 *Не забывайте: всегда можно вернуться в главное меню командой* `/start`\n\n" +
"_📖 Давайте начнём ваше литературное путешествие!_";
}
}
@@ -14,10 +14,8 @@ import java.util.Optional;
public interface BookRepository extends JpaRepository<Book, Long> {
List<Book> findByBotUser(BotUser botUser);
Page<Book> findByBotUser(BotUser botUser, Pageable pageable);
Page<Book> findByBotUserOrderByIdAsc(BotUser botUser, Pageable pageable);
@Query("SELECT b FROM Book b WHERE b.botUser = :user ORDER BY b.id DESC")
List<Book> findPaginatedByBotUser(@Param("user") BotUser user, Pageable pageable);
Optional<Book> findByIdAndBotUser(Long bookId, BotUser user);
@@ -11,5 +11,7 @@ public interface BookManageService {
void handleStartMessage(BotUser botUser) throws ServiceException;
void handleRestartMessage(BotUser botUser);
void processBookQuery(BotUser user, String text);
}
@@ -10,6 +10,10 @@ public interface TelegramClientService {
Message sendMessage(BotUser botUser, String text, ReplyKeyboard replyKeyboard);
Message sendMessageWithMarkdown(BotUser botUser, String text);
Message sendMessageWithMarkdown(BotUser botUser, String text, ReplyKeyboard replyKeyboard);
Message sendMessage(BotUser botUser, String text);
Message sendMessageWithMarkdown(BotUser botUser, String text, InlineKeyboardMarkup bookListKeyboard);
@@ -7,7 +7,6 @@ import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import ru.cathub.telegabot.bot.KeyboardHelper;
import ru.cathub.telegabot.exception.ServiceException;
import ru.cathub.telegabot.model.*;
import ru.cathub.telegabot.repository.BookRepository;
import ru.cathub.telegabot.repository.UserRepository;
@@ -26,11 +25,35 @@ import static ru.cathub.telegabot.utils.Constants.*;
@Slf4j
public class BookManageServiceImpl implements BookManageService {
private static final String FULL_STAR = ""; // Эмодзи U+2B50
private static final String EMPTY_STAR = ""; // Символ U+2606
private final TelegramClientService telegramClientService;
private final UserRepository userRepository;
private final BookCacheService bookCacheService;
private final BookRepository bookRepository;
public static String formatRating(int rating) {
// Ограничиваем рейтинг в диапазоне 0-10
int validatedRating = Math.min(10, Math.max(0, rating));
StringBuilder sb = new StringBuilder();
// Добавляем заполненные звезды
for (int i = 0; i < validatedRating; i++) {
sb.append(FULL_STAR);
}
// Добавляем пустые звезды
for (int i = validatedRating; i < 10; i++) {
sb.append(EMPTY_STAR);
}
// Добавляем текстовое представление
sb.append(" (").append(validatedRating).append("/10)");
return sb.toString();
}
@Override
public void processBookInput(BotUser user, String text) {
Long userId = user.getId();
@@ -40,10 +63,19 @@ public class BookManageServiceImpl implements BookManageService {
switch (text) {
case LIST_BOOKS:
bookCacheService.clearBookListCache(userId);
bookCacheService.clearBookEditCache(userId);
showPaginatedBookList(user, 0, null);
break;
case EDIT_BOOKS:
startBookEditing(user);
bookCacheService.clearBookListCache(userId);
bookCacheService.clearBookEditCache(userId);
showPaginatedBookList(user, 0, null, true);
//startBookEditing(user);
break;
case ADD_BOOK:
bookCacheService.clearBookListCache(userId);
bookCacheService.clearBookEditCache(userId);
handleNewAddRequest(user, text);
break;
default:
if (editCache == null || editCache.getBookEditState() == BookEditStateCache.BookEditState.NONE) {
@@ -52,7 +84,7 @@ public class BookManageServiceImpl implements BookManageService {
}
switch (editCache.getBookEditState()) {
case ADDING_TITLE:
handleNewAddRequest(user, text);
handleExistingAddCache(user, creationCache, text);
break;
case ADDING_AUTHOR:
handleExistingAddCache(user, creationCache, text);
@@ -80,12 +112,16 @@ public class BookManageServiceImpl implements BookManageService {
}
@Override
public void handleStartMessage(BotUser botUser) throws ServiceException {
public void handleStartMessage(BotUser botUser) {
botUser.setWorkingMode(BotUser.WorkingMode.BOOK);
userRepository.save(botUser);
telegramClientService.sendMessage(botUser, "Начало обработки книг", KeyboardHelper.getBookMenuKeyboard());
telegramClientService.sendMessageWithMarkdown(botUser, getWelcomeMessage(), KeyboardHelper.getBookMenuKeyboard());
}
@Override
public void handleRestartMessage(BotUser botUser) {
telegramClientService.sendMessageWithMarkdown(botUser, "Выберите действие \uD83D\uDCCB", KeyboardHelper.getBookMenuKeyboard());
}
@Override
@@ -97,7 +133,8 @@ public class BookManageServiceImpl implements BookManageService {
bookListCache.setCurrentPage(page);
bookCacheService.updateBookListState(user.getId(), bookListCache);
}
showPaginatedBookList(user, page, bookListCache.getListMessageId());
var bookEditCache = bookCacheService.getBookEditCache(user.getId());
showPaginatedBookList(user, page, bookListCache.getListMessageId(),bookEditCache != null);
} else if (text.startsWith("BOOK_EDIT_ID_")) {
Long bookId = Long.parseLong(text.split("_")[3]);
processBookSelection(user, bookId);
@@ -120,12 +157,12 @@ public class BookManageServiceImpl implements BookManageService {
}
}
private void handleNewAddRequest(BotUser user, String text) {
if (ADD_BOOK.equals(text)) {
BookCreationCache newCache = new BookCreationCache();
bookCacheService.updateCache(user.getId(), newCache);
telegramClientService.sendMessage(user, "Введите название книги:");
bookCacheService.updateBookEditCache(user.getId(), BookEditStateCache.builder().bookEditState(BookEditStateCache.BookEditState.ADDING_TITLE).build());
telegramClientService.sendMessageWithMarkdown(user, "*Введите название книги: \uD83D\uDCDA*",null);
}
}
@@ -133,41 +170,35 @@ public class BookManageServiceImpl implements BookManageService {
if (cache.getTitle() == null) {
cache.setTitle(text);
bookCacheService.updateCache(user.getId(), cache);
telegramClientService.sendMessage(user, "Введите автора:");
telegramClientService.sendMessageWithMarkdown(user, "*Укажите автора книги: ✍\uFE0F*",null);
} else if (cache.getAuthor() == null) {
cache.setAuthor(text);
bookCacheService.updateCache(user.getId(), cache);
telegramClientService.sendMessage(user, "Введите рейтинг:");
telegramClientService.sendMessageWithMarkdown(user, "*Укажите рейтинг книги \\(целое число от 0 до 10\\): ⭐\uFE0F*",null);
} else {
try {
int year = Integer.parseInt(text);
saveBook(user, cache, year);
bookCacheService.clearCache(user.getId());
telegramClientService.sendMessage(user, "Книга сохранена!");
handleStartMessage(user);
handleRestartMessage(user);
} catch (NumberFormatException e) {
telegramClientService.sendMessage(user, "Ошибка! Введите целов число");
} catch (ServiceException e) {
throw new RuntimeException(e);
}
}
}
private void saveBook(BotUser user, BookCreationCache cache, int rating) {
Book book = Book.builder()
.title(cache.getTitle())
.rating(rating)
.author(cache.getAuthor())
.botUser(user)
.build();
Book book = Book.builder().title(cache.getTitle()).rating(rating).author(cache.getAuthor()).botUser(user).build();
bookRepository.save(book);
}
private void showPaginatedBookList(BotUser botUser, int page, Integer messageId) {
private void showPaginatedBookList(BotUser botUser, int page, Integer messageId, Boolean forEdit) {
try {
Pageable pageable = PageRequest.of(page, 5);
Page<Book> books = bookRepository.findByBotUser(botUser, pageable);
Page<Book> books = bookRepository.findByBotUserOrderByIdAsc(botUser, pageable);
StringBuilder sb = new StringBuilder();
@@ -176,7 +207,7 @@ public class BookManageServiceImpl implements BookManageService {
int counter = 1;
for (Book book : books) {
sb.append(formatBookEntry(book, (counter - 1) % 5));
sb.append(formatBookEntry(book, (counter - 1) % 5,forEdit));
counter++;
}
@@ -185,15 +216,15 @@ public class BookManageServiceImpl implements BookManageService {
bookListCache.setCurrentPage(page);
bookCacheService.updateBookListState(botUser.getId(), bookListCache);
telegramClientService.editMessageWithMarkdown(botUser, messageId, sb.toString(),getBookListKeyboard(page, books.hasNext()));
telegramClientService.editMessageWithMarkdown(botUser, messageId, sb.toString(), getBookListKeyboard(page, books.hasNext(), books, forEdit));
} else {
var message = telegramClientService.sendMessageWithMarkdown(
botUser,
sb.toString(),
getBookListKeyboard(page, books.hasNext())
);
var message = telegramClientService.sendMessageWithMarkdown(botUser, sb.toString(), getBookListKeyboard(page, books.hasNext(), books, forEdit));
bookCacheService.updateBookListState(botUser.getId(), BookListCache.builder().currentPage(page).listMessageId(message.getMessageId()).build());
}
if (forEdit)
{
bookCacheService.updateBookEditCache(botUser.getId(), BookEditStateCache.builder().bookEditState(BookEditStateCache.BookEditState.CHOOSING_FIELD).build());
}
} catch (Exception e) {
// 6. Обработка ошибок
log.error("Error loading book list", e);
@@ -201,22 +232,32 @@ public class BookManageServiceImpl implements BookManageService {
}
}
private void showPaginatedBookList(BotUser botUser, int page, Integer messageId) {
showPaginatedBookList(botUser, page, messageId, false);
}
private String escapeMarkdown(String text) {
return text.replaceAll("([_*\\[\\]()~`>#+=|{}.!-])", "\\\\$1");
}
public void startBookEditing(BotUser user) {
bookCacheService.updateBookEditCache(user.getId(), BookEditStateCache.builder().bookEditState(BookEditStateCache.BookEditState.SELECTING_BOOK).build());
var message = telegramClientService.sendMessage(user, "Выберите книгу для редактирования:",
getBookEditListKeyboard(user, bookRepository.findByBotUser(user)));
bookCacheService.updateBookListState(user.getId(), BookListCache.builder().currentPage(0).listMessageId(message.getMessageId()).build());
}
// public void startBookEditing(BotUser user) {
// bookCacheService.updateBookEditCache(user.getId(), BookEditStateCache.builder().bookEditState(BookEditStateCache.BookEditState.SELECTING_BOOK).build());
// //getBookListKeyboard
// //var message = telegramClientService.sendMessage(user, "Выберите книгу для редактирования:", getBookEditListKeyboard(user, bookRepository.findByBotUser(user)));
// bookCacheService.updateBookListState(user.getId(), BookListCache.builder().currentPage(0).listMessageId(message.getMessageId()).build());
//
// }
private void processBookSelection(BotUser user, Long bookId) {
try {
Optional<Book> book = bookRepository.findByIdAndBotUser(bookId, user);
var bookCache = bookCacheService.getBookEditCache(user.getId());
if (bookCache != null) {
bookCache.setBookId(bookId);
} else {
bookCache = BookEditStateCache.builder().bookEditState(BookEditStateCache.BookEditState.NONE).bookId(bookId).build();
}
bookCacheService.updateBookEditCache(user.getId(), bookCache);
if (book.isPresent()) {
log.info("Found book with id {}", bookId);
telegramClientService.sendMessage(user, "Выберите поле для редактирования:", getEditOptions(user));
@@ -228,72 +269,43 @@ public class BookManageServiceImpl implements BookManageService {
}
}
private static final String FULL_STAR = ""; // Эмодзи U+2B50
private static final String EMPTY_STAR = ""; // Символ U+2606
public static String formatRating(int rating) {
// Ограничиваем рейтинг в диапазоне 0-10
int validatedRating = Math.min(10, Math.max(0, rating));
StringBuilder sb = new StringBuilder();
// Добавляем заполненные звезды
for (int i = 0; i < validatedRating; i++) {
sb.append(FULL_STAR);
}
// Добавляем пустые звезды
for (int i = validatedRating; i < 10; i++) {
sb.append(EMPTY_STAR);
}
// Добавляем текстовое представление
sb.append(" (").append(validatedRating).append("/10)");
return sb.toString();
}
private String getBookIcon(int index) {
String[] icons = {"📘", "📙", "📕", "📗", "📓"};
return icons[index % icons.length];
}
private String formatBookEntry(Book book, int index) {
return String.format(
"%s *%s*\n" +
"✍️ _%s_\n" +
"🏆 %s\n" +
// "\n" +
"▔▔▔▔▔▔▔▔▔▔▔▔▔▔\n",
getBookIcon(index),
escapeMarkdown(book.getTitle()),
escapeMarkdown(book.getAuthor()),
escapeMarkdown(formatRating(book.getRating()))
);
private String formatBookEntry(Book book, int index, boolean forEdit) {
if (forEdit){
return String.format("%s %s *%s*\n" + "✍️ _%s_\n" + "🏆 %s\n" +
// "\n" +
"▔▔▔▔▔▔▔▔▔▔▔▔▔▔\n",String.valueOf((char) ('0' + index + 1)) + "\uFE0F\u20E3", getBookIcon(index), escapeMarkdown(book.getTitle()), escapeMarkdown(book.getAuthor()), escapeMarkdown(formatRating(book.getRating())));
}
return String.format("%s *%s*\n" + "✍️ _%s_\n" + "🏆 %s\n" +
// "\n" +
"▔▔▔▔▔▔▔▔▔▔▔▔▔▔\n", getBookIcon(index), escapeMarkdown(book.getTitle()), escapeMarkdown(book.getAuthor()), escapeMarkdown(formatRating(book.getRating())));
}
private void handleEditTitle(BotUser user) {
var bookEditState = bookCacheService.getBookEditCache(user.getId());
bookEditState.setBookEditState(BookEditStateCache.BookEditState.EDITING_TITLE);
bookCacheService.updateBookEditCache(user.getId(), BookEditStateCache.builder().bookEditState(BookEditStateCache.BookEditState.EDITING_TITLE).build());
telegramClientService.sendMessage(user,"Введите новое название:");
var bookCache = bookCacheService.getBookEditCache(user.getId());
bookCache.setBookEditState(BookEditStateCache.BookEditState.EDITING_TITLE);
bookCacheService.updateBookEditCache(user.getId(), bookCache);
telegramClientService.sendMessage(user, "Введите новое название:");
}
private void handleEditAuthor(BotUser user) {
var bookEditState = bookCacheService.getBookEditCache(user.getId());
bookEditState.setBookEditState(BookEditStateCache.BookEditState.EDITING_AUTHOR);
bookCacheService.updateBookEditCache(user.getId(), BookEditStateCache.builder().bookEditState(BookEditStateCache.BookEditState.EDITING_TITLE).build());
telegramClientService.sendMessage(user,"Введите нового автора:");
var bookCache = bookCacheService.getBookEditCache(user.getId());
bookCache.setBookEditState(BookEditStateCache.BookEditState.EDITING_AUTHOR);
bookCacheService.updateBookEditCache(user.getId(), bookCache);
telegramClientService.sendMessage(user, "Введите нового автора:");
}
private void handleEditRating(BotUser user) {
var bookEditState = bookCacheService.getBookEditCache(user.getId());
bookEditState.setBookEditState(BookEditStateCache.BookEditState.EDITING_RATING);
bookCacheService.updateBookEditCache(user.getId(), BookEditStateCache.builder().bookEditState(BookEditStateCache.BookEditState.EDITING_TITLE).build());
telegramClientService.sendMessage(user,"Введите рейтинг:");
var bookCache = bookCacheService.getBookEditCache(user.getId());
bookCache.setBookEditState(BookEditStateCache.BookEditState.EDITING_RATING);
bookCacheService.updateBookEditCache(user.getId(), bookCache);
telegramClientService.sendMessage(user, "Введите рейтинг:");
}
@@ -307,6 +319,7 @@ public class BookManageServiceImpl implements BookManageService {
bookCacheService.updateBookEditCache(user.getId(), bookEditState);
telegramClientService.sendMessage(user, "Название успешно обновлено!");
handleRestartMessage(user);
}
private void updateBookAuthor(BotUser user, String newAuthor) {
@@ -317,7 +330,8 @@ public class BookManageServiceImpl implements BookManageService {
bookEditState.setBookEditState(BookEditStateCache.BookEditState.NONE);
bookCacheService.updateBookEditCache(user.getId(), bookEditState);
telegramClientService.sendMessage(user,"Автор успешно обновлен");
telegramClientService.sendMessage(user, "Автор успешно обновлен");
handleRestartMessage(user);
}
private void updateBookRating(BotUser user, String newRating) {
@@ -328,6 +342,22 @@ public class BookManageServiceImpl implements BookManageService {
bookEditState.setBookEditState(BookEditStateCache.BookEditState.NONE);
bookCacheService.updateBookEditCache(user.getId(), bookEditState);
telegramClientService.sendMessage(user,"Рейтинг успешно обновлен");
telegramClientService.sendMessage(user, "Рейтинг успешно обновлен");
handleRestartMessage(user);
}
public String getWelcomeMessage() {
return "✨ *Добро пожаловать в Бок Книжник\\!* 📚\n" +
"_Ваш персональный помощник для управления библиотекой_\n\n" +
"Чтобы начать, отправьте:\n" +
"👉 `/start` — активировать бота и открыть меню\n\n" +
"**С нами вы сможете:**\n" +
"🔸 Добавлять книги с рейтингом\n" +
"🔸 Просматривать свою коллекцию\n" +
"🔸 Редактировать записи\n" +
"🔸 Сортировать по рейтингу\n\n" +
"📌 *Не забывайте: всегда можно вернуться в главное меню командой* `/start`\n\n" +
"_📖 Давайте начнём ваше литературное путешествие\\!_";
}
}
@@ -36,6 +36,29 @@ public class TelegramClientServiceImps implements TelegramClientService {
}
}
@Override
public Message sendMessageWithMarkdown(BotUser botUser, String text) {
return sendMessageWithMarkdown(botUser, text, null);
}
@Override
public Message sendMessageWithMarkdown(BotUser botUser, String text, ReplyKeyboard replyKeyboard) {
SendMessage message = SendMessage.builder()
.chatId(botUser.getChatId().toString())
.parseMode("MarkdownV2")
.replyMarkup(replyKeyboard)
.text(text)
.build();
try {
return telegramClient.execute(message);
} catch (TelegramApiException e) {
System.out.println("Ошибка отправки сообщения: {}" + e.getMessage());
throw new RuntimeException(e);
}
}
@Override
public Message sendMessage(BotUser botUser, String text) {
return sendMessage(botUser, text, null);