diff --git a/stbf-common/src/main/java/ru/penkrat/stbf/common/command/SessionAwareCommandChain.java b/stbf-common/src/main/java/ru/penkrat/stbf/common/command/SessionAwareCommandChain.java new file mode 100644 index 0000000..1073185 --- /dev/null +++ b/stbf-common/src/main/java/ru/penkrat/stbf/common/command/SessionAwareCommandChain.java @@ -0,0 +1,20 @@ +package ru.penkrat.stbf.common.command; + +import lombok.RequiredArgsConstructor; +import ru.penkrat.stbf.api.BotRequest; +import ru.penkrat.stbf.api.BotResponse; +import ru.penkrat.stbf.api.CommandChain; +import ru.penkrat.stbf.api.SessionProvider; +import ru.penkrat.stbf.common.session.SessionAwareBotRequest; + +@RequiredArgsConstructor +public class SessionAwareCommandChain implements CommandChain { + + private final CommandChain delegate; + private final SessionProvider sessionProvider; + + @Override + public void processCommand(BotRequest botRequest, BotResponse botResponse) { + delegate.processCommand(new SessionAwareBotRequest(botRequest, sessionProvider), botResponse); + } +} diff --git a/stbf-common/src/main/java/ru/penkrat/stbf/common/command/ThreadPolledCommandChain.java b/stbf-common/src/main/java/ru/penkrat/stbf/common/command/ThreadPolledCommandChain.java new file mode 100644 index 0000000..0984c72 --- /dev/null +++ b/stbf-common/src/main/java/ru/penkrat/stbf/common/command/ThreadPolledCommandChain.java @@ -0,0 +1,31 @@ +package ru.penkrat.stbf.common.command; + +import ru.penkrat.stbf.api.BotRequest; +import ru.penkrat.stbf.api.BotResponse; +import ru.penkrat.stbf.api.CommandChain; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class ThreadPolledCommandChain implements CommandChain { + + private final CommandChain delegate; + private final ExecutorService executor; + + public ThreadPolledCommandChain(CommandChain delegate) { + this(delegate, 4); + } + + public ThreadPolledCommandChain(CommandChain delegate, int nThreads) { + this.delegate = delegate; + this.executor = Executors.newFixedThreadPool(nThreads); + } + + @Override + public void processCommand(BotRequest botRequest, BotResponse botResponse) { + executor.execute(() -> { + delegate.processCommand(botRequest, botResponse); + }); + } + +} diff --git a/stbf-common/src/main/java/ru/penkrat/stbf/common/session/SessionAwareBotRequest.java b/stbf-common/src/main/java/ru/penkrat/stbf/common/session/SessionAwareBotRequest.java new file mode 100644 index 0000000..283257f --- /dev/null +++ b/stbf-common/src/main/java/ru/penkrat/stbf/common/session/SessionAwareBotRequest.java @@ -0,0 +1,49 @@ +package ru.penkrat.stbf.common.session; + +import lombok.RequiredArgsConstructor; +import ru.penkrat.stbf.api.BotRequest; +import ru.penkrat.stbf.api.BotSession; +import ru.penkrat.stbf.api.SessionProvider; + +import java.util.Objects; +import java.util.Optional; + +@RequiredArgsConstructor +public class SessionAwareBotRequest implements BotRequest { + + private final BotRequest delegate; + + private final SessionProvider sessionProvider; + + private BotSession botSession; + + @Override + public BotSession getSession() { + if (botSession == null) { + botSession = sessionProvider.get(getChatId()); + Objects.requireNonNull(botSession, "Session can not be null"); + } + return botSession; + } + + public Optional getMessageText() { + return this.delegate.getMessageText(); + } + + public Optional getPhoneNumber() { + return this.delegate.getPhoneNumber(); + } + + public Optional getCallbackData() { + return this.delegate.getCallbackData(); + } + + public Optional getCallbackMessageText() { + return this.delegate.getCallbackMessageText(); + } + + public Long getChatId() { + return this.delegate.getChatId(); + } + +} diff --git a/stbf-demo/src/main/java/ru/penkrat/stbf/demo/App.java b/stbf-demo/src/main/java/ru/penkrat/stbf/demo/App.java index 29c34c8..0f961ac 100644 --- a/stbf-demo/src/main/java/ru/penkrat/stbf/demo/App.java +++ b/stbf-demo/src/main/java/ru/penkrat/stbf/demo/App.java @@ -5,6 +5,8 @@ import org.slf4j.LoggerFactory; import picocli.CommandLine; import ru.penkrat.stbf.api.BotCommandChain; import ru.penkrat.stbf.api.CommandChain; +import ru.penkrat.stbf.common.command.SessionAwareCommandChain; +import ru.penkrat.stbf.common.command.ThreadPolledCommandChain; import ru.penkrat.stbf.common.session.InMemBotSessionProvider; import ru.penkrat.stbf.impl.pengrad.PengradTelegramBot; import ru.penkrat.stbf.templates.xml.XmlFlowResolver; @@ -12,58 +14,58 @@ import ru.penkrat.stbf.templates.xml.XmlFlowResolver; import java.util.concurrent.atomic.AtomicReference; @CommandLine.Command(name = "java -jar stbf-demo.jar", mixinStandardHelpOptions = true, description = "Run bot", - version = "Simple Telegram bot framework Demo app v. 0.0.1") + version = "Simple Telegram bot framework Demo app v. 0.0.2") public class App implements Runnable { - private static final Logger log = LoggerFactory.getLogger(App.class); + private static final Logger log = LoggerFactory.getLogger(App.class); - private static AtomicReference botAtomicReference = new AtomicReference<>(); + private static AtomicReference botAtomicReference = new AtomicReference<>(); - @CommandLine.Option(names = {"-f", "--file"}, description = "The file with config.") - private String flowFile = "classpath:/flow.xml"; + @CommandLine.Option(names = {"-f", "--file"}, description = "The file with config.") + private String flowFile = "classpath:/flow.xml"; - @CommandLine.Option(names = {"-t", "--token"}, required = true, description = "bot token 1234:abscdf...") - private String botToken = ""; + @CommandLine.Option(names = {"-t", "--token"}, required = true, description = "bot token 1234:abscdf...") + private String botToken = ""; - public static void main(String[] args) { - new CommandLine(new App()).execute(args); - } + public static void main(String[] args) { + new CommandLine(new App()).execute(args); + } - private BotCommandChain getCommandChain(String filename) { - XmlFlowResolver flow = new XmlFlowResolver(filename); + private CommandChain getCommandChain(String filename) { + XmlFlowResolver flow = new XmlFlowResolver(filename); - BotCommandChain chain = new BotCommandChain(); - flow.getCommands().forEach(chain::add); + BotCommandChain chain = new BotCommandChain(); + flow.getCommands().forEach(chain::add); - return chain; - } + return new ThreadPolledCommandChain(new SessionAwareCommandChain(chain, new InMemBotSessionProvider())); + } - private Runnable start(String token, CommandChain chain) { - return () -> botAtomicReference.set(new PengradTelegramBot(token, chain, new InMemBotSessionProvider())); - } + private Runnable start(String token, CommandChain chain) { + return () -> botAtomicReference.set(new PengradTelegramBot(token, chain)); + } - private void onShutdown() { - try { - if (botAtomicReference.get() != null) { - botAtomicReference.get().close(); - log.info("Bot finished."); - } - } catch (Exception e) { - log.error("Error:", e); - } - } + private void onShutdown() { + try { + if (botAtomicReference.get() != null) { + botAtomicReference.get().close(); + log.info("Bot finished."); + } + } catch (Exception e) { + log.error("Error:", e); + } + } - @Override - public void run() { - Thread botThread = new Thread(start(botToken, getCommandChain(flowFile))); - botThread.setDaemon(false); - botThread.setName("stbf-bot-thread"); + @Override + public void run() { + Thread botThread = new Thread(start(botToken, getCommandChain(flowFile))); + botThread.setDaemon(false); + botThread.setName("stbf-bot-thread"); - Runtime.getRuntime().addShutdownHook(new Thread(this::onShutdown)); + Runtime.getRuntime().addShutdownHook(new Thread(this::onShutdown)); - log.info("Starting bot..."); - botThread.start(); - log.info("Bot started."); - log.info("Press Ctrl+C to exit."); - } + log.info("Starting bot..."); + botThread.start(); + log.info("Bot started."); + log.info("Press Ctrl+C to exit."); + } } diff --git a/stbf-pengrad/src/main/java/ru/penkrat/stbf/impl/pengrad/BotRequestImpl.java b/stbf-pengrad/src/main/java/ru/penkrat/stbf/impl/pengrad/BotRequestImpl.java index 9af397a..368ba0f 100644 --- a/stbf-pengrad/src/main/java/ru/penkrat/stbf/impl/pengrad/BotRequestImpl.java +++ b/stbf-pengrad/src/main/java/ru/penkrat/stbf/impl/pengrad/BotRequestImpl.java @@ -16,49 +16,51 @@ import static lombok.AccessLevel.PROTECTED; @RequiredArgsConstructor public class BotRequestImpl implements BotRequest { - @Getter(PROTECTED) - private final Update update; + @Getter(PROTECTED) + private final Update update; - @Getter - private final BotSession session; + @Override + public Optional getMessageText() { + if (update.message() != null) { + return Optional.ofNullable(update.message().text()); + } + return Optional.empty(); + } - @Override - public Optional getMessageText() { - if (update.message() != null) { - return Optional.ofNullable(update.message().text()); - } - return Optional.empty(); - } + @Override + public Optional getPhoneNumber() { + return Optional.of(update) + .map(Update::message) + .map(Message::contact) + .map(Contact::phoneNumber); + } - @Override - public Optional getPhoneNumber() { - return Optional.of(update) - .map(Update::message) - .map(Message::contact) - .map(Contact::phoneNumber); - } + @Override + public Optional getCallbackData() { + return Optional.of(update) + .map(Update::callbackQuery) + .map(CallbackQuery::data); + } - @Override - public Optional getCallbackData() { - return Optional.of(update) - .map(Update::callbackQuery) - .map(CallbackQuery::data); - } + @Override + public Optional getCallbackMessageText() { + return Optional.of(update) + .map(Update::callbackQuery) + .map(CallbackQuery::message) + .map(Message::text); + } - @Override - public Optional getCallbackMessageText() { - return Optional.of(update) - .map(Update::callbackQuery) - .map(CallbackQuery::message) - .map(Message::text); - } + @Override + public Long getChatId() { + return Optional.of(update) + .map(Update::callbackQuery) + .map(CallbackQuery::message) + .orElseGet(() -> update.message()).chat().id(); + } - @Override - public Long getChatId() { - return Optional.of(update) - .map(Update::callbackQuery) - .map(CallbackQuery::message) - .orElseGet(() -> update.message()).chat().id(); - } + @Override + public BotSession getSession() { + throw new UnsupportedOperationException("Session is not supported"); + } } diff --git a/stbf-pengrad/src/main/java/ru/penkrat/stbf/impl/pengrad/KeyboardImpl.java b/stbf-pengrad/src/main/java/ru/penkrat/stbf/impl/pengrad/KeyboardImpl.java index 10c390b..9ba1a60 100644 --- a/stbf-pengrad/src/main/java/ru/penkrat/stbf/impl/pengrad/KeyboardImpl.java +++ b/stbf-pengrad/src/main/java/ru/penkrat/stbf/impl/pengrad/KeyboardImpl.java @@ -1,6 +1,6 @@ package ru.penkrat.stbf.impl.pengrad; -import com.google.gson.internal.reflect.ReflectionAccessor; +import com.google.gson.internal.reflect.ReflectionHelper; import com.pengrad.telegrambot.model.request.InlineKeyboardButton; import com.pengrad.telegrambot.model.request.KeyboardButton; import lombok.Value; @@ -58,7 +58,7 @@ class KeyboardImpl implements Keyboard { return text.computeIfAbsent(btn, o -> { try { Field text = btn.getClass().getDeclaredField("text"); - ReflectionAccessor.getInstance().makeAccessible(text); + ReflectionHelper.makeAccessible(text); return String.valueOf(text.get(btn)); } catch (NoSuchFieldException | IllegalAccessException e) { return "*error*"; diff --git a/stbf-pengrad/src/main/java/ru/penkrat/stbf/impl/pengrad/PengradTelegramBot.java b/stbf-pengrad/src/main/java/ru/penkrat/stbf/impl/pengrad/PengradTelegramBot.java index d20203e..3bc599c 100644 --- a/stbf-pengrad/src/main/java/ru/penkrat/stbf/impl/pengrad/PengradTelegramBot.java +++ b/stbf-pengrad/src/main/java/ru/penkrat/stbf/impl/pengrad/PengradTelegramBot.java @@ -6,46 +6,39 @@ import com.pengrad.telegrambot.model.CallbackQuery; import com.pengrad.telegrambot.model.Update; import lombok.extern.slf4j.Slf4j; import ru.penkrat.stbf.api.CommandChain; -import ru.penkrat.stbf.api.SessionProvider; import java.util.Optional; @Slf4j public class PengradTelegramBot extends TelegramBot implements AutoCloseable { - public PengradTelegramBot(String botToken, CommandChain commandChain) { - this(botToken, commandChain, (id) -> { - throw new IllegalArgumentException("'SessionProvider' is not defined"); - }); - } + public PengradTelegramBot(String botToken, CommandChain commandChain) { + super(botToken); + this.setUpdatesListener(updates -> { + for (Update update : updates) { + try { + final Long chatId = Optional.of(update) + .map(Update::callbackQuery) + .map(CallbackQuery::message) + .orElseGet(() -> update.message()).chat().id(); - public PengradTelegramBot(String botToken, CommandChain commandChain, SessionProvider sessionProvider) { - super(botToken); - this.setUpdatesListener(updates -> { - for (Update update : updates) { - try { - final Long chatId = Optional.of(update) - .map(Update::callbackQuery) - .map(CallbackQuery::message) - .orElseGet(() -> update.message()).chat().id(); + log.debug("New message in chat {}", chatId); - log.debug("New message in chat {}", chatId); + commandChain.processCommand( + new BotRequestImpl(update), + new BotResponseImpl(update, this)); + } catch (Exception e) { + log.error("Bot Error:", e); + } + } + return UpdatesListener.CONFIRMED_UPDATES_ALL; + }); + } - commandChain.processCommand( - new BotRequestImpl(update, sessionProvider.get(chatId)), - new BotResponseImpl(update, this)); - } catch (Exception e) { - log.error("Bot Error:", e); - } - } - return UpdatesListener.CONFIRMED_UPDATES_ALL; - }); - } - - @Override - public void close() throws Exception { - removeGetUpdatesListener(); - log.debug("Bot closed."); - } + @Override + public void close() throws Exception { + removeGetUpdatesListener(); + log.debug("Bot closed."); + } } diff --git a/stbf-rubenlagus/src/main/java/ru/penkrat/stbf/impl/rubenlagus/BotRequestImpl.java b/stbf-rubenlagus/src/main/java/ru/penkrat/stbf/impl/rubenlagus/BotRequestImpl.java index 3d5ab6a..b6182ec 100644 --- a/stbf-rubenlagus/src/main/java/ru/penkrat/stbf/impl/rubenlagus/BotRequestImpl.java +++ b/stbf-rubenlagus/src/main/java/ru/penkrat/stbf/impl/rubenlagus/BotRequestImpl.java @@ -16,46 +16,48 @@ import static lombok.AccessLevel.PROTECTED; @RequiredArgsConstructor class BotRequestImpl implements BotRequest { - @Getter(PROTECTED) - private final Update update; + @Getter(PROTECTED) + private final Update update; - @Getter - private final BotSession session; + @Override + public Optional getMessageText() { + if (update.hasMessage()) { + return Optional.ofNullable(update.getMessage().getText()); + } + return Optional.empty(); + } - @Override - public Optional getMessageText() { - if (update.hasMessage()) { - return Optional.ofNullable(update.getMessage().getText()); - } - return Optional.empty(); - } + @Override + public Optional getPhoneNumber() { + return Optional.of(update) + .map(Update::getMessage) + .map(Message::getContact) + .map(Contact::getPhoneNumber); + } - @Override - public Optional getPhoneNumber() { - return Optional.of(update) - .map(Update::getMessage) - .map(Message::getContact) - .map(Contact::getPhoneNumber); - } + @Override + public Optional getCallbackData() { + return Optional.of(update) + .map(Update::getCallbackQuery) + .map(CallbackQuery::getData); + } - @Override - public Optional getCallbackData() { - return Optional.of(update) - .map(Update::getCallbackQuery) - .map(CallbackQuery::getData); - } + @Override + public Optional getCallbackMessageText() { + return Optional.of(update) + .map(Update::getCallbackQuery) + .map(CallbackQuery::getMessage) + .map(Message::getText); + } - @Override - public Optional getCallbackMessageText() { - return Optional.of(update) - .map(Update::getCallbackQuery) - .map(CallbackQuery::getMessage) - .map(Message::getText); - } + @Override + public Long getChatId() { + return Utils.getChatId(update); + } - @Override - public Long getChatId() { - return Utils.getChatId(update); - } + @Override + public BotSession getSession() { + throw new UnsupportedOperationException("Session is not supported"); + } } diff --git a/stbf-rubenlagus/src/main/java/ru/penkrat/stbf/impl/rubenlagus/RubenlagusTelegramBot.java b/stbf-rubenlagus/src/main/java/ru/penkrat/stbf/impl/rubenlagus/RubenlagusTelegramBot.java index 90cc35e..a8f2399 100644 --- a/stbf-rubenlagus/src/main/java/ru/penkrat/stbf/impl/rubenlagus/RubenlagusTelegramBot.java +++ b/stbf-rubenlagus/src/main/java/ru/penkrat/stbf/impl/rubenlagus/RubenlagusTelegramBot.java @@ -7,56 +7,47 @@ import org.telegram.telegrambots.meta.api.objects.Update; import org.telegram.telegrambots.meta.exceptions.TelegramApiException; import org.telegram.telegrambots.updatesreceivers.DefaultBotSession; import ru.penkrat.stbf.api.BotCommandChain; -import ru.penkrat.stbf.api.SessionProvider; @Slf4j public class RubenlagusTelegramBot extends TelegramLongPollingBot { - private final String botUsername; - private final String botToken; - private final BotCommandChain commandChain; - private final SessionProvider sessionProvider; + private final String botUsername; + private final String botToken; + private final BotCommandChain commandChain; - public RubenlagusTelegramBot(String botUsername, String botToken, BotCommandChain botCommandChain) { - this(botUsername, botToken, botCommandChain, (id) -> { - throw new IllegalArgumentException("'SessionProvider' is not defined"); - }); - } + public RubenlagusTelegramBot(String botUsername, String botToken, BotCommandChain botCommandChain) { + this.botUsername = botUsername; + this.botToken = botToken; + this.commandChain = botCommandChain; - public RubenlagusTelegramBot(String botUsername, String botToken, BotCommandChain botCommandChain, SessionProvider sessionProvider) { - this.botUsername = botUsername; - this.botToken = botToken; - this.commandChain = botCommandChain; - this.sessionProvider = sessionProvider; + TelegramBotsApi telegramBotsApi; + try { + telegramBotsApi = new TelegramBotsApi(DefaultBotSession.class); + telegramBotsApi.registerBot(this); + } catch (TelegramApiException e) { + log.error("Error", e); + } + } - TelegramBotsApi telegramBotsApi; - try { - telegramBotsApi = new TelegramBotsApi(DefaultBotSession.class); - telegramBotsApi.registerBot(this); - } catch (TelegramApiException e) { - log.error("Error", e); - } - } + @Override + public void onUpdateReceived(Update update) { + try { + commandChain.processCommand( + new BotRequestImpl(update), + new BotResponseImpl(update, this)); + } catch (Exception e) { + log.error("Bot Error:", e); + } + } - @Override - public void onUpdateReceived(Update update) { - try { - commandChain.processCommand( - new BotRequestImpl(update, sessionProvider.get(Utils.getChatId(update))), - new BotResponseImpl(update, this)); - } catch (Exception e) { - log.error("Bot Error:", e); - } - } + @Override + public String getBotUsername() { + return botUsername; + } - @Override - public String getBotUsername() { - return botUsername; - } - - @Override - public String getBotToken() { - return botToken; - } + @Override + public String getBotToken() { + return botToken; + } }