diff --git a/stbf-api/src/main/java/ru/penkrat/stbf/api/BotRequest.java b/stbf-api/src/main/java/ru/penkrat/stbf/api/BotRequest.java index 05b9a9b..cd34aeb 100644 --- a/stbf-api/src/main/java/ru/penkrat/stbf/api/BotRequest.java +++ b/stbf-api/src/main/java/ru/penkrat/stbf/api/BotRequest.java @@ -14,4 +14,5 @@ public interface BotRequest { Long getChatId(); + BotSession getSession(); } diff --git a/stbf-api/src/main/java/ru/penkrat/stbf/api/BotSession.java b/stbf-api/src/main/java/ru/penkrat/stbf/api/BotSession.java new file mode 100644 index 0000000..8138782 --- /dev/null +++ b/stbf-api/src/main/java/ru/penkrat/stbf/api/BotSession.java @@ -0,0 +1,9 @@ +package ru.penkrat.stbf.api; + +public interface BotSession { + + Object getAttribute(String name); + + void setAttribute(String name, Object value); + +} diff --git a/stbf-api/src/main/java/ru/penkrat/stbf/api/SessionProvider.java b/stbf-api/src/main/java/ru/penkrat/stbf/api/SessionProvider.java new file mode 100644 index 0000000..62c2347 --- /dev/null +++ b/stbf-api/src/main/java/ru/penkrat/stbf/api/SessionProvider.java @@ -0,0 +1,8 @@ +package ru.penkrat.stbf.api; + +@FunctionalInterface +public interface SessionProvider { + + BotSession get(Long chatId); + +} diff --git a/stbf-common/src/main/java/ru/penkrat/stbf/common/session/InMemBotSessionProvider.java b/stbf-common/src/main/java/ru/penkrat/stbf/common/session/InMemBotSessionProvider.java new file mode 100644 index 0000000..31a2644 --- /dev/null +++ b/stbf-common/src/main/java/ru/penkrat/stbf/common/session/InMemBotSessionProvider.java @@ -0,0 +1,18 @@ +package ru.penkrat.stbf.common.session; + +import ru.penkrat.stbf.api.BotSession; +import ru.penkrat.stbf.api.SessionProvider; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class InMemBotSessionProvider implements SessionProvider { + + private static final Map MAP = new ConcurrentHashMap<>(); + + @Override + public BotSession get(Long chatId) { + return MAP.computeIfAbsent(chatId, k -> new MapBackedBotSession(new ConcurrentHashMap<>())); + } + +} diff --git a/stbf-common/src/main/java/ru/penkrat/stbf/common/session/MapBackedBotSession.java b/stbf-common/src/main/java/ru/penkrat/stbf/common/session/MapBackedBotSession.java new file mode 100644 index 0000000..4fd9cb1 --- /dev/null +++ b/stbf-common/src/main/java/ru/penkrat/stbf/common/session/MapBackedBotSession.java @@ -0,0 +1,22 @@ +package ru.penkrat.stbf.common.session; + +import lombok.RequiredArgsConstructor; +import ru.penkrat.stbf.api.BotSession; + +import java.util.Map; + +@RequiredArgsConstructor +class MapBackedBotSession implements BotSession { + + private final Map map; + + @Override + public Object getAttribute(String name) { + return map.get(name); + } + + @Override + public void setAttribute(String name, Object value) { + map.put(name, value); + } +} diff --git a/stbf-demo/pom.xml b/stbf-demo/pom.xml index 113fa15..d705b82 100644 --- a/stbf-demo/pom.xml +++ b/stbf-demo/pom.xml @@ -34,6 +34,11 @@ ru.penkrat.stbf ${project.version} + + stbf-common + ru.penkrat.stbf + ${project.version} + info.picocli picocli 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 410b519..29c34c8 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,7 @@ import org.slf4j.LoggerFactory; import picocli.CommandLine; import ru.penkrat.stbf.api.BotCommandChain; import ru.penkrat.stbf.api.CommandChain; +import ru.penkrat.stbf.common.session.InMemBotSessionProvider; import ru.penkrat.stbf.impl.pengrad.PengradTelegramBot; import ru.penkrat.stbf.templates.xml.XmlFlowResolver; @@ -38,7 +39,7 @@ public class App implements Runnable { } private Runnable start(String token, CommandChain chain) { - return () -> botAtomicReference.set(new PengradTelegramBot(token, chain)); + return () -> botAtomicReference.set(new PengradTelegramBot(token, chain, new InMemBotSessionProvider())); } private void onShutdown() { 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 a60df3b..9af397a 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 @@ -1,61 +1,64 @@ package ru.penkrat.stbf.impl.pengrad; -import static lombok.AccessLevel.PROTECTED; - -import java.util.Optional; - import com.pengrad.telegrambot.model.CallbackQuery; import com.pengrad.telegrambot.model.Contact; import com.pengrad.telegrambot.model.Message; import com.pengrad.telegrambot.model.Update; - import lombok.Getter; import lombok.RequiredArgsConstructor; import ru.penkrat.stbf.api.BotRequest; +import ru.penkrat.stbf.api.BotSession; + +import java.util.Optional; + +import static lombok.AccessLevel.PROTECTED; @RequiredArgsConstructor public class BotRequestImpl implements BotRequest { - @Getter(PROTECTED) - private final Update update; + @Getter(PROTECTED) + private final Update update; - @Override - public Optional getMessageText() { - if (update.message() != null) { - return Optional.ofNullable(update.message().text()); - } - return Optional.empty(); - } + @Getter + private final BotSession session; - @Override - public Optional getPhoneNumber() { - return Optional.of(update) - .map(Update::message) - .map(Message::contact) - .map(Contact::phoneNumber); - } + @Override + public Optional getMessageText() { + if (update.message() != null) { + return Optional.ofNullable(update.message().text()); + } + return Optional.empty(); + } - @Override - public Optional getCallbackData() { - return Optional.of(update) - .map(Update::callbackQuery) - .map(CallbackQuery::data); - } + @Override + public Optional getPhoneNumber() { + return Optional.of(update) + .map(Update::message) + .map(Message::contact) + .map(Contact::phoneNumber); + } - @Override - public Optional getCallbackMessageText() { - return Optional.of(update) - .map(Update::callbackQuery) - .map(CallbackQuery::message) - .map(Message::text); - } + @Override + public Optional getCallbackData() { + return Optional.of(update) + .map(Update::callbackQuery) + .map(CallbackQuery::data); + } - @Override - public Long getChatId() { - return Optional.of(update) - .map(Update::callbackQuery) - .map(CallbackQuery::message) - .orElseGet(() -> update.message()).chat().id(); - } + @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(); + } } 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 3da7fec..151e6f7 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 @@ -2,34 +2,48 @@ package ru.penkrat.stbf.impl.pengrad; import com.pengrad.telegrambot.TelegramBot; import com.pengrad.telegrambot.UpdatesListener; +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) { - super(botToken); - this.setUpdatesListener(updates -> { - for (Update update : updates) { - try { - commandChain.processCommand( - new BotRequestImpl(update), - new BotResponseImpl(update, this)); - } catch (Exception e) { - log.error("Bot Error:", e); - } - } - return UpdatesListener.CONFIRMED_UPDATES_ALL; - }); - } + public PengradTelegramBot(String botToken, CommandChain commandChain) { + this(botToken, commandChain, (id) -> { + throw new IllegalArgumentException("'SessionProvider' is not defined"); + }); + } - @Override - public void close() throws Exception { - removeGetUpdatesListener(); - log.debug("Bot closed."); - } + 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(); + + 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."); + } } 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 2cbc2b0..3d5ab6a 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 @@ -1,58 +1,61 @@ package ru.penkrat.stbf.impl.rubenlagus; -import static lombok.AccessLevel.PROTECTED; - -import java.util.Optional; - +import lombok.Getter; +import lombok.RequiredArgsConstructor; import org.telegram.telegrambots.meta.api.objects.CallbackQuery; import org.telegram.telegrambots.meta.api.objects.Contact; import org.telegram.telegrambots.meta.api.objects.Message; import org.telegram.telegrambots.meta.api.objects.Update; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; import ru.penkrat.stbf.api.BotRequest; +import ru.penkrat.stbf.api.BotSession; + +import java.util.Optional; + +import static lombok.AccessLevel.PROTECTED; @RequiredArgsConstructor class BotRequestImpl implements BotRequest { - @Getter(PROTECTED) - private final Update update; + @Getter(PROTECTED) + private final Update update; - @Override - public Optional getMessageText() { - if (update.hasMessage()) { - return Optional.ofNullable(update.getMessage().getText()); - } - return Optional.empty(); - } + @Getter + private final BotSession session; - @Override - public Optional getPhoneNumber() { - return Optional.of(update) - .map(Update::getMessage) - .map(Message::getContact) - .map(Contact::getPhoneNumber); - } + @Override + public Optional getMessageText() { + if (update.hasMessage()) { + return Optional.ofNullable(update.getMessage().getText()); + } + return Optional.empty(); + } - @Override - public Optional getCallbackData() { - return Optional.of(update) - .map(Update::getCallbackQuery) - .map(CallbackQuery::getData); - } + @Override + public Optional getPhoneNumber() { + return Optional.of(update) + .map(Update::getMessage) + .map(Message::getContact) + .map(Contact::getPhoneNumber); + } - @Override - public Optional getCallbackMessageText() { - return Optional.of(update) - .map(Update::getCallbackQuery) - .map(CallbackQuery::getMessage) - .map(Message::getText); - } + @Override + public Optional getCallbackData() { + return Optional.of(update) + .map(Update::getCallbackQuery) + .map(CallbackQuery::getData); + } - @Override - public Long getChatId() { - return update.getMessage().getChatId(); - } + @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); + } } 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 4e86c75..90cc35e 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 @@ -1,54 +1,62 @@ package ru.penkrat.stbf.impl.rubenlagus; +import lombok.extern.slf4j.Slf4j; import org.telegram.telegrambots.bots.TelegramLongPollingBot; import org.telegram.telegrambots.meta.TelegramBotsApi; import org.telegram.telegrambots.meta.api.objects.Update; import org.telegram.telegrambots.meta.exceptions.TelegramApiException; import org.telegram.telegrambots.updatesreceivers.DefaultBotSession; - -import lombok.extern.slf4j.Slf4j; 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 String botUsername; + private final String botToken; + private final BotCommandChain commandChain; + private final SessionProvider sessionProvider; - 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) { + this(botUsername, botToken, botCommandChain, (id) -> { + throw new IllegalArgumentException("'SessionProvider' is not defined"); + }); + } - TelegramBotsApi telegramBotsApi; - try { - telegramBotsApi = new TelegramBotsApi(DefaultBotSession.class); - telegramBotsApi.registerBot(this); - } catch (TelegramApiException e) { - log.error("Error", e); - } - } + public RubenlagusTelegramBot(String botUsername, String botToken, BotCommandChain botCommandChain, SessionProvider sessionProvider) { + this.botUsername = botUsername; + this.botToken = botToken; + this.commandChain = botCommandChain; + this.sessionProvider = sessionProvider; - @Override - public void onUpdateReceived(Update update) { - try { - commandChain.processCommand( - new BotRequestImpl(update), - new BotResponseImpl(update, this)); - } catch (Exception e) { - log.error("Bot Error:", e); - } - } + TelegramBotsApi telegramBotsApi; + try { + telegramBotsApi = new TelegramBotsApi(DefaultBotSession.class); + telegramBotsApi.registerBot(this); + } catch (TelegramApiException e) { + log.error("Error", e); + } + } - @Override - public String getBotUsername() { - return botUsername; - } + @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 getBotToken() { - return botToken; - } + @Override + public String getBotUsername() { + return botUsername; + } + + @Override + public String getBotToken() { + return botToken; + } } diff --git a/stbf-rubenlagus/src/main/java/ru/penkrat/stbf/impl/rubenlagus/Utils.java b/stbf-rubenlagus/src/main/java/ru/penkrat/stbf/impl/rubenlagus/Utils.java new file mode 100644 index 0000000..ae32bda --- /dev/null +++ b/stbf-rubenlagus/src/main/java/ru/penkrat/stbf/impl/rubenlagus/Utils.java @@ -0,0 +1,21 @@ +package ru.penkrat.stbf.impl.rubenlagus; + +import lombok.experimental.UtilityClass; +import org.telegram.telegrambots.meta.api.objects.Update; + +@UtilityClass +class Utils { + + Long getChatId(Update update) { + if (update.getMessage() != null) { + return update.getMessage().getChatId(); + } + + if (update.getCallbackQuery() != null) { + update.getCallbackQuery().getMessage().getChatId(); + } + + throw new IllegalStateException("Can't extract chatId from 'Update'"); + } + +} diff --git a/stbf-test/src/main/java/ru/penkrat/stbf/test/BotRequestImpl.java b/stbf-test/src/main/java/ru/penkrat/stbf/test/BotRequestImpl.java index 10ced29..3165064 100644 --- a/stbf-test/src/main/java/ru/penkrat/stbf/test/BotRequestImpl.java +++ b/stbf-test/src/main/java/ru/penkrat/stbf/test/BotRequestImpl.java @@ -6,6 +6,7 @@ import java.util.Random; import lombok.Builder; import lombok.Getter; import ru.penkrat.stbf.api.BotRequest; +import ru.penkrat.stbf.api.BotSession; @Builder class BotRequestImpl implements BotRequest { @@ -14,6 +15,7 @@ class BotRequestImpl implements BotRequest { private String phoneNumber; private String callbackData; private String callbackMessageText; + private BotSession session; @Getter @Builder.Default @@ -39,4 +41,9 @@ class BotRequestImpl implements BotRequest { return Optional.ofNullable(callbackMessageText); } + @Override + public BotSession getSession() { + return session; + } + }