JavaRush /Java блогу /Random-KY /Spring Boot Pt.2 аркылуу телеграмма ботун түзүү: Quiz Bot...
Whiskels
Деңгээл
Москва

Spring Boot Pt.2 аркылуу телеграмма ботун түзүү: Quiz Bot

Группада жарыяланган
1-БӨЛҮМ Аллох! Мурунку макалада биз кандайдыр бир иш-чарага бизди тосуп алган жөнөкөй бот жаратканбыз. Биз буга чейин миңдеген code саптарын жазганбыз жана биздин ботко татаалыраак функцияларды кошууга убакыт келди. Бүгүн биз бош убактыбызда Java Core боюнча бorмибизди интервьюга чейин өркүндөтүү үчүн жөнөкөй бот жазууга аракет кылабыз (таң калыштуусу, мен мындай түрдөгү бир дагы жумушчу бот таба элекмин). Бул үчүн биз төмөнкүлөрдү жасайбыз:
  • тышкы Postgres базасын Heroku менен туташтыруу;
  • Келгиле, маалымат базасын инициализациялоо жана толтуруу үчүн биринчи сценарийлерибизди жазалы;
  • маалымат базасы менен иштөө үчүн Spring Boot Data JPAти туташтыралы;
  • Биз боттун жүрүм-турумунун ар кандай сценарийлерин ишке ашырабыз.
Эгер бул сиз үчүн кызыктуу болсо жана макаланы окуп чыккандан кийин, чириген помидорлорду ыргытууну каалабасаңыз, анда менин репозиторийге жылдызчаны коюңуз , мен ыраазы болом! Эгерде сизде кандайдыр бир суроолор болсо, биз аларды комментарийлерде талкуулайбыз. Акырында мына ушундай болот: Spring Boot Pt.2 аркылуу телеграмма ботун түзүү: Quiz Bot - 1Spring Boot Pt.2 аркылуу телеграмма ботун түзүү: Quiz Bot - 2Spring Boot Pt.2 аркылуу телеграмма ботун түзүү: Quiz Bot - 3<h2>Ошондуктан, кетели!</h2><h3>Heroku боюнча маалымат базасын түзүү</h3>Келгиле, биринчи тышкы маалымат базасын түзүү менен баштайлы. Жергorктүү жумуш үчүн pgAdmin ды текшерүүнү сунуштайм . Бирок мен сиздин жергorктүү машинаңыздан көз каранды болбошу үчүн келечекте ботту Герокуга жайгаштыра турганыңызды жетекчorкке алышыңызды каалайм жана бул үчүн бул кызмат менен таанышалы. Процедура:
  • Heroku боюнча каттоо ;
  • Биздин башкаруу тактасына өтүңүз -> Жаңы -> Жаңы колдонмо түзүү жана жаңы тиркеме түзүү;
  • Биз жаңы түзүлгөн тиркемеге киребиз, бизди көптөгөн баскычтар коркутат, бирок биз "Орнотулган кошумчалар" панелине көңүл бурабыз - анын жанында Кошумчаларды конфигурациялоо баскычы бар, биз аны басыңыз;
  • Издөөгө "Heroku Postgres" киргизиңиз, "Hobby Dev - Free" планын тандаңыз -> Заказ формасын тапшырыңыз;
  • Жаңы алынган маалымат базасын ачыңыз -> Орнотуулар -> Каттоо маалыматтарын көрүңүз. Бул өтмөк маалымат базасына кирүү үчүн биздин ачкычтарды камтыйт. Биз алардын жайгашкан жерин эстейбиз - алар IDEAдагы DataSource сыяктуу маалымат базасын туташтыруу үчүн керек болот.
<h3>Pom.xml'ге көз карандылыктарды кошуңуз</h3>Биздин ботто иштөөнүн бир бөлүгү катары, биз төмөнкү көз карандылыктарды помубузга кошобуз: Lombok, Spring Boot Data JPA, PostgreSQL. Токто! Мунун баары эмне жана аны эмне үчүн кошуп жатабыз?
  • Lombok китепканасы, анын аркасында биз ар кандай codeдун көлөмүн бир топ кыскартабыз. Анын жардамы менен биз автоматтык түрдө конструкторлорду, орнотуучуларды, гетерлерди жана башка көптөгөн нерселерди түзө алабыз.
  • Spring Data JPA маалымат базалары менен иштөө үчүн негиз болуп саналат (бул өтө жөнөкөй угулат да). Spring Data JPA мүмкүнчүлүктөрүнүн сүрөттөлүшү бир катар макалалар болмокчу, жана биз белгилеген көз карандылык Күтүү режимин жана башка көптөгөн нерселерди камтыйт, андыктан келгиле, майда-чүйдөсүнө чейин өткөрүп жиберип, бүгүн Spring JPA аркылуу бир нерсе жазууга аракет кылалы.
  • PostgreSQL - биз маалымат базабыз менен иштей турган драйверди алуу үчүн китепкананы тартабыз.
Биздин pom.xml төмөнкүдөй боло баштайт: касиеттери: Spring Boot Pt.2 аркылуу телеграмма ботун түзүү: Quiz Bot - 1Көз карандылыктар: Spring Boot Pt.2 аркылуу телеграмма ботун түзүү: Quiz Bot - 2Эгер сиз мурунку макаланы окубасаңыз, биз бул жерде жаңы көз карандылыктарды кошуп жатканыбызды эске алыңыз, андыктан бул толук pom.xml түзүмү эмес. Бул көз карандылыктарды биздин долбоорго жүктөөнү унутпаңыз (мисалы, Maven -> Бардык Maven долбоорлорун кайра импорттоо терезесине өтүү менен).<h3>IDEAдагы маалыматтар базасын туташтыруу</h3>Эгер сиз IDEA Community Edition колдонсоңуз, DataSource өтмөгүн төмөнкүдөй иштетиңиз . Плагинди кошкондон кийин, биз DataSource конфигурациялашыбыз керек. Бул үчүн, алгач плагиндин дисплейин иштетиңиз: Көрүү -> Курал Windows -> DB Браузер. Ачылган терезеде жашыл плюсту басыңыз (жаңы байланыш) -> PostgreSQL. Бул жерде биз Херокуда көргөн ишеним грамоталары керек болот. Терезени толтуруңуз: Spring Boot Pt.2 аркылуу телеграмма ботун түзүү: Quiz Bot - 3Жана "Тест туташуу" баскычын басыңыз. Эгер бардыгы туура аткарылса, маалымат базасына туташуу ийгorктүү болгонун көрсөткөн калкыма терезе пайда болот. Маалымат булагыбызды сактаңыз.<h3>Базада tableларды түзүңүз</h3>Эми биз иштей турган tableларды түзөлү. Биринчиден, PostgreSQL орнотобуз . Орнотуудан кийин src/main/resources папкасында initDB.sql файлын түзүңүз:
DROP TABLE IF EXISTS java_quiz;
DROP TABLE IF EXISTS users;
CREATE SEQUENCE global_seq START WITH 100000;

CREATE TABLE users
(
    id         INTEGER PRIMARY KEY DEFAULT nextval('global_seq'),
    chat_id    INTEGER UNIQUE                NOT NULL,
    name       VARCHAR                       NOT NULL,
    score      INTEGER             DEFAULT 0 NOT NULL,
    high_score INTEGER             DEFAULT 0 NOT NULL,
    bot_state  VARCHAR                       NOT NULL
);

CREATE TABLE java_quiz
(
    id             INTEGER PRIMARY KEY DEFAULT nextval('global_seq'),
    question       VARCHAR NOT NULL,
    answer_correct VARCHAR NOT NULL,
    option1        VARCHAR NOT NULL,
    option2        VARCHAR NOT NULL,
    option3        VARCHAR NOT NULL
);
Биздин сценарий эмне кылат? Биринчи эки сап, эгер бар болсо, аларды кайра түзүү үчүн tableларды өчүрөт. Үчүнчү сап биздин маалымат базасында уникалдуу ID жазууларды түзүү үчүн колдонула турган ырааттуулукту түзөт. Андан кийин биз эки table түзөбүз: колдонуучулар жана суроолор үчүн. Колдонуучунун уникалдуу идентификатору, телеграмма чат идентификатору, аты, упайлардын саны (учурдагы жана максималдуу), ошондой эле боттун учурдагы абалы болот. Суроолордун уникалдуу идентификатору, ошондой эле суроо-жооп варианттары үчүн жооптуу талаалар болот. Алынган скриптти оң баскыч менен чыкылдатып, "SQL скриптин аткаруу" дегенди тандай алабыз. "Cmd-Line интерфейси" пунктуна өзгөчө көңүл буруу керек - бул жерде бизге жаңы орнотулган PostgreSQL керек болот. Бул талааны конфигурациялоодо "Жаңы Cmd-Line интерфейсин" тандап, psql.exe жолду көрсөтүңүз. Натыйжада, орнотуулар төмөнкүдөй көрүнүшү керек: Spring Boot Pt.2 аркылуу телеграмма ботун түзүү: Quiz Bot - 4Биз сценарийди аткарабыз жана эгер биз эч жерде ката кетирген жокпуз, анда биздин ишибиздин натыйжасы төмөнкүдөй болот: Spring Boot Pt.2 аркылуу телеграмма ботун түзүү: Quiz Bot - 8<h3>Модель түзүү</h3>Эми убакыт келди Java codeун жазууга кайтуу. Макаланы кыскартуу үчүн мен класстарды жазууда колдонулган annotationлардын сүрөттөлүшүн калтырып коём, андыктан алар менен тааныша аласыз. Келгиле, моделдик пакетти түзөлү, анда үч классыбыз болот:
  • AbstractBaseEntity - бул идентификатору болушу мүмкүн болгон ар кандай an objectти сүрөттөгөн класс (бул класс стажировкада көрө турган нерселердин күчтүү жөнөкөйлөштүрүлүшү):
    package com.whiskels.telegram.model;
    
    import lombok.Getter;
    import lombok.Setter;
    
    import javax.persistence.*;
    // Аннотация, которая говорит нам, что это суперкласс для всех Entity
    // https://vladmihalcea.com/how-to-inherit-properties-from-a-base-class-entity-using-mappedsuperclass-with-jpa-and-hibernate/
    @MappedSuperclass
    // http://stackoverflow.com/questions/594597/hibernate-annotations-which-is-better-field-or-property-access
    @Access(AccessType.FIELD)
    
    // Аннотации Lombok для автогенерации сеттеров и геттеров на все поля
    @Getter
    @Setter
    public abstract class AbstractBaseEntity {
    
    // Аннотации, описывающие механизм генерации id - разберитесь в documentации каждой!
        @Id
        @SequenceGenerator(name = "global_seq", sequenceName = "global_seq", allocationSize = 1, initialValue = START_SEQ)
        @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "global_seq")
    //  See https://hibernate.atlassian.net/browse/HHH-3718 and https://hibernate.atlassian.net/browse/HHH-12034
    //  Proxy initialization when accessing its identifier managed now by JPA_PROXY_COMPLIANCE setting
        protected Integer id;
    
        protected AbstractBaseEntity() {
        }
    }
  • Колдонуучу :
    package com.whiskels.telegram.model;
    
    import com.whiskels.telegram.bot.State;
    import lombok.AllArgsConstructor;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    import lombok.Setter;
    import org.hibernate.annotations.BatchSize;
    
    import javax.persistence.*;
    import javax.validation.constraints.NotBlank;
    import javax.validation.constraints.NotNull;
    import java.util.Set;
    
    import static javax.persistence.FetchType.EAGER;
    
    @Entity
    @Table(name = "users", uniqueConstraints = {@UniqueConstraint(columnNames = "chat_id", name = "users_unique_chatid_idx")})
    @Getter
    @Setter
    @NoArgsConstructor
    @AllArgsConstructor
    public class User extends AbstractBaseEntity {
        @Column(name = "chat_id", unique = true, nullable = false)
        @NotNull
        private Integer chatId;
    
        @Column(name = "name", unique = true, nullable = false)
        @NotBlank
        private String name;
    
        @Column(name = "score", nullable = false)
        @NotNull
        private Integer score;
    
        @Column(name = "high_score", nullable = false)
        @NotNull
        private Integer highScore;
    
        @Column(name = "bot_state", nullable = false)
        @NotBlank
        private State botState;
    
    // Конструктор нужен для создания нового пользователя (а может и нет? :))
        public User(int chatId) {
            this.chatId = chatId;
            this.name = String.valueOf(chatId);
            this.score = 0;
            this.highScore = 0;
            this.botState = State.START;
        }
    }
  • Суроо классы :
    package com.whiskels.telegram.model;
    
    import lombok.AllArgsConstructor;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    import lombok.Setter;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.Table;
    import javax.validation.constraints.NotBlank;
    
    @Entity
    @Table(name = "java_quiz")
    @Getter
    @Setter
    @NoArgsConstructor
    @AllArgsConstructor
    public class Question extends AbstractBaseEntity {
        @Column(name = "question", nullable = false)
        @NotBlank
        private String question;
    
        @Column(name = "answer_correct", nullable = false)
        @NotBlank
        private String correctAnswer;
    
        @Column(name = "option2", nullable = false)
        @NotBlank
        private String optionOne;
    
        @Column(name = "option1", nullable = false)
        @NotBlank
        private String optionTwo;
    
        @Column(name = "option3", nullable = false)
        @NotBlank
        private String optionThree;
    
        @Override
        public String toString() {
            return "Question{" +
                    "question='" + question + '\'' +
                    ", correctAnswer='" + correctAnswer + '\'' +
                    ", optionOne='" + optionOne + '\'' +
                    ", optionTwo='" + optionTwo + '\'' +
                    ", optionThree='" + optionThree + '\'' +
                    '}';
        }
    }
<h3>Репозиторийлерди түзүү</h3>Эми Spring Data Jpa репозиторийлерин жазалы. Биз эки интерфейске ээ болгон репозиторий пакетин түзөбүз : JpaUserRepository, JpaQuestionRepository. Алар JpaRepository'ден, жазгы маалыматтар интерфейсинен мураска алышат, ал бизге сыйкырды иш жүзүндө жаратууга мүмкүндүк берет. Алардын ишин түшүнүү үчүн, мен Евгений Борисовдун видеосун көрүүнү сунуштайм . Класстар абдан аз болот:
  • JpaUserRepository:
    package com.whiskels.telegram.repository;
    
    import com.whiskels.telegram.model.User;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.stereotype.Repository;
    import org.springframework.transaction.annotation.Transactional;
    
    import java.util.Optional;
    
    @Repository
    @Transactional(readOnly = true)
    public interface JpaUserRepository extends JpaRepository<user, integer=""> {
    // По названию метода Spring сам поймет, что мы хотим получить пользователя по переданному chatId
        Optional<user> getByChatId(int chatId);
    }
    </user></user,>
  • JpaQuestionRepository:
    package com.whiskels.telegram.repository;
    
    import com.whiskels.telegram.model.Question;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.data.jpa.repository.Query;
    
    @Repository
    @Transactional(readOnly = true)
    public interface JpaQuestionRepository extends JpaRepository<question, integer=""> {
    // А здесь мы написали SQL Query, которая будет выбирать 1 случайный вопрос из таблицы вопросов
        @Query(nativeQuery = true, value = "SELECT *  FROM java_quiz ORDER BY random() LIMIT 1")
        Question getRandomQuestion();
    }
    </question,>
<h3>Ботко функция кошуу</h3> Колдонуучу классында бизде түзүлө элек State классынын талаасы бар , ал колдонуучу учурда бот менен иштөөнүн кайсы баскычында экенин айтып берет. Аны /bot пакетинде түзөлү:
package com.whiskels.telegram.bot;

public enum State {
    NONE,
    START,
    ENTER_NAME,
    PLAYING_QUIZ,
}
Андан кийин биз бот/иштетүү пакетин түзөбүз, анда биз иштеткич интерфейсин жарыялайбыз:
package com.whiskels.telegram.bot.handler;

import com.whiskels.telegram.bot.State;
import com.whiskels.telegram.model.User;
import org.telegram.telegrambots.meta.api.methods.PartialBotApiMethod;

import java.io.Serializable;
import java.util.List;

public interface Handler {

// основной метод, который будет обрабатывать действия пользователя
    List<partialbotapimethod<? extends="" serializable="">> handle(User user, String message);
// метод, который позволяет узнать, можем ли мы обработать текущий State у пользователя
    State operatedBotState();
// метод, который позволяет узнать, Howие команды CallBackQuery мы можем обработать в этом классе
    List<string> operatedCallBackQuery();
}

</string></partialbotapimethod<?>
Биз иштеткичтерди бир аз кийинчерээк түзөбүз, бирок азыр окуяны иштетүүнү жаңы UpdateReceiver классына өткөрүп берели , аны бот топтомунун тамырында түзөбүз: КӨҢҮЛ БЕРҮҮ! Бул жерде жана андан ары List> handle(args) катары көрсөтүлгөн ыкмалар болот; чындыгында алар ушундай көрүнөт, бирок code форматтоочу аларды сындырды:Spring Boot Pt.2 аркылуу телеграмма ботун түзүү: Quiz Bot - 6
package com.whiskels.telegram.bot;

import com.whiskels.telegram.bot.handler.Handler;
import com.whiskels.telegram.model.User;
import com.whiskels.telegram.repository.JpaUserRepository;
import org.springframework.stereotype.Component;
import org.telegram.telegrambots.meta.api.methods.PartialBotApiMethod;
import org.telegram.telegrambots.meta.api.objects.CallbackQuery;
import org.telegram.telegrambots.meta.api.objects.Message;
import org.telegram.telegrambots.meta.api.objects.Update;

import java.io.Serializable;
import java.util.Collections;
import java.util.List;

@Component
public class UpdateReceiver {
    // Храним доступные хендлеры в списке (подсмотрел у Miroha)
    private final List<handler> handlers;
    // Имеем доступ в базу пользователей
    private final JpaUserRepository userRepository;

    public UpdateReceiver(List<handler> handlers, JpaUserRepository userRepository) {
        this.handlers = handlers;
        this.userRepository = userRepository;
    }

    // Обрабатываем полученный Update
    public List<partialbotapimethod<? extends="" serializable="">> handle(Update update) {
        // try-catch, чтобы при несуществующей команде просто возвращать пустой список
        try {
            // Проверяем, если Update - сообщение с текстом
            if (isMessageWithText(update)) {
                // Получаем Message из Update
                final Message message = update.getMessage();
                // Получаем айди чата с пользователем
                final int chatId = message.getFrom().getId();

                // Просим у репозитория пользователя. Если такого пользователя нет - создаем нового и возвращаем его.
                // Как раз на случай нового пользователя мы и сделали конструктор с одним параметром в классе User
                final User user = userRepository.getByChatId(chatId)
                        .orElseGet(() -> userRepository.save(new User(chatId)));
                // Ищем нужный обработчик и возвращаем результат его работы
                return getHandlerByState(user.getBotState()).handle(user, message.getText());

            } else if (update.hasCallbackQuery()) {
                final CallbackQuery callbackQuery = update.getCallbackQuery();
                final int chatId = callbackQuery.getFrom().getId();
                final User user = userRepository.getByChatId(chatId)
                        .orElseGet(() -> userRepository.save(new User(chatId)));

                return getHandlerByCallBackQuery(callbackQuery.getData()).handle(user, callbackQuery.getData());
            }

            throw new UnsupportedOperationException();
        } catch (UnsupportedOperationException e) {
            return Collections.emptyList();
        }
    }

    private Handler getHandlerByState(State state) {
        return handlers.stream()
                .filter(h -> h.operatedBotState() != null)
                .filter(h -> h.operatedBotState().equals(state))
                .findAny()
                .orElseThrow(UnsupportedOperationException::new);
    }

    private Handler getHandlerByCallBackQuery(String query) {
        return handlers.stream()
                .filter(h -> h.operatedCallBackQuery().stream()
                        .anyMatch(query::startsWith))
                .findAny()
                .orElseThrow(UnsupportedOperationException::new);
    }

    private boolean isMessageWithText(Update update) {
        return !update.hasCallbackQuery() && update.hasMessage() && update.getMessage().hasText();
    }
}

</partialbotapimethod<?></handler></handler>
Жана биз аны иштетүүнү Bot классында тапшырабыз:
package com.whiskels.telegram.bot;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.telegram.telegrambots.bots.TelegramLongPollingBot;
import org.telegram.telegrambots.meta.api.methods.PartialBotApiMethod;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.Update;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;

import java.io.Serializable;
import java.util.List;

@Slf4j
@Component
public class Bot extends TelegramLongPollingBot {
    @Value("${bot.name}")
    @Getter
    private String botUsername;

    @Value("${bot.token}")
    @Getter
    private String botToken;

    private final UpdateReceiver updateReceiver;

    public Bot(UpdateReceiver updateReceiver) {
        this.updateReceiver = updateReceiver;
    }

    @Override
    public void onUpdateReceived(Update update) {
        List<partialbotapimethod<? extends="" serializable="">> messagesToSend = updateReceiver.handle(update);

        if (messagesToSend != null && !messagesToSend.isEmpty()) {
            messagesToSend.forEach(response -> {
                if (response instanceof SendMessage) {
                    executeWithExceptionCheck((SendMessage) response);
                }
            });
        }
    }

    public void executeWithExceptionCheck(SendMessage sendMessage) {
        try {
            execute(sendMessage);
        } catch (TelegramApiException e) {
            log.error("oops");
        }
    }
}

</partialbotapimethod<?>
Азыр биздин боттун делегаттары окуяны UpdateReceiver классына иштетүүдө , бирок бизде азырынча эч кандай иштетүүчүлөр жок. Келгиле, аларды түзөлү! ЖАШОО! Мен чындап эле ушундай бот жазуу мүмкүнчүлүктөрү менен бөлүшкүм келди, ошондуктан андан аркы codeду (негизинен UpdateReceiver codeу сыяктуу) ар кандай үлгүлөрдү колдонуу менен абдан жакшы рефакторизациялоого болот. Бирок биз үйрөнүп жатабыз жана биздин максат – минималдуу жашоого жөндөмдүү бот, ошондуктан дагы бир үй тапшырмасы катары, сиз көргөн нерселердин бардыгын рефакциялай аласыз :) Пайдалуу пакетти түзүңүз жана анын ичинде - TelegramUtil классын түзүңүз :
package com.whiskels.telegram.util;

import com.whiskels.telegram.model.User;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton;

public class TelegramUtil {
    public static SendMessage createMessageTemplate(User user) {
        return createMessageTemplate(String.valueOf(user.getChatId()));
    }

    // Создаем шаблон SendMessage с включенным Markdown
    public static SendMessage createMessageTemplate(String chatId) {
        return new SendMessage()
                .setChatId(chatId)
                .enableMarkdown(true);
    }

    // Создаем кнопку
    public static InlineKeyboardButton createInlineKeyboardButton(String text, String command) {
        return new InlineKeyboardButton()
                .setText(text)
                .setCallbackData(command);
    }
}
Биз төрт иштеткичти жазабыз: HelpHandler, QuizHandler, RegistrationHandler, StartHandler. StartHandler:
package com.whiskels.telegram.bot.handler;

import com.whiskels.telegram.bot.State;
import com.whiskels.telegram.model.User;
import com.whiskels.telegram.repository.JpaUserRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.telegram.telegrambots.meta.api.methods.PartialBotApiMethod;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;

import java.io.Serializable;
import java.util.Collections;
import java.util.List;

import static com.whiskels.telegram.util.TelegramUtil.createMessageTemplate;

@Component
public class StartHandler implements Handler {
    @Value("${bot.name}")
    private String botUsername;

    private final JpaUserRepository userRepository;

    public StartHandler(JpaUserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public List<partialbotapimethod<? extends="" serializable="">> handle(User user, String message) {
        // Приветствуем пользователя
        SendMessage welcomeMessage = createMessageTemplate(user)
                .setText(String.format(
                        "Hola! I'm *%s*%nI am here to help you learn Java", botUsername
                ));
        // Просим назваться
        SendMessage registrationMessage = createMessageTemplate(user)
                .setText("In order to start our journey tell me your name");
        // Меняем пользователю статус на - "ожидание ввода имени"
        user.setBotState(State.ENTER_NAME);
        userRepository.save(user);

        return List.of(welcomeMessage, registrationMessage);
    }

    @Override
    public State operatedBotState() {
        return State.START;
    }

    @Override
    public List<string> operatedCallBackQuery() {
        return Collections.emptyList();
    }
}

</string></partialbotapimethod<?>
RegistrationHandler:
package com.whiskels.telegram.bot.handler;

import com.whiskels.telegram.bot.State;
import com.whiskels.telegram.model.User;
import com.whiskels.telegram.repository.JpaUserRepository;
import org.springframework.stereotype.Component;
import org.telegram.telegrambots.meta.api.methods.PartialBotApiMethod;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton;

import java.io.Serializable;
import java.util.List;

import static com.whiskels.telegram.bot.handler.QuizHandler.QUIZ_START;
import static com.whiskels.telegram.util.TelegramUtil.createInlineKeyboardButton;
import static com.whiskels.telegram.util.TelegramUtil.createMessageTemplate;

@Component
public class RegistrationHandler implements Handler {
    //Храним поддерживаемые CallBackQuery в виде констант
    public static final String NAME_ACCEPT = "/enter_name_accept";
    public static final String NAME_CHANGE = "/enter_name";
    public static final String NAME_CHANGE_CANCEL = "/enter_name_cancel";

    private final JpaUserRepository userRepository;

    public RegistrationHandler(JpaUserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public List<partialbotapimethod<? extends="" serializable="">> handle(User user, String message) {
        // Проверяем тип полученного события
        if (message.equalsIgnoreCase(NAME_ACCEPT) || message.equalsIgnoreCase(NAME_CHANGE_CANCEL)) {
            return accept(user);
        } else if (message.equalsIgnoreCase(NAME_CHANGE)) {
            return changeName(user);
        }
        return checkName(user, message);

    }

    private List<partialbotapimethod<? extends="" serializable="">> accept(User user) {
        // Если пользователь принял Name - меняем статус и сохраняем
        user.setBotState(State.NONE);
        userRepository.save(user);

        // Создаем кнопку для начала игры
        InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();

        List<inlinekeyboardbutton> inlineKeyboardButtonsRowOne = List.of(
                createInlineKeyboardButton("Start quiz", QUIZ_START));

        inlineKeyboardMarkup.setKeyboard(List.of(inlineKeyboardButtonsRowOne));

        return List.of(createMessageTemplate(user).setText(String.format(
                "Your name is saved as: %s", user.getName()))
                .setReplyMarkup(inlineKeyboardMarkup));
    }

    private List<partialbotapimethod<? extends="" serializable="">> checkName(User user, String message) {
        // При проверке имени мы превентивно сохраняем пользователю новое Name в базе
        // идея для рефакторинга - добавить временное хранение имени
        user.setName(message);
        userRepository.save(user);

        // Doing кнопку для применения изменений
        InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();

        List<inlinekeyboardbutton> inlineKeyboardButtonsRowOne = List.of(
                createInlineKeyboardButton("Accept", NAME_ACCEPT));

        inlineKeyboardMarkup.setKeyboard(List.of(inlineKeyboardButtonsRowOne));

        return List.of(createMessageTemplate(user)
                .setText(String.format("You have entered: %s%nIf this is correct - press the button", user.getName()))
                .setReplyMarkup(inlineKeyboardMarkup));
    }

    private List<partialbotapimethod<? extends="" serializable="">> changeName(User user) {
        // При requestе изменения имени мы меняем State
        user.setBotState(State.ENTER_NAME);
        userRepository.save(user);

        // Создаем кнопку для отмены операции
        InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();

        List<inlinekeyboardbutton> inlineKeyboardButtonsRowOne = List.of(
                createInlineKeyboardButton("Cancel", NAME_CHANGE_CANCEL));

        inlineKeyboardMarkup.setKeyboard(List.of(inlineKeyboardButtonsRowOne));

        return List.of(createMessageTemplate(user).setText(String.format(
                "Your current name is: %s%nEnter new name or press the button to continue", user.getName()))
                .setReplyMarkup(inlineKeyboardMarkup));
    }

    @Override
    public State operatedBotState() {
        return State.ENTER_NAME;
    }

    @Override
    public List<string> operatedCallBackQuery() {
        return List.of(NAME_ACCEPT, NAME_CHANGE, NAME_CHANGE_CANCEL);
    }
}

</string></inlinekeyboardbutton></partialbotapimethod<?></inlinekeyboardbutton></partialbotapimethod<?></inlinekeyboardbutton></partialbotapimethod<?></partialbotapimethod<?>
Жардам берүүчү:
package com.whiskels.telegram.bot.handler;

import com.whiskels.telegram.bot.State;
import com.whiskels.telegram.model.User;
import org.springframework.stereotype.Component;
import org.telegram.telegrambots.meta.api.methods.PartialBotApiMethod;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static com.whiskels.telegram.bot.handler.RegistrationHandler.NAME_CHANGE;
import static com.whiskels.telegram.util.TelegramUtil.createInlineKeyboardButton;
import static com.whiskels.telegram.util.TelegramUtil.createMessageTemplate;

@Component
public class HelpHandler implements Handler {

    @Override
    public List<partialbotapimethod<? extends="" serializable="">> handle(User user, String message) {
        // Создаем кнопку для смены имени
        InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();

        List<inlinekeyboardbutton> inlineKeyboardButtonsRowOne = List.of(
                createInlineKeyboardButton("Change name", NAME_CHANGE));

        inlineKeyboardMarkup.setKeyboard(List.of(inlineKeyboardButtonsRowOne));

        return List.of(createMessageTemplate(user).setText(String.format("" +
                "You've asked for help %s? Here it comes!", user.getName()))
        .setReplyMarkup(inlineKeyboardMarkup));

    }

    @Override
    public State operatedBotState() {
        return State.NONE;
    }

    @Override
    public List<string> operatedCallBackQuery() {
        return Collections.emptyList();
    }
}

</string></inlinekeyboardbutton></partialbotapimethod<?>
QuizHandler (эң начар
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION