Заменен телеграм бот на кафку

This commit is contained in:
Kayashov.SM
2025-04-24 15:17:02 +04:00
parent 704875d297
commit 0f39e22310
35 changed files with 144 additions and 1071 deletions

11
pom.xml
View File

@@ -34,6 +34,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -73,6 +77,8 @@
<version>2.18.2</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
@@ -83,6 +89,11 @@
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<version>3.2.4</version>
</dependency>
</dependencies>
<build>

View File

@@ -1,15 +0,0 @@
package ru.kayashov.bar.bot.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Аннотация для пометки команд бота
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface CommandData {
String name();
}

View File

@@ -1,28 +0,0 @@
package ru.kayashov.bar.bot.config;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.telegram.telegrambots.meta.TelegramBotsApi;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;
import org.telegram.telegrambots.updatesreceivers.DefaultBotSession;
@Slf4j
@Configuration
@RequiredArgsConstructor
public class TelegramBotConfig {
private final TelegramExecutorBot bot;
@Bean
TelegramBotsApi telegramBotsApi() throws TelegramApiException {
TelegramBotsApi telegramBotsApi = new TelegramBotsApi(DefaultBotSession.class);
try {
telegramBotsApi.registerBot(bot);
} catch (TelegramApiException e) {
log.error("Bot did not register", e);
}
return telegramBotsApi;
}
}

View File

@@ -1,41 +0,0 @@
package ru.kayashov.bar.bot.config;
import org.telegram.telegrambots.meta.api.objects.CallbackQuery;
import org.telegram.telegrambots.meta.api.objects.Chat;
import org.telegram.telegrambots.meta.api.objects.Message;
import org.telegram.telegrambots.meta.api.objects.Update;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboardMarkup;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.KeyboardRow;
import ru.kayashov.bar.bot.domain.model.AbstractCallbackQuery;
import ru.kayashov.bar.bot.domain.model.AbstractChat;
import ru.kayashov.bar.bot.domain.model.AbstractKeyboard;
import ru.kayashov.bar.bot.domain.model.AbstractKeyboardButton;
import ru.kayashov.bar.bot.domain.model.AbstractMessage;
import ru.kayashov.bar.bot.domain.model.AbstractUpdate;
import java.util.List;
public interface TelegramBotMapper {
AbstractUpdate toAbstractUpdate(Update update);
AbstractMessage toAbstractMessage(Message message);
AbstractCallbackQuery toAbstractCallbackQuery(CallbackQuery query);
AbstractChat toAbstractChat(Chat chat);
InlineKeyboardMarkup toInlineKeyboard(AbstractKeyboard replyKeyboard);
ReplyKeyboardMarkup toReplyKeyboard(AbstractKeyboard replyKeyboard);
List<List<InlineKeyboardButton>> toInlineKeyboard(List<List<AbstractKeyboardButton>> buttons);
List<InlineKeyboardButton> toListButtons(List<AbstractKeyboardButton> buttons);
List<KeyboardRow> toKeyboardRows(List<List<AbstractKeyboardButton>> rows);
KeyboardRow toKeyboardRow(List<AbstractKeyboardButton> row);
}

View File

@@ -1,128 +0,0 @@
package ru.kayashov.bar.bot.config;
import org.springframework.stereotype.Component;
import org.telegram.telegrambots.meta.api.objects.CallbackQuery;
import org.telegram.telegrambots.meta.api.objects.Chat;
import org.telegram.telegrambots.meta.api.objects.Message;
import org.telegram.telegrambots.meta.api.objects.Update;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboardMarkup;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.KeyboardRow;
import ru.kayashov.bar.bot.domain.model.AbstractCallbackQuery;
import ru.kayashov.bar.bot.domain.model.AbstractChat;
import ru.kayashov.bar.bot.domain.model.AbstractKeyboard;
import ru.kayashov.bar.bot.domain.model.AbstractKeyboardButton;
import ru.kayashov.bar.bot.domain.model.AbstractMessage;
import ru.kayashov.bar.bot.domain.model.AbstractUpdate;
import java.util.List;
@Component
public class TelegramBotMapperImpl implements TelegramBotMapper {
@Override
public AbstractUpdate toAbstractUpdate(Update update) {
if (update == null) return null;
AbstractUpdate abstractUpdate = new AbstractUpdate();
abstractUpdate.setMessage(toAbstractMessage(update.getMessage()));
abstractUpdate.setCallbackQuery(toAbstractCallbackQuery(update.getCallbackQuery()));
return abstractUpdate;
}
@Override
public AbstractMessage toAbstractMessage(Message message) {
if (message == null) {
return null;
}
AbstractMessage abstractMessage = new AbstractMessage();
abstractMessage.setMessage(message.getText());
abstractMessage.setMessageId(message.getMessageId());
abstractMessage.setChat(toAbstractChat(message.getChat()));
abstractMessage.setChatId(message.getChatId());
return abstractMessage;
}
@Override
public AbstractCallbackQuery toAbstractCallbackQuery(CallbackQuery query) {
if (query == null) {
return null;
}
AbstractCallbackQuery callbackQuery = new AbstractCallbackQuery();
callbackQuery.setData(query.getData());
callbackQuery.setMessage(toAbstractMessage(query.getMessage()));
return callbackQuery;
}
@Override
public AbstractChat toAbstractChat(Chat chat) {
if (chat == null) {
return null;
}
AbstractChat abstractChat = new AbstractChat();
abstractChat.setId(chat.getId());
abstractChat.setFirstName(chat.getFirstName());
abstractChat.setLastName(chat.getLastName());
return abstractChat;
}
@Override
public InlineKeyboardMarkup toInlineKeyboard(AbstractKeyboard replyKeyboard) {
InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();
inlineKeyboardMarkup.setKeyboard(toInlineKeyboard(replyKeyboard.getKeyboard()));
return inlineKeyboardMarkup;
}
@Override
public ReplyKeyboardMarkup toReplyKeyboard(AbstractKeyboard replyKeyboard) {
return ReplyKeyboardMarkup.builder()
.keyboard(toKeyboardRows(replyKeyboard.getKeyboard()))
.resizeKeyboard(replyKeyboard.getResizeKeyboard())
.inputFieldPlaceholder(replyKeyboard.getInputFieldPlaceholder())
.oneTimeKeyboard(replyKeyboard.getOneTimeKeyboard())
.selective(replyKeyboard.getSelective())
.build();
}
@Override
public List<List<InlineKeyboardButton>> toInlineKeyboard(List<List<AbstractKeyboardButton>> buttons) {
return buttons.stream()
.map(this::toListButtons)
.toList();
}
@Override
public List<InlineKeyboardButton> toListButtons(List<AbstractKeyboardButton> buttons) {
return buttons.stream().map(this::toInlineKeyboardButton).toList();
}
private InlineKeyboardButton toInlineKeyboardButton(AbstractKeyboardButton button) {
InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton();
inlineKeyboardButton.setCallbackData(button.getCallbackData());
inlineKeyboardButton.setText(button.getText());
return inlineKeyboardButton;
}
@Override
public List<KeyboardRow> toKeyboardRows(List<List<AbstractKeyboardButton>> rows) {
return rows.stream()
.map(this::toKeyboardRow)
.toList();
}
@Override
public KeyboardRow toKeyboardRow(List<AbstractKeyboardButton> row) {
KeyboardRow keyboardRow = new KeyboardRow();
row.forEach(b -> keyboardRow.add(b.getText()));
return keyboardRow;
}
}

View File

@@ -1,17 +0,0 @@
package ru.kayashov.bar.bot.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Setter
@Getter
@Component
@ConfigurationProperties(prefix = "telegram.bot")
public class TelegramBotProperty {
private String username;
private String botToken;
}

View File

@@ -1,138 +0,0 @@
package ru.kayashov.bar.bot.config;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.telegram.telegrambots.bots.TelegramLongPollingBot;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.methods.updatingmessages.DeleteMessage;
import org.telegram.telegrambots.meta.api.objects.Message;
import org.telegram.telegrambots.meta.api.objects.Update;
import org.telegram.telegrambots.meta.api.objects.User;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboard;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;
import ru.kayashov.bar.bot.domain.Bot;
import ru.kayashov.bar.bot.domain.command.CommandContainer;
import ru.kayashov.bar.bot.domain.command.api.Command;
import ru.kayashov.bar.bot.domain.methods.AbstractDeleteMessage;
import ru.kayashov.bar.bot.domain.methods.AbstractInlineKeyboard;
import ru.kayashov.bar.bot.domain.methods.AbstractMethod;
import ru.kayashov.bar.bot.domain.methods.AbstractSendKeyboardMessage;
import ru.kayashov.bar.bot.domain.methods.AbstractSendMessage;
import ru.kayashov.bar.bot.domain.model.AbstractUpdate;
import java.util.List;
@Slf4j
@Component
@RequiredArgsConstructor
public class TelegramExecutorBot extends TelegramLongPollingBot implements Bot {
private final TelegramBotMapper mapper;
private final TelegramBotProperty botConfig;
private final CommandContainer commandContainer;
@Override
public String getBotUsername() {
return botConfig.getUsername();
}
@Override
public String getBotToken() {
return botConfig.getBotToken();
}
@Override
public void onUpdateReceived(Update update) {
if (update.hasMessage()) {
Message reply = update.getMessage().getReplyToMessage();
if (reply != null) {
String text = update.getMessage().getText();
String data = "Cocktail:jump:" + text + ":" + reply.getMessageId();
AbstractUpdate message = mapper.toAbstractUpdate(update);
message.getMessage().setMessage(data);
methodExecute(commandContainer.retrieveCommand("Cocktail").execute(message));
return;
}
if (update.hasMessage()) {
String data = update.getMessage().getText();
int dot = data.indexOf(':');
if (dot > -1) {
data = data.substring(0, data.indexOf(":"));
}
methodExecute(commandContainer
.retrieveCommand(data)
.execute(mapper.toAbstractUpdate(update)));
}
} else if (update.hasCallbackQuery() && !update.getCallbackQuery().getData().equals("null")) {
String callbackData = update.getCallbackQuery().getData();
String callbackCommand = callbackData.substring(0, callbackData.indexOf(":"));/*.replace("{\"", "");*/
Command command = commandContainer.retrieveCommand(callbackCommand);
List<? extends AbstractMethod> methods = command.execute(mapper.toAbstractUpdate(update));
methodExecute(methods);
}
}
@Override
public void sendMessage(AbstractSendMessage message) {
SendMessage sendMessage = SendMessage.builder()
.chatId(message.getChatId())
.text(message.getMessage())
.parseMode("HTML")
.build();
try {
execute(sendMessage);
log.info("AbstractMessage successfully sent to chat with id {}", message.getChatId());
} catch (TelegramApiException e) {
log.error("AbstractMessage {} didn't send to chat with id {}", message, message.getChatId(), e);
}
}
@Override
public void sendKeyboardMessage(AbstractSendKeyboardMessage message) {
send(getMessage(message.getChatId(), message.getMessage(), mapper.toReplyKeyboard(message.getKeyboard())));
}
@Override
public void sendInlineKeyboard(AbstractInlineKeyboard message) {
send(getMessage(message.getChatId(), message.getMessage(), mapper.toInlineKeyboard(message.getKeyboard())));
}
@Override
public void deleteMessage(AbstractDeleteMessage message) {
delete(getDeleteMessage(message.getChatId(), message.getMessageId()));
}
private SendMessage getMessage(Long chatId, String message, ReplyKeyboard replyKeyboard) {
return SendMessage.builder()
.chatId(chatId)
.text(message)
.parseMode("HTML")
.replyMarkup(replyKeyboard)
.build();
}
private DeleteMessage getDeleteMessage(Long chatId, Integer messageId) {
return DeleteMessage.builder()
.chatId(chatId)
.messageId(messageId)
.build();
}
private void delete(DeleteMessage abstractDeleteMessage) {
try {
execute(abstractDeleteMessage);
} catch (TelegramApiException e) {
e.printStackTrace();
}
}
private void send(SendMessage sendMessage) {
try {
execute(sendMessage);
log.info("AbstractMessage {} successfully sent to chat with id {}", sendMessage.getText(), sendMessage.getChatId());
} catch (TelegramApiException e) {
log.error("AbstractMessage {} didn't send to chat with id {}", sendMessage.getText(), sendMessage.getChatId(), e);
}
}
}

View File

@@ -1,24 +0,0 @@
package ru.kayashov.bar.bot.domain;
import ru.kayashov.bar.bot.domain.methods.AbstractDeleteMessage;
import ru.kayashov.bar.bot.domain.methods.AbstractInlineKeyboard;
import ru.kayashov.bar.bot.domain.methods.AbstractMethod;
import ru.kayashov.bar.bot.domain.methods.AbstractSendKeyboardMessage;
import ru.kayashov.bar.bot.domain.methods.AbstractSendMessage;
import java.util.List;
public interface Bot {
default void methodExecute(List<? extends AbstractMethod> methods) {
methods.forEach(x -> x.execute(this));
}
void deleteMessage(AbstractDeleteMessage abstractDeleteMessage);
void sendMessage(AbstractSendMessage abstractSendMessage);
void sendKeyboardMessage(AbstractSendKeyboardMessage keyboardMessage);
void sendInlineKeyboard(AbstractInlineKeyboard keyboardMessage);
}

View File

@@ -1,52 +0,0 @@
package ru.kayashov.bar.bot.domain.command;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import ru.kayashov.bar.bot.annotation.CommandData;
import ru.kayashov.bar.bot.domain.command.api.Command;
import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Getter
@Component
@RequiredArgsConstructor
public class CommandContainer {
private final List<Command> commandList;
@Qualifier("unknownCommand")
private final Command unknownCommand;
private final Map<String, Command> commandMap = new HashMap<>();
/**
* Метод, собирающий команды, помеченные аннотацией в список
*/
@PostConstruct
private void createCommandMap() throws ClassNotFoundException {
for(Command command : commandList) {
String proxyName = command.getClass().getName();
String className = proxyName.substring(0, proxyName.indexOf("$$"));
String name = Class.forName(className)
.getAnnotation(CommandData.class)
.name();
commandMap.put(name, command);
}
}
/**
* Метод поиска команды
*
* @param commandIdentifier - название команды, пришедшее от пользователя
* @return - Команда бота {@link }, либо сообщение о неизвестной команде
*/
public Command retrieveCommand(String commandIdentifier) {
return commandMap.getOrDefault(commandIdentifier, unknownCommand);
}
}

View File

@@ -1,45 +0,0 @@
package ru.kayashov.bar.bot.domain.command.api;
import org.springframework.transaction.annotation.Transactional;
import ru.kayashov.bar.bot.domain.methods.AbstractMethod;
import ru.kayashov.bar.bot.domain.model.AbstractCallbackQuery;
import ru.kayashov.bar.bot.domain.model.AbstractMessage;
import ru.kayashov.bar.bot.domain.model.AbstractUpdate;
import java.util.List;
public interface Command {
@Transactional
List<? extends AbstractMethod> execute (AbstractUpdate abstractUpdate);
default Long getChatId(AbstractUpdate abstractUpdate) {
AbstractMessage message = abstractUpdate.getMessage();
AbstractCallbackQuery query = abstractUpdate.getCallbackQuery();
if(message != null) {
return message.getChat().getId();
}
if (query != null) {
return query.getMessage().getChatId();
}
throw new RuntimeException("Не возможно найти id чата");
}
default Integer getMessageId(AbstractUpdate abstractUpdate) {
AbstractMessage message = abstractUpdate.getMessage();
AbstractCallbackQuery query = abstractUpdate.getCallbackQuery();
if(message != null) {
return message.getMessageId();
}
if (query != null) {
return query.getMessage().getMessageId();
}
throw new RuntimeException("Не возможно найти id сообщения");
}
}

View File

@@ -1,24 +0,0 @@
package ru.kayashov.bar.bot.domain.command.impl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import ru.kayashov.bar.bot.annotation.CommandData;
import ru.kayashov.bar.bot.domain.command.api.Command;
import ru.kayashov.bar.bot.domain.methods.AbstractMethod;
import ru.kayashov.bar.bot.domain.model.AbstractUpdate;
import java.util.List;
import static ru.kayashov.bar.bot.domain.utils.MethodUtils.deleteMessage;
@Component
@CommandData(name = "hide")
@RequiredArgsConstructor
public class HideCommand implements Command {
@Override
public List<? extends AbstractMethod> execute(AbstractUpdate abstractUpdate) {
return List.of(deleteMessage(abstractUpdate.getCallbackQuery().getMessage().getChatId(),
abstractUpdate.getCallbackQuery().getMessage().getMessageId()));
}
}

View File

@@ -1,49 +0,0 @@
package ru.kayashov.bar.bot.domain.command.impl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import ru.kayashov.bar.bot.annotation.CommandData;
import ru.kayashov.bar.bot.domain.command.api.Command;
import ru.kayashov.bar.bot.domain.methods.AbstractMethod;
import ru.kayashov.bar.bot.domain.model.AbstractChat;
import ru.kayashov.bar.bot.domain.model.AbstractUpdate;
import ru.kayashov.bar.model.entity.Visitor;
import ru.kayashov.bar.repository.VisitorsRepository;
import java.util.Base64;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import static ru.kayashov.bar.bot.domain.utils.MethodUtils.sendMessage;
@Component
@CommandData(name = "\uD83D\uDD11 Вход")
@RequiredArgsConstructor
public class LoginCommand implements Command {
private final VisitorsRepository visitorsRepository;
@Override
public List<? extends AbstractMethod> execute(AbstractUpdate abstractUpdate) {
AbstractChat chat = abstractUpdate.getMessage().getChat();
Random random = new Random();
final Integer code = random.nextInt(1010, 9999);
Optional<Visitor> visitorOpt = visitorsRepository.findById(chat.getId());
if (visitorOpt.isPresent()) {
Visitor visitor = visitorOpt.get();
visitor.setCode(code);
visitorsRepository.save(visitor);
String responseCode = chat.getId() + ":" + code;
responseCode = Base64.getEncoder().encodeToString(responseCode.getBytes());
String message = "Скопируйте код: <code>" + responseCode + "</code> или "
+ "<a href=\"https://bar.kayashov.keenetic.pro/tg?code=" + responseCode
+ "\">перейдите по ссылке</a>";
return List.of(sendMessage(chat.getId(), message));
}
String errorMessage = "Не удалось найти вас в системе. Попробуйте выполнить команду /start";
return List.of(sendMessage(chat.getId(), errorMessage));
}
}

View File

@@ -1,55 +0,0 @@
package ru.kayashov.bar.bot.domain.command.impl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import ru.kayashov.bar.bot.annotation.CommandData;
import ru.kayashov.bar.bot.domain.command.api.Command;
import ru.kayashov.bar.bot.domain.methods.AbstractMethod;
import ru.kayashov.bar.bot.domain.model.AbstractChat;
import ru.kayashov.bar.bot.domain.model.AbstractUpdate;
import ru.kayashov.bar.model.entity.Visitor;
import ru.kayashov.bar.repository.VisitorsRepository;
import java.util.List;
import java.util.Optional;
import static ru.kayashov.bar.bot.domain.keyboards.reply.ReplyKeyboardMarker.getMainMenu;
import static ru.kayashov.bar.bot.domain.utils.MethodUtils.sendKeyboardMessage;
/**
* Команда обработки запроса меню
* <p>
* Реализация {@link }
*/
@Component
@CommandData(name = "/start")
@RequiredArgsConstructor
public class StartCommand implements Command {
private final VisitorsRepository visitorsRepository;
@Override
public List<? extends AbstractMethod> execute(AbstractUpdate abstractUpdate) {
AbstractChat chat = abstractUpdate.getMessage().getChat();
Optional<Visitor> visitorOpt = visitorsRepository.findById(chat.getId());
String message;
Visitor visitor;
if (visitorOpt.isPresent()) {
visitor = visitorOpt.get();
message = "С возвращением ";
} else {
message = "Спасибо за регистрацию ";
visitor = new Visitor();
visitor.setId(chat.getId());
visitor.setName(chat.getFirstName());
visitor.setLastName(chat.getLastName());
visitor = visitorsRepository.save(visitor);
}
message = message + visitor.getName().strip() +
(visitor.getLastName() != null ? (" " + visitor.getLastName().strip() + "!") : "!");
return List.of(sendKeyboardMessage(chat.getId(), message, getMainMenu()));
}
}

View File

@@ -1,27 +0,0 @@
package ru.kayashov.bar.bot.domain.command.impl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import ru.kayashov.bar.bot.annotation.CommandData;
import ru.kayashov.bar.bot.domain.command.api.Command;
import ru.kayashov.bar.bot.domain.methods.AbstractMethod;
import ru.kayashov.bar.bot.domain.model.AbstractUpdate;
import java.util.Collections;
import java.util.List;
/**
* Обработка неизвестных команд
* <p>
* Реализация {@link Command}
*/
@Component
@CommandData(name = "unknownCommand")
@RequiredArgsConstructor
public class UnknownCommand implements Command {
@Override
public List<? extends AbstractMethod> execute(AbstractUpdate abstractUpdate) {
return Collections.emptyList();
}
}

View File

@@ -1,157 +0,0 @@
package ru.kayashov.bar.bot.domain.keyboards.inline;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import ru.kayashov.bar.bot.domain.model.AbstractKeyboard;
import ru.kayashov.bar.bot.domain.model.AbstractKeyboardButton;
import java.util.ArrayList;
import java.util.List;
/**
* Класс методов для создания инлайн клавиатур
*/
@Component
@RequiredArgsConstructor
public class InlineKeyboardMarker {
/**
* Преобразование списка кнопок в блок основных кнопок
*
* @param sizeRow опциональный размер строки
* @param abstractKeyboardButtons список кнопок для блока
* @return блок основных кнопок
*/
public List<List<AbstractKeyboardButton>> getMainButtons(Integer sizeRow, List<AbstractKeyboardButton> abstractKeyboardButtons) {
// int rowSize = PaginationUtil.getRowSize(sizeRow);
int rowSize = sizeRow;
int rowCountInPage = getRowCount(abstractKeyboardButtons.size(), rowSize);
List<List<AbstractKeyboardButton>> buttons = new ArrayList<>();
for (int row = 0; row < rowCountInPage; row++) {
List<AbstractKeyboardButton> rowButtons = new ArrayList<>(rowSize);
for (int i = 0; i < rowSize; i++) {
int index = (row * rowSize) + i;
if (index >= abstractKeyboardButtons.size()) {
break;
}
rowButtons.add(abstractKeyboardButtons.get(index));
}
buttons.add(rowButtons);
}
return buttons;
}
/**
* Запрос количества строк для основных кнопок
*
* @param elementsOnPage элементов на странице или общее количество
* @param rowSize количество элементов в строке
* @return число строк основных кнопок
*/
private int getRowCount(int elementsOnPage, int rowSize) {
return elementsOnPage / rowSize + (elementsOnPage % rowSize == 0 ? 0 : 1);
}
/**
* Метод создания кнопок пагинации
*
* @return ряд кнопок для пагинации
*/
public List<AbstractKeyboardButton> getPaginationButtons(int index, int total) {
List<AbstractKeyboardButton> buttons = new ArrayList<>();
String pageName = "";
String callback = index != 0 ? "Cocktail:get:" + (index - 1) : "null";
buttons.add(getButtonForPage(callback, pageName));
buttons.add(AbstractKeyboardButton.builder().text(index + 1 + "/" + total).callbackData("null").build());
pageName = "";
callback = index < total - 1 ? "Cocktail:get:" + (index + 1) : "null";
buttons.add(getButtonForPage(callback, pageName));
return buttons;
}
/**
* Метод создания кнопки
*
* @param callBack данные, возвращаемые на сервер
* @param text текст, видимый пользователю
* @return кнопка для вставки в текст
*/
public AbstractKeyboardButton getButtonForPage(String callBack, String text) {
return AbstractKeyboardButton.builder()
.text(text)
.callbackData(callBack)
.build();
}
public static class builder {
private final List<List<AbstractKeyboardButton>> buttons = new ArrayList<>();
private List<List<AbstractKeyboardButton>> mainButtons;
private List<AbstractKeyboardButton> pagination;
private List<AbstractKeyboardButton> back;
private List<AbstractKeyboardButton> hideButton;
private List<AbstractKeyboardButton> stop;
public builder() {
}
public builder buttons(List<List<AbstractKeyboardButton>> buttons) {
this.mainButtons = buttons;
return this;
}
public builder pagination(List<AbstractKeyboardButton> buttons) {
this.pagination = buttons;
return this;
}
public builder backButton(AbstractKeyboardButton button) {
if (button != null)
this.back = List.of(button);
return this;
}
public builder hidden(boolean isActive) {
if (isActive) {
AbstractKeyboardButton button = AbstractKeyboardButton.builder()
.text("Скрыть")
.callbackData("hide:")
.build();
this.hideButton = List.of(button);
}
return this;
}
public builder stopList(boolean isActive, Long cocktailId) {
if (isActive) {
AbstractKeyboardButton button = AbstractKeyboardButton.builder()
.text("Стоп-лист")
.callbackData("Cocktail:stop:" + cocktailId)
.build();
this.stop = List.of(button);
}
return this;
}
public AbstractKeyboard build() {
if (mainButtons != null)
buttons.addAll(mainButtons);
if (pagination != null)
buttons.add(pagination);
if (back != null)
buttons.add(back);
if (hideButton != null)
buttons.add(hideButton);
if (stop != null)
buttons.add(stop);
return AbstractKeyboard.builder().keyboard(buttons).build();
}
}
}

View File

@@ -1,36 +0,0 @@
package ru.kayashov.bar.bot.domain.keyboards.reply;
import lombok.experimental.UtilityClass;
import ru.kayashov.bar.bot.domain.model.AbstractKeyboard;
import ru.kayashov.bar.bot.domain.model.AbstractKeyboardButton;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@UtilityClass
public class ReplyKeyboardMarker {
public static AbstractKeyboard getMainMenu() {
String[] menu;
String mainMenu = "\uD83D\uDD11 Вход";
menu = new String[]{mainMenu/*, " Ингредиенты", "✅ Взять заказ", "💵 Что докупить?"*/};
List<List<AbstractKeyboardButton>> keyboard = getKeyboardRows(menu);
return AbstractKeyboard.builder()
.keyboard(keyboard)
.inputFieldPlaceholder("Меню")
.resizeKeyboard(true)
.selective(true)
.oneTimeKeyboard(false)
.build();
}
private static List<List<AbstractKeyboardButton>> getKeyboardRows(String... buttons) {
return Arrays
.stream(buttons)
.map(x -> List.of(new AbstractKeyboardButton(x)))
.collect(Collectors.toList());
}
}

View File

@@ -1,22 +0,0 @@
package ru.kayashov.bar.bot.domain.methods;
import lombok.Builder;
import lombok.Getter;
import ru.kayashov.bar.bot.domain.Bot;
@Getter
public class AbstractDeleteMessage extends AbstractMethod {
Integer messageId;
@Builder
public AbstractDeleteMessage(Long chatId, Integer messageId) {
super(chatId);
this.messageId = messageId;
}
@Override
public void execute(Bot nBot) {
nBot.deleteMessage(this);
}
}

View File

@@ -1,25 +0,0 @@
package ru.kayashov.bar.bot.domain.methods;
import lombok.Builder;
import lombok.Getter;
import ru.kayashov.bar.bot.domain.Bot;
import ru.kayashov.bar.bot.domain.model.AbstractKeyboard;
@Getter
public class AbstractInlineKeyboard extends AbstractMethod {
private final String message;
private final AbstractKeyboard keyboard;
@Builder
public AbstractInlineKeyboard(Long chatId, String message, AbstractKeyboard keyboard) {
super(chatId);
this.message = message;
this.keyboard = keyboard;
}
@Override
public void execute(Bot nBot) {
nBot.sendInlineKeyboard(this);
}
}

View File

@@ -1,13 +0,0 @@
package ru.kayashov.bar.bot.domain.methods;
import lombok.AllArgsConstructor;
import lombok.Getter;
import ru.kayashov.bar.bot.domain.Bot;
@Getter
@AllArgsConstructor
public abstract class AbstractMethod {
private Long chatId;
public abstract void execute(Bot nBot);
}

View File

@@ -1,23 +0,0 @@
package ru.kayashov.bar.bot.domain.methods;
import lombok.Builder;
import lombok.Getter;
import ru.kayashov.bar.bot.domain.Bot;
import ru.kayashov.bar.bot.domain.model.AbstractKeyboard;
@Getter
public class AbstractSendKeyboardMessage extends AbstractMethod {
String message;
AbstractKeyboard keyboard;
@Builder
public AbstractSendKeyboardMessage(Long chatId, String message, AbstractKeyboard keyboard) {
super(chatId);
this.keyboard = keyboard;
this.message = message;
}
@Override
public void execute(Bot nBot) {
nBot.sendKeyboardMessage(this);
}
}

View File

@@ -1,19 +0,0 @@
package ru.kayashov.bar.bot.domain.methods;
import lombok.Builder;
import lombok.Getter;
import ru.kayashov.bar.bot.domain.Bot;
@Getter
public class AbstractSendMessage extends AbstractMethod {
String message;
@Builder
public AbstractSendMessage(Long chatId, String message) {
super(chatId);
this.message = message;
}
public void execute(Bot bot) {
bot.sendMessage(this);
}
}

View File

@@ -1,11 +0,0 @@
package ru.kayashov.bar.bot.domain.model;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class AbstractCallbackQuery {
private AbstractMessage message;
private String data;
}

View File

@@ -1,12 +0,0 @@
package ru.kayashov.bar.bot.domain.model;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class AbstractChat {
private Long id;
private String firstName;
private String lastName;
}

View File

@@ -1,16 +0,0 @@
package ru.kayashov.bar.bot.domain.model;
import lombok.Builder;
import lombok.Data;
import java.util.List;
@Data
@Builder
public class AbstractKeyboard {
private List<List<AbstractKeyboardButton>> keyboard;
private String inputFieldPlaceholder;
private Boolean resizeKeyboard;
private Boolean selective;
private Boolean oneTimeKeyboard;
}

View File

@@ -1,21 +0,0 @@
package ru.kayashov.bar.bot.domain.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AbstractKeyboardButton {
private String text;
private String callbackData;
public AbstractKeyboardButton(String text) {
this.text = text;
}
}

View File

@@ -1,13 +0,0 @@
package ru.kayashov.bar.bot.domain.model;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class AbstractMessage {
private Long chatId;
private Integer messageId;
private String message;
private AbstractChat chat;
}

View File

@@ -1,12 +0,0 @@
package ru.kayashov.bar.bot.domain.model;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class AbstractUpdate {
private AbstractCallbackQuery callbackQuery;
private AbstractMessage message;
}

View File

@@ -1,40 +0,0 @@
package ru.kayashov.bar.bot.domain.utils;
import ru.kayashov.bar.bot.domain.methods.AbstractDeleteMessage;
import ru.kayashov.bar.bot.domain.methods.AbstractInlineKeyboard;
import ru.kayashov.bar.bot.domain.methods.AbstractSendKeyboardMessage;
import ru.kayashov.bar.bot.domain.methods.AbstractSendMessage;
import ru.kayashov.bar.bot.domain.model.AbstractKeyboard;
public class MethodUtils {
public static AbstractDeleteMessage deleteMessage(Long chatId, Integer messageId) {
return AbstractDeleteMessage.builder()
.messageId(messageId)
.chatId(chatId)
.build();
}
public static AbstractSendMessage sendMessage(Long chatId, String message) {
return AbstractSendMessage.builder()
.message(message)
.chatId(chatId)
.build();
}
public static AbstractSendKeyboardMessage sendKeyboardMessage(Long chatId, String message, AbstractKeyboard keyboard) {
return AbstractSendKeyboardMessage.builder()
.keyboard(keyboard)
.message(message)
.chatId(chatId)
.build();
}
public static AbstractInlineKeyboard sendInlineKeyboard(Long chatId, String message, AbstractKeyboard keyboard) {
return AbstractInlineKeyboard.builder()
.keyboard(keyboard)
.message(message)
.chatId(chatId)
.build();
}
}

View File

@@ -0,0 +1,39 @@
package ru.kayashov.bar.config;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.serialization.StringSerializer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.core.DefaultKafkaProducerFactory;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.core.ProducerFactory;
import ru.kayashov.bar.model.AbstractSendMessage;
import ru.kayashov.bar.service.AbstractMessageSerializer;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class KafkaConfig {
@Value("${spring.kafka.bootstrap-servers}")
private String bootstrapServers;
@Bean
public ProducerFactory<String, AbstractSendMessage> producerFactory() {
Map<String, Object> configProps = new HashMap<>();
configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, AbstractMessageSerializer.class);
configProps.put(ProducerConfig.MAX_REQUEST_SIZE_CONFIG, 31457280);
return new DefaultKafkaProducerFactory<>(configProps);
}
@Bean
public KafkaTemplate<String, AbstractSendMessage> kafkaTemplate(ProducerFactory<String, AbstractSendMessage> factory) {
KafkaTemplate<String, AbstractSendMessage> broker = new KafkaTemplate<>(factory);
broker.flush();
return broker;
}
}

View File

@@ -2,6 +2,7 @@ package ru.kayashov.bar.controller;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
@@ -11,13 +12,13 @@ import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import ru.kayashov.bar.bot.domain.Bot;
import ru.kayashov.bar.bot.domain.methods.AbstractSendMessage;
import ru.kayashov.bar.controller.dto.OrderResponseDto;
import ru.kayashov.bar.model.AbstractSendMessage;
import ru.kayashov.bar.model.entity.BarEntity;
import ru.kayashov.bar.model.entity.BarResident;
import ru.kayashov.bar.model.entity.SessionEntity;
import ru.kayashov.bar.model.entity.Visitor;
import ru.kayashov.bar.service.KafkaSender;
import ru.kayashov.bar.service.OrderService;
import ru.kayashov.bar.service.VisitorService;
@@ -32,28 +33,31 @@ import java.util.concurrent.ExecutorService;
@RequiredArgsConstructor
public class OrderController {
@Value("${kafka-topic")
private String topic;
private final ExecutorService executor;
private final OrderService orderService;
private final Bot bot;
private final KafkaSender kafkaSender;
private final VisitorService visitorService;
@PostMapping
public void pay(@RequestParam Long cocktail) {
Long id = ((Visitor) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getId();
List<AbstractSendMessage> messages = orderService.createOrder(id, cocktail);
executor.submit(() -> messages.forEach(bot::sendMessage));
executor.submit(() -> messages.forEach(m -> kafkaSender.send(topic, m)));
}
@DeleteMapping
public void cancelOrder(@RequestParam Long id) {
AbstractSendMessage message = orderService.updateOrder(false, id);
executor.submit(() -> bot.sendMessage(message));
executor.submit(() -> kafkaSender.send(topic, message));
}
@PutMapping
public void doneOrder(@RequestParam Long id) {
AbstractSendMessage message = orderService.updateOrder(true, id);
executor.submit(() -> bot.sendMessage(message));
executor.submit(() -> kafkaSender.send(topic, message));
}
@GetMapping

View File

@@ -0,0 +1,11 @@
package ru.kayashov.bar.model;
import lombok.Builder;
import lombok.Getter;
@Builder
@Getter
public class AbstractSendMessage {
private Long chatId;
private String message;
}

View File

@@ -0,0 +1,42 @@
package ru.kayashov.bar.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.common.errors.SerializationException;
import org.apache.kafka.common.header.Headers;
import org.apache.kafka.common.serialization.Serializer;
import ru.kayashov.bar.model.AbstractSendMessage;
import java.util.Map;
@Slf4j
public class AbstractMessageSerializer implements Serializer<AbstractSendMessage> {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public void configure(Map<String, ?> configs, boolean isKey) {
}
@Override
public byte[] serialize(String topic, AbstractSendMessage data) {
try {
if (data == null){
log.warn("Отсутствует объект для отправки сообщения");
return null;
}
return objectMapper.writeValueAsBytes(data);
} catch (Exception e) {
throw new SerializationException("Ошибка сериализации объекта");
}
}
@Override
public byte[] serialize(String topic, Headers headers, AbstractSendMessage data) {
return Serializer.super.serialize(topic, headers, data);
}
@Override
public void close() {
}
}

View File

@@ -0,0 +1,28 @@
package ru.kayashov.bar.service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Component;
import ru.kayashov.bar.model.AbstractSendMessage;
import java.util.Arrays;
@Slf4j
@Component
@RequiredArgsConstructor
public class KafkaSender {
private final KafkaTemplate<String, AbstractSendMessage> kafkaTemplate;
public String send(String topic, AbstractSendMessage message) {
try {
kafkaTemplate.send(topic, message);
log.info("отправлено сообщение в топик {}, сообщение {}", topic, message.getMessage());
return null;
} catch (Exception e) {
log.error(e.getMessage(), Arrays.toString(e.getStackTrace()));
return e.getMessage();
}
}
}

View File

@@ -2,7 +2,7 @@ package ru.kayashov.bar.service;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import ru.kayashov.bar.bot.domain.methods.AbstractSendMessage;
import ru.kayashov.bar.model.AbstractSendMessage;
import ru.kayashov.bar.model.entity.BarEntity;
import ru.kayashov.bar.model.entity.BarResident;
import ru.kayashov.bar.model.entity.CocktailEntity;
@@ -11,7 +11,6 @@ import ru.kayashov.bar.model.entity.SessionEntity;
import ru.kayashov.bar.model.entity.Visitor;
import ru.kayashov.bar.repository.CocktailRepository;
import ru.kayashov.bar.repository.OrdersRepository;
import ru.kayashov.bar.repository.VisitorsRepository;
import java.time.LocalDateTime;
import java.util.List;

View File

@@ -1,4 +1,6 @@
spring.application.name=myBar
spring.kafka.bootstrap-servers=192.168.1.100:29092
kafka-topic=my-bar-bot-message
cocktail.photo.path=${COCKTAIL_PHOTO_PATH:/mnt/sdb1/my-bar-front/build/assets/cocktails}