Мене звати Владимир. Мені 43 роки. І якщо тобі, читачу за 40, то так, після 40 можна стати програмістом, якщо тобі це подобається. Моя робота взагалі ніяк не пов'язана з програмуванням, я керівник проекту в галузі автоматизації та всього такого. Але планую змінити рід діяльності. Ох вже ці нові віяння... змінювати сферу діяльності раз на 5-7 років. Так ось : Проект вийшов не маленький так що деякі моменти доведеться опускати або говорити про них коротко, сподіваючись, що читач вміє гуглити. На просторах інтернетів повно публікацій telegram-ботів, що працюють за принципом Long polling. І дуже мало, що працюють за принципом Webhook. Що це таке? Long polling- означає ваш додаток сам опитуватиме сервер telegram на предмет наявності повідомлень з певною періодичністю, повільно. Webhook - означає сервер telegram миттєво перенаправлятиме повідомлення на вказаний вами сервер. У нашому випадку, люб'язно наданий сервіс Heroku. Докладніше про все це і взагалі про бот можна звичайно ж почитати на сайті Telegram - https://tlgrm.ru/docs/bots/api Інтерфейс бот виглядає ось так: Цей додаток я розглядаю саме як навчальний проект з тієї причини, що при написанні цього бота я засвоїв більше інформації, ніж при навчанні. Бажаєте навчитися програмувати? Почніть писати код! Але! Тут не буде докладних інструкцій, як залити додаток на github, як створити базу даних. Цього всього повно в інтернетах і розписано докладним чином, крім того, це і так вийде дуже довге читання. Додаток буде працювати наступним чином: Вводьте опис події, вводите дату та час події, вибираєте періодичність (можна на один раз, можна нагадування щодня у певний час, можна раз на місяць у певний час, або раз на рік). Варіації сповіщень можна допилювати нескінченно, у самих ідей багато. Далі введені дані зберігаються до бази даних (теж розгорнуто безкоштовно на heroku, безкоштовними є 10 000 рядків) Далі один раз на початку дня о 0:00 за серверним часом Spring витягує з бази всі події за критеріями, які повинні спрацювати в цей день і направляє їх на виконання у встановлений час. УВАГА!!! ЦЯ ЧАСТИНА ПРОГРАМИ ЕКСПЕРЕМЕНТАЛЬНА! ІСНУЄ РЕАЛІЗАЦІЯ БІЛЬШ ПРОСТІЙ І ВІРНА! ТАК ЗРОБЛЕНО СПЕЦІАЛЬНО, ЩОБ ПОДИВИТИСЯ ЯК ПРАЦЮЄ КЛАС Time! Доторкнутися своїми руками працюючого бота можна набравши в возі @calendar_event_bot але не розраховуйте на нього, бо я з нього все ще знущаюся. код - ТАК ЗРОБЛЕНО СПЕЦІАЛЬНО, ЩОБ ПОДИВИТИСЯ ЯК ПРАЦЮЄ КЛАС Time! Доторкнутися своїми руками працюючого бота можна набравши в возі @calendar_event_bot але не розраховуйте на нього, бо я з нього все ще знущаюся. код - ТАК ЗРОБЛЕНО СПЕЦІАЛЬНО, ЩОБ ПОДИВИТИСЯ ЯК ПРАЦЮЄ КЛАС Time! Доторкнутися своїми руками працюючого бота можна набравши в возі @calendar_event_bot але не розраховуйте на нього, бо я з нього все ще знущаюся. код -https://github.com/papoff8295/webHookBotForHabr В принципі, щоб запустити власного вам необхідно зробити наступні кроки: • Пройти реєстрацію в @BotFather , це не складно, отримати токен та ім'я • Зробити fork проекту на github • Зареєструватися на Heroku , створити додаток(покроково розбиратимемо), зробити deploy з вашого репозиторію. • Створити на Heroku базу даних • Замінити в репозиторії відповідні поля на свої (токен, назва таблиць по суті, webHookPath, user name, пароль та шлях до бази, це все буде розбирати) • Змусити heroku працювати 24/7 за допомогою https: / /uptimerobot.com/ Підсумкова структура проекту у мене вийшла така: Почнемо зі створення проекту https://start.spring.ioВибираємо потрібні нам залежності як показано на малюнку: Вибираємо власну назву для проекту та натискаємо Generate . Далі вам запропонують зберегти проект на диск. Залишається відкрити файл pom.xm l зі свого середовища розробки. Перед Вами готовий проект. Тепер нам лишилося додати нашу основну бібліотеку. Я використовував бібліотеку з https://github.com/rubenlagus/TelegramBots Загалом можна заморочитися і обійтися без неї. Адже весь зміст роботи полягає в конкатенації URL на кшталт такого: https://api.telegram.org/bot1866835969:AAE6gJG6ptUyqhV2XX0MxyUak4QbAGGnz10/setWebhook?url=https://e9c658b548aa.ngro - Сервер telegram. bot1866835969:AAE6gJG6ptUyqhV2XX0MxyUak4QbAGGnz10/ - після слова bot- секретний токен, який отримуєте при реєстрації бота. setWebhook?url=https://e9c658b548aa.ngrok.io – назва методу та його параметри. В даному випадку ми встановлюємо Ваш webhook сервер, на нього будуть надходити всі повідомлення. Загалом, я вирішив, що проект і так не маленький для публікації, а з ручною реалізацією взагалі буде нечитабельним. Отже, кінцевий вид файлу 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 з нашої біліотеки telegram, і нам необхідно реалізувати всього один метод onWebhookUpdateReceived . Він приймає рапарсенний JSON як об'єкт Update і повертає те, що хоче від нас «почути» сервер telegram. Тут у нас зустрічаються інструкції з бібліотеки Lombok . Lombok – робимо життя програміста легшим!! Ну, тобто. нам не треба перевизначати гетери та сеттери, це робить за нас Lombok, а також не треба писати ідентифікатор рівня доступу. Вже й не варто писати, що цим займаються анотації @ Getter, @ Setter, @ FieldDefaults Поле botPath – означає нашу адресау webhook, яку ми отримаємо на Heroku пізніше. ПолеbotUsername – це назва нашого бота, яку ми отримаємо при реєстрації нашого бота в Telegram. Поле botToken це наш токен, який ми отримаємо при реєстрації нашого бота в Telegram. Поле telegramFacade - це наш клас, де відбуватиметься обробка повідомлень, до нього ми повернемося трохи пізніше, нехай він поки буде червоним. Тепер нам час звернутися до @BotFather і отримати заповітний botToken і botUsername. Просто напишіть йому в telegram і він вам все підкаже. Записуємо дані в наш application.properties, повністю зрештою він виглядатиме ось так:
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 або клас обгортку.
@Component
@Getter
@FieldDefaults(level = AccessLevel.PRIVATE)
public class TelegramBotConfig {
@Value("${telegrambot.webHookPath}")
String webHookPath;
@Value("${telegrambot.userName}")
String userName;
@Value("${telegrambot.botToken}")
String botToken;
Тут інструкція @Value ініціалізує відповідні рядки з файлу application.properties, про цю папку Spring знає за замовчуванням. А анотація @Component створює нам Bean під час запуску програми. Займемося тепер файлом конфігурації 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;
}
}
Жодної магії тут немає, при старті Spring створює нам об'єкти 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 сервер відправляє на зареєстровану адресау webhook повідомлення у форматі JSON методом POST, наш контролер їх приймає та передає бібліотеці telegram у вигляді об'єкта Update. Метод get я зробив просто так ) Тепер нам залишилося реалізувати якусь логіку обробки повідомлень та відповіді у класі TelegramFacade , я наведу його короткий код, щоб уже можна було запускати програму і далі йти своїм шляхом або перейти вже у deploy на Heroku, потім буде повна версія:
@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. Для цього нам необхідно завантажити утиліту ngrok. https://ngrok.com/download Ця утиліта є командним рядком, який дає нам тимчасову адресау на 2 години і перенаправляє всі повідомлення на вказаний порт локального сервера. Запускаємо та пишемо у рядку ngrok http 5000 (або можете вказати свій порт): Отримуємо результат: https://23b1a54ccbbd.ngrok.io – це і є наша webhook адресаа. Як ви могли помітити у файлі properties ми написали server.port=5000 при старті сервера tomcat, він буде займати порт 5000, стежте, щоб ці поля були однакові. Також не забуваємо, що адресаа дається на дві години. У командному рядку за цим слідкує поле Session Expires. Коли час скінчиться потрібно буде знову отримати адресау і пройти процедуру його реєстрації на telegram. Тепер беремо рядок . отриманий рядок та натискаємо enter. Повинен вийде такий результат: Все, тепер можна запускати додаток: Перевірте, що Ваш клас з методом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повинні отримати задовільний результат: Тепер ви можете створити базу з таблицями прямо із середовища розробки, а можете скористатися 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, щоб не писати двічі. Пройдемося трохи. Відразу скажу таблиця event_cash нам знадобиться тільки для роботи з Heroku у зв'язку з його специфікою та моїм невгамовним бажанням попрацювати з класом Time , в локальній версії він не знадобиться. У таблицю users ми будемо записувати id облікового запису користувача telegram, його ім'я, якого може і не бути, буде розрахований часовий пояс користувача, для коректної відправки повідомлень, а також стан 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, що це клас є сутністю для бази даних, тобто. при наданні даних з бази вони будуть представлені нам у вигляді об'єкта Event, User і EventCashEntity. @Table - кажемо як називається наша таблиця в базі даних. Для того, щоб назва таблиці не підкреслювалася червоною, нам потрібно в IDEA погодитися із запропонованим варіантом виправлення помилки і натиснути Assign data sources. І вибрати там нашу основу. @id і @GeneratedValue – використовується Spring для створення бази даних, якщо її ще немає. @Column – використовується для позначення назви полів у таблиці, якщо вони збігаються, але правила хорошого коду рекомендують це писати. СтавленняOneToMany - рекомендую витратити час і розібратися що це таке тут https://en.wikibooks.org/wiki/Java_Persistence Я не зможу викласти більш зрозуміло, просто повірте. Скажу лише, що в даному випадку анотація @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>
В даному випадку ми не маємо ніякої обробки даних, ми просто дістаємо дані з таблиць. У нас все готово, щоб привести повний код класу T elegramFacade і почати розбирати логіку.
@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;
Якщо ми всі кодуватимемо в одному класі, то в нас вийде онуча до місяця, у зв'язку з цим ми відносимо логіку роботи з текстовими повідомленнями в клас MessageHandler, логіку роботи з повідомленнями callbackquery в клас СallbackQueryHandler . Настав час розібрати, що таке c allbackquery і які види меню бувають. Для цього наведу ще раз картинку з інтерфейсом робота: Бувають два типи меню. Ті, що закріплені знизу вікна - основне меню, і ті, що закріплені за повідомленням, наприклад, кнопки видалити, редагувати, змінити пояс. При натисканні на кнопку основного меню надсилається однойменне повідомлення, наприклад, при натисканні «Мої нагадування» , буде просто надіслано текст «Мої нагадування». А при встановленні клавіатури callbackquery, для кожної кнопки встановлюється певне значення і буде спрямоване значення без виведення на екран. Далі у нас є поле BotStateCash . Це спеціально створений клас, який стежитиме за станом бота, і увага, це складний елемент, треба напружитися. Було перевищено кількість символів, про що, до речі ніде не нписано )). Так що ось посилання на другу частину
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ