JavaRush /Java Blog /Random-TK /Telegram bot — напоминалка через webHook на Java или скаж...

Telegram bot — напоминалка через webHook на Java или скажи нет Google-календарю! Часть 1

Toparda çap edildi
Меня зовут Владимир. Мне 43 года. И если тебе, читатель за 40, то да, после 40 можно стать програмистом, если тебе это нравится. Моя работа вообще ниHow не связана с программированием, я руководитель проекта в области автоматизации и всего такого. Но планирую сменить род деятельности. Ох уж эти новые веяния.. менять сферу деятельности раз в 5-7 лет. Так вот: Проект получился не маленький так что некоторые моменты придётся опускать or говорить о них кратко, в надежде, что читатель умеет гуглить. На просторах интернетов полно публикаций telegram-ботов, работающих по принипу Long polling. И очень не много, работающих по принципу Webhook. What это такое? Long polling - значит ваше приложение само будет опрашивать server telegram на предмет наличия сообщений с определенной периодичностью, медленно. Webhook - значит server telegram моментально будет перенаправлять messages на указанный вами server. В нашем случае, любезно предоставленный service Heroku. Подробнее про все это и вообще про бота можно конечно же почитать на сайте Telegram - https://tlgrm.ru/docs/bots/api Интерфейс бота выглядит вот так: Telegram bot — напоминалка через webHook на Java or скажи нет Google-календарю! - 1 Данное приложение я рассматриваю именно How обучающий проект по той причине, что при написании этого бота я усвоил больше информации чем при обучении. Хотите научиться программировать? Начните писать code!!! Но! Тут не будет подробных инструкций How залить приложение на github, How создать базу данных. Этого всего полно в интернетах и расписано подробнейшим образом, кроме того это и так получится очень длинное чтение. Приложение будет работать следующим образом: Вводите описание события, вводите date и время события, выбираете периодичность (можно на один раз, можно напоминание каждый день в определенное время, можно раз в месяц в определенное время, or раз в год). Вариации оповещений можно допorвать бесконечно, у самого идей много. Далее введенные данные сохраняются в базу данных(тоже развернуто бесплатно на heroku, бесплатными являются 10 000 строк) Дальше один раз в начале дня в 0:00 по serverному времени Spring извлекает из базы все события по критериям, которые должны сработать в этот день и направляет их на исполнение в установленное время. ВНИМАНИЕ!!! ЭТА ЧАСТЬ ПРОГРАММЫ ЭКСПЕРЕМЕНТАЛЬНА! СУЩЕСТВУЕТ РЕАЛИЗАЦИЯЫ БОЛЕЕ ПРОСТОЯ И ВЕРНАЯ! ТАК СДЕЛАНО СПЕЦИАЛЬНО, ЧТОБЫ ПОСМОТРЕТЬ КАК РАБОТАЕТ КЛАСС Time! Потрогать своими руками работающего бота можно набрав в телеге @calendar_event_bot но не рассчитывайте на него, ибо я над ним все еще издеваюсь. code - https://github.com/papoff8295/webHookBotForHabr В принципе, чтобы запустить собственного вам необходимо сделать следующие шаги: • Пройти регистрацию в @BotFather, это не сложно, получить токен и Name • Сделать fork проекта на github • Зарегестрироваться на Heroku, создать приложение(пошагово будем разбирать), сделать deploy из вашего репозитория. • Создать на Heroku базу данных • Заменить в репозитории соответсвующие поля на свои(токен, название таблиц в сущностях, webHookPath, user name, пароль и way to базе, это все будет разбирать) • Заставить heroku работать 24/7 с помощью https://uptimerobot.com/ Totalвая структура проекта у меня получилась такая: Telegram bot — напоминалка через webHook на Java or скажи нет Google-календарю! - 2 Начнем с создания проекта в https://start.spring.io Выбираем нужные нам зависимости How показано на рисунке: Telegram bot — напоминалка через webHook на Java or скажи нет Google-календарю! - 3Выбираем собственное название для проекта и нажимаем Generate. Дальше вам предложат сохранить проект себе на диск. Остается открыть файл pom.xml из своей среды разработки. Перед Вами готовый проект. Теперь нам осталось добавить нашу основную библиотеку. Я использовал библиотеку с https://github.com/rubenlagus/TelegramBots В общем-то можно заморочиться и обойтись без нее. Ведь весь смысл работы заключается в конкатенации URL вроде такого: https://api.telegram.org/bot1866835969:AAE6gJG6ptUyqhV2XX0MxyUak4QbAGGnz10/setWebhook?url=https://e9c658b548aa.ngrok.io Немного разберем: https://api.telegram.org – server telegram. bot1866835969:AAE6gJG6ptUyqhV2XX0MxyUak4QbAGGnz10/ - после слова bot- секретный токен, который получаете при регистрации бота. setWebhook?url=https://e9c658b548aa.ngrok.io – название метода и его параметры. В данном случае мы устанавливаем Ваш webhook server, на него будут приходить все messages. В общем я решил, что проект и так не маленький для публикации, а с ручной реализацией вообще будет нечитабельно. Итак, конечный вид file pom:

<!--?xml version="1.0" encoding="UTF-8"?-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelversion>4.0.0</modelversion>
   <parent>
      <groupid>org.springframework.boot</groupid>
      <artifactid>spring-boot-starter-parent</artifactid>
      <version>2.5.0</version>
      <relativepath> <!-- lookup parent from repository -->
   </relativepath></parent>
   <groupid>ru.popov</groupid>
   <artifactid>telegrambot</artifactid>
   <version>0.0.1-SNAPSHOT</version>
   <name>telegrambot</name>
   <description>Demo project for Spring Boot</description>
   <properties>
      <java.version>1.8</java.version>
   </properties>
   <dependencies>
      <dependency>
         <groupid>org.springframework.boot</groupid>
         <artifactid>spring-boot-starter-web</artifactid>
      </dependency>

      <dependency>
         <groupid>org.springframework.boot</groupid>
         <artifactid>spring-boot-starter-data-jpa</artifactid>
      </dependency>

      <dependency>
         <groupid>org.springframework.boot</groupid>
         <artifactid>spring-boot-starter-test</artifactid>
         <scope>test</scope>
      </dependency>

      <!-- https://mvnrepository.com/artifact/org.telegram/telegrambots-spring-boot-starter -->
      <dependency>
         <groupid>org.telegram</groupid>
         <artifactid>telegrambots-spring-boot-starter</artifactid>
         <version>5.2.0</version>
      </dependency>

      <dependency>
         <groupid>org.projectlombok</groupid>
         <artifactid>lombok</artifactid>
         <version>1.18.16</version>
      </dependency>

      <dependency>
         <groupid>org.postgresql</groupid>
         <artifactid>postgresql</artifactid>
         <scope>runtime</scope>
      </dependency>

   </dependencies>

   <build>
      <plugins>
         <plugin>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-maven-plugin</artifactid>
         </plugin>
      </plugins>
   </build>

</project>
Все готово для написания нашего бота. Создадим класс TelegramBot. Название папок я писать не буду, можете посмотреть в структуре проекта выше.

@Getter
@Setter
@FieldDefaults(level = AccessLevel.PRIVATE)
public class TelegramBot extends SpringWebhookBot {
    String botPath;
    String botUsername;
    String botToken;

    private TelegramFacade telegramFacade;

    public TelegramBot(TelegramFacade telegramFacade, DefaultBotOptions options, SetWebhook setWebhook) {
        super(options, setWebhook);
        this.telegramFacade = telegramFacade;
    }
    public TelegramBot(TelegramFacade telegramFacade, SetWebhook setWebhook) {
        super(setWebhook);
        this.telegramFacade = telegramFacade;
    }

    @Override
    public BotApiMethod<!--?--> onWebhookUpdateReceived(Update update) {
        return telegramFacade.handleUpdate(update);
    }
}
Класс расширяется от SpringWebhookBot из нашей бorотеки telegram, и нам необходимо реализовать всего один метод onWebhookUpdateReceived. Он принимает рапарсенный JSON в виде an object Update и возвращает, то, что хочет от нас «услышать» server telegram. Тут у нас встречаются аннотации из библиотеки Lombok. Lombok – делаем жизнь программиста легче!! Ну т.е. нам не надо переопределять геттеры и сеттеры, это делает за нас Lombok, а также не надо писать идентификатор уровня доступа. Уже и не стоит писать, что этим занимаются аннотации @Getter, @Setter, @FieldDefaults Поле botPath – означает наш address webhook, который мы получим на Heroku позднее. Поле botUsername – означает название нашего бота, которое мы получим при регистрации нашего бота в Telegram. Поле botToken – это наш токен, который мы получим при регистрации нашего бота в Telegram. Поле telegramFacade – это наш класс, где будет происходить обработка сообщений, к нему мы вернемся немного позднее, пусть он пока будет красненьким. Теперь нам пора обратиться к @BotFather и получить заветный botToken и botUsername. Telegram bot — напоминалка через webHook на Java or скажи нет Google-календарю! - 4Просто напишите ему в telegram и он все вам подскажет. Записываем данные в наш application.properties, fully в конечном итоге он будет выглядеть вот так:

server#server.port=5000

telegrambot.userName=@calendar_event_bot
telegrambot.botToken=1731265488:AAFDjUSk3vu5SFfgdfh556gOOFmuml7SqEjwrmnEF5Ak
#telegrambot.webHookPath=https://telegrambotsimpl.herokuapp.com/
telegrambot.webHookPath=https://f5d6beeb7b93.ngrok.io


telegrambot.adminId=39376213

eventservice.period =600000

spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/telegramUsers
spring.datasource.username=postgres
spring.datasource.password=password

#spring.datasource.url=jdbc:postgresql:ec2-54-247-158-179.eu-west-1.compute.amazonaws.com:5432/d2um126le5notq?ssl=true&sslmode=require&sslfactory=org.postgresql.ssl.NonValidatingFactory
#spring.datasource.username=ulmbeywyhvsxa
#spring.datasource.password=4c7646c69dbbgeacb98fa96e8daa6d9b1bl4894e67f3f3ddd6a27fe7b0537fd
Данная конфигурация настроена для работы с локальной базой данных, в последствии мы сделаем необходимые изменения. Замените botToken и username на свои. Не годится использовать данные из application.properties напрямую в приложении. Создадим из этих данных bean or класс обертку.

@Component
@Getter
@FieldDefaults(level = AccessLevel.PRIVATE)

public class TelegramBotConfig {
    @Value("${telegrambot.webHookPath}")
    String webHookPath;
    @Value("${telegrambot.userName}")
    String userName;
    @Value("${telegrambot.botToken}")
    String botToken;
Здесь annotation @Value инициализирует соответствующие строки из file application.properties, об этой папке Spring знает по умолчанию. А annotation @Component создает нам Bean при запуске applications. Займемся теперь файлом конфигурации Spring:

@Configuration
public class AppConfig {
    private final TelegramBotConfig botConfig;

    public AppConfig(TelegramBotConfig botConfig) {
        this.botConfig = botConfig;
    }

    @Bean
    public SetWebhook setWebhookInstance() {
        return SetWebhook.builder().url(botConfig.getWebHookPath()).build();
    }

    @Bean
    public TelegramBot springWebhookBot(SetWebhook setWebhook, TelegramFacade telegramFacade) {
        TelegramBot bot = new TelegramBot(telegramFacade, setWebhook);
        bot.setBotToken(botConfig.getBotToken());
        bot.setBotUsername(botConfig.getUserName());
        bot.setBotPath(botConfig.getWebHookPath());

        return bot;
    }
}
НиHowой магии тут нет, при старте Spring создает нам an objectы SetWebhook и TelegramBot. Создадим теперь точки входа наших сообщений:

@RestController
public class WebhookController {

    private final TelegramBot telegramBot;

    public WebhookController(TelegramBot telegramBot) {
        this.telegramBot = telegramBot;
    }

// point for message
    @PostMapping("/")
    public BotApiMethod<!--?--> onUpdateReceived(@RequestBody Update update) {
        return telegramBot.onWebhookUpdateReceived(update);
    }

    @GetMapping
    public ResponseEntity get() {
        return ResponseEntity.ok().build();
    }
}
Telegram server отправляет на зарегистрированный address webhook messages в формате JSON методом POST, наш контроллер их принимает и передает библиотеке telegram в виде an object Update. Метод get я сделал просто так ) Теперь нам осталось реализовать Howую-то логику обработки сообщений и ответа в классе TelegramFacade, я приведу его краткий code, чтобы уже можно было запускать приложение и дальше идти своим путем or перейти уже у deploy на Heroku, потом будет полная version:

@Component
@FieldDefaults(level = AccessLevel.PRIVATE)
public class TelegramFacade {

    public BotApiMethod<!--?--> handleUpdate(Update update) {

        if (update.hasCallbackQuery()) {
            CallbackQuery callbackQuery = update.getCallbackQuery();
            return null;
        } else {

            Message message = update.getMessage();
            SendMessage sendMessage = new SendMessage();
            sendMessage.setChatId(String.valueOf(message.getChatId()));
            if (message.hasText()) {
                sendMessage.setText("Hello world");
                return sendMessage;
            }
        }
        return null;
    }
    
}
Данный метод будет отвечать на любое сообщение Hello world! Для того, что запустить наше приложение нам осталось сделать так чтобы мы могли тестировать наше приложение прямо из IDEA. Для этого нам необходимо скачать утorту ngrok. https://ngrok.com/download Эта утorта представляет собой командную строку, которая дает нам временный address на 2 часа и перенаправляет все messages на указанный порт локального serverа. Запускаем и пишем в строке ngrok http 5000(or можете указать свой порт): Telegram bot — напоминалка через webHook на Java or скажи нет Google-календарю! - 5Получаем результат: Telegram bot — напоминалка через webHook на Java or скажи нет Google-календарю! - 6https://23b1a54ccbbd.ngrok.io – это и есть наш webhook address. Как вы могли заметить в файле properties мы написали server.port=5000 при старте serverа tomcat, он будет занимать порт 5000, следите чтобы эти поля были одинаковы. Также не забываем, что address дается на два часа. В командной строке за этим следит поле Session Expires. Когда время кончится нужно будет заново получить address и пройти proceduresу его регистрации на telegram. Теперь берем строку https://api.telegram.org/bot1866835969:AAE6gJG6ptUyqhV2XX0MxyUak4QbAGGnz10/setWebhook?url=https://e9c658b548aa.ngrok.io И ловкими движениями рук заменяем токен на свой, url на свой, вставляем в браузер полученную строку и нажимаем enter. Должен получится вот такой результат: Telegram bot — напоминалка через webHook на Java or скажи нет Google-календарю! - 7Все, теперь можно запускать приложение: Telegram bot — напоминалка через webHook на Java or скажи нет Google-календарю! - 8Проверьте, что Ваш класс с методом main, был такой:

@SpringBootApplication
public class TelegramBotApplication {

   public static void main(String[] args) {
      SpringApplication.run(TelegramBotApplication.class, args);
   }
}
Если Вы все сделали правильно, то теперь Ваш бот будет откликаться на любое сообщение фразой – «Hello world». Дальше можете идти своим путем. Если же Вы со мной и Вам интересно пойти все шаги, то приступим к написанию сущностей для базы данных и создадим саму базу данных. Начнем с базы: Как я уже говорил, полагаю, что Вы уже имеете минимальные навыки работы с базой, и у вас установлена локальная база postgreSQL, если нет проделайте этот путь. https://www.postgresql.org/download/ В файле application.properties замените логин и пароль к базе данных на свои. В IDEA справа есть вкладка database, в ней нужно нажать на +/Data source/PostgreSQL. В итоге при нажатии на Test Connection должны получить удовлетворительный результат: Telegram bot — напоминалка через webHook на Java or скажи нет Google-календарю! - 9Теперь вы можете создать базу с tableми прямо из среды разработки, а можете воспользоваться web- интерфейсом pgadmin, который находится в меню пуск. Нам понадобятся 3 таблицы:

CREATE TABLE users
(
    id               INTEGER PRIMARY KEY UNIQUE NOT NULL,
    name             VARCHAR,
    time_zone        INTEGER DEFAULT 0,
    on_off           BOOLEAN DEFAULT true
);

CREATE TABLE user_events
(
    user_id INTEGER ,
    time timestamp ,
    description varchar ,
    event_id serial,
    event_freq varchar default 'TIME',
    FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
);

CREATE TABLE event_cash
(
    time timestamp ,
    description varchar ,
    user_id INTEGER ,
    id serial
);
Рекомендую создать отдельно этот скрипт, он нам пригодится для создания базы на Heroku, чтобы не писать два раза. Пройдемся немного. Сразу скажу table event_cash нам понадобиться только для работы с Heroku в виду его специфики и моего неуемного желания поработать с классом Time, в локальной версии он не пригодится. В таблицу users мы будем записывать id аккаунта пользователя telegram, его Name, которого может и не быть, будет рассчитан часовой пояс пользователя, для корректной отправки уведомлений, а также состояние on/off рассылки уведомлений. В таблицу user_events у нас будет записываться id пользователя, время уведомления, описание, будет автоматически генерироваться id для события, и устанавливаться частота уведомлений. В таблице event_cash будет записываться уведомление перед отправкой, и, если оно отправлено, то удаляться из таблицы. Таблицы готовы, давайте теперь добавим сущности.

@Entity
@Table(name = "user_events")
@Getter
@Setter
public class Event {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column( name = "event_id", columnDefinition = "serial")
    private int eventId;

    @Column(name = "time")
    @NotNull(message = "Need date!")
    private Date date;

    @Column(name = "description")
    @Size(min = 4, max = 200, message = "Description must be between 0 and 200 chars!")
    private String description;

    @Column(name = "event_freq", columnDefinition = "TIME")
    @Enumerated(EnumType.STRING)
    private EventFreq freq;

    @ManyToOne(fetch=FetchType.EAGER)
    @JoinColumn(name="user_id")
    @OnDelete(action = OnDeleteAction.CASCADE)
    private User user;

    public Event() {
    }

    public Event(int eventId,
                 @NotNull(message = "Need date!") Date date,
                 @Size(min = 4, max = 200, message = "Description must be between 0 and 200 chars!")
                         String description,
                 EventFreq freq, User user) {
        this.eventId = eventId;
        this.date = date;
        this.description = description;
        this.freq = freq;
        this.user = user;
    }
}

@Entity
@Table(name = "users")
@Getter
@Setter
public class User {

    @Id
    @Column(name = "id")
    private long id;

    @Column(name = "name")
    private String name;

    @Column(name = "time_zone", columnDefinition = "default 0")
    //sets the broadcast time of events for your time zone
    private int timeZone;

    @OneToMany(mappedBy="user")
    private List<event> events;

    @Column(name = "on_off")
    // on/off send event
    private boolean on;

    public User() {
    }
}

</event>

@Entity
@Table(name = "event_cash")
@Getter
@Setter
//serves to save unhandled events after rebooting heroku
public class EventCashEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column( name = "id", columnDefinition = "serial")
    private long id;

    @Column(name = "time")
    private Date date;

    @Column(name = "description")
    private String description;

    @Column(name = "user_id")
    private long userId;

    public EventCashEntity() {
    }

    public static EventCashEntity eventTo(Date date, String description, long userId) {
        EventCashEntity eventCashEntity = new EventCashEntity();
        eventCashEntity.setDate(date);
        eventCashEntity.setDescription(description);
        eventCashEntity.setUserId(userId);
        return eventCashEntity;
    }
}
Немного пробежимся по основным моментам. @Entity – помечает класс для нашего dada jpa, что этот класс является сущностью для базы данных, т.е. при доставании данных из базы они будут представлены нам в виде an object Event, User и EventCashEntity. @Table – говорим How называется наша table в базе данных. Для того, чтобы название таблицы не подчеркивалось красным нам нужно в IDEA согласиться с предложенным вариантом исправления ошибки и нажать Assign data sources. И выбрать там нашу базу. @id и @GeneratedValue – используется Spring для создания базы данных если ее еще нет. @Column – используется для обозначения названия полей в таблице, если они не совпадают, но правила хорошего codeа рекомендуют всегда это писать. Отношение OneToMany - рекомендую потратить время и разобраться что это такое вот тут https://en.wikibooks.org/wiki/Java_Persistence Я не смогу изложить более понятно, просто поверьте. Скажу лишь, что в данном случае annotation @OneToMany говорит что у одного пользователя может быть много событий, и они будут предоставлены нам в виде списка. Теперь нам надо доставать данные из таблиц. В библиотеке SRING DATA JPA уже все за нас написано, нам надо просто создать интерфейс для каждой таблицы и расширить его от JpaRepository.

public interface EventRepository extends JpaRepository<event, long=""> {
    Event findByEventId(long id);
}
public interface UserRepository extends JpaRepository<user, long=""> {

    User findById(long id);
}
public interface EventCashRepository extends JpaRepository<eventcashentity, long=""> {
    EventCashEntity findById(long id);
}

</eventcashentity,></user,></event,>
Основная логика работы с базой данных вынесена в сервис, так называемый Data Access Object(DAO):

@Service
public class UserDAO {

    private final UserRepository userRepository;

    @Autowired
    public UserDAO(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User findByUserId(long id) {
        return userRepository.findById(id);
    }

    public List<user> findAllUsers() {
        return userRepository.findAll();
    }

    public void removeUser(User user) {
        userRepository.delete(user);
    }


    public void save(User user) {
        userRepository.save(user);
    }

    public boolean isExist(long id) {
        User user = findByUserId(id);
        return user != null;
    }
}
@Service
public class EventDAO {

    private final UserRepository userRepository;
    private final EventRepository eventRepository;

    @Autowired
    public EventDAO(UserRepository userRepository, EventRepository eventRepository) {
        this.userRepository = userRepository;
        this.eventRepository = eventRepository;
    }

    public List<event> findByUserId(long userId) {
        User user = userRepository.findById(userId);
        return user.getEvents();
    }
    public List<event> findAllEvent() {
       return eventRepository.findAll();
    }

    public Event findByEventId(long eventId) {
        return eventRepository.findByEventId(eventId);
    }

    public void remove(Event event) {
        eventRepository.delete(event);
    }

    public void save(Event event) {
        eventRepository.save(event);
    }
}

</event></event></user>

@Service
//handles events not dispatched after reboot heroku
public class EventCashDAO {

    private EventCashRepository eventCashRepository;

    @Autowired
    public void setEventCashRepository(EventCashRepository eventCashRepository) {
        this.eventCashRepository = eventCashRepository;
    }

    public List<eventcashentity> findAllEventCash() {
        return eventCashRepository.findAll();
    }

    public void save(EventCashEntity eventCashEntity) {
        eventCashRepository.save(eventCashEntity);
    }

    public void delete(long id) {
        eventCashRepository.deleteById(id);
    }
}

</eventcashentity>
В данном случае у нас нет ниHowой обработки данных, мы просто достаем данные из таблиц. У нас все готово, для того, чтоб привести полный code класса TelegramFacade и начать разбирать логику.

@Component
@FieldDefaults(level = AccessLevel.PRIVATE)
public class TelegramFacade {

    final MessageHandler messageHandler;
    final CallbackQueryHandler callbackQueryHandler;
    final BotStateCash botStateCash;

    @Value("${telegrambot.adminId}")
    int adminId;


    public TelegramFacade(MessageHandler messageHandler, CallbackQueryHandler callbackQueryHandler, BotStateCash botStateCash) {
        this.messageHandler = messageHandler;
        this.callbackQueryHandler = callbackQueryHandler;
        this.botStateCash = botStateCash;
    }

    public BotApiMethod<!--?--> handleUpdate(Update update) {

        if (update.hasCallbackQuery()) {
            CallbackQuery callbackQuery = update.getCallbackQuery();
            return callbackQueryHandler.processCallbackQuery(callbackQuery);
        } else {

            Message message = update.getMessage();
            if (message != null && message.hasText()) {
                return handleInputMessage(message);
            }
        }
        return null;
    }

    private BotApiMethod<!--?--> handleInputMessage(Message message) {
        BotState botState;
        String inputMsg = message.getText();
        //we process messages of the main menu and any other messages
        //set state
        switch (inputMsg) {
            case "/start":
                botState = BotState.START;
                break;
            case "Мои напоминания":
                botState = BotState.MYEVENTS;
                break;
            case "Создать напоминание":
                botState = BotState.CREATE;
                break;
            case "Отключить напоминания":
            case "Включить напоминания":
                botState = BotState.ONEVENT;
                break;
            case "All users":
                if (message.getFrom().getId() == adminId)
                botState = BotState.ALLUSERS;
                else botState = BotState.START;
                break;
            case "All events":
                if (message.getFrom().getId() == adminId)
                botState = BotState.ALLEVENTS;
                else botState = BotState.START;
                break;
            default:
                botState = botStateCash.getBotStateMap().get(message.getFrom().getId()) == null?
                        BotState.START: botStateCash.getBotStateMap().get(message.getFrom().getId());
        }
        //we pass the corresponding state to the handler
        //the corresponding method will be called
        return messageHandler.handle(message, botState);

    }
}
Разберем для чего нужны поля

final MessageHandler messageHandler;
    final CallbackQueryHandler callbackQueryHandler;
    final BotStateCash botStateCash;
Если мы все будем codeить в одном классе, то у нас получится портянка до луны, в связи с этим мы относим логику работы с текстовыми messagesми в класс MessageHandler, логику работы с messagesми callbackquery в класс СallbackQueryHandler. Настало время разобрать что такое callbackquery и Howие виды меню бывают. Для этого приведу еще раз картинку с интерфейсом бота: Telegram bot — напоминалка через webHook на Java or скажи нет Google-календарю! - 10Бывают два типа меню. Те, которые закреплены снизу окна - основное меню, и те, которые закреплены за сообщением, например, кнопки удалить, редактировать, изменить пояс. При нажатии на кнопку основного меню отправляется одноименное сообщение, например при нажатии «Мои напоминания», будет просто отправлен текст «Мои напоминания». А при установке клавиатуры callbackquery, для каждой кнопки устанавливается определенное meaning и будет направлено его meaning без вывода на экран. Дальше у нас есть поле BotStateCash. Это специально созданный класс, который будет следить за состоянием бота, и внимание, это сложный элемент, надо напрячься. Было превышено кол-во символов, о чем, кстати нигде не нписано )). Так что вот link на вторую часть
Teswirler
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION