JavaRush /Java Blog /Random-ID /Membuat bot telegram menggunakan Spring Boot Pt.2: Quiz B...
Whiskels
Level 41
Москва

Membuat bot telegram menggunakan Spring Boot Pt.2: Quiz Bot

Dipublikasikan di grup Random-ID
BAGIAN 1 Aloha! Pada artikel sebelumnya, kami membuat bot sederhana yang menyambut kami di acara apa pun. Kami telah menulis ribuan baris kode, dan inilah saatnya menambahkan fungsionalitas yang lebih kompleks ke bot kami. Hari ini kami akan mencoba menulis bot sederhana sehingga di waktu luang kami, kami dapat mengasah pengetahuan kami tentang Java Core sebelum wawancara (yang mengejutkan, saya belum menemukan satu pun bot yang berfungsi seperti ini). Untuk melakukan ini kami akan melakukan hal berikut:
  • sambungkan database Postgres eksternal ke Heroku;
  • Mari kita tulis skrip pertama kita untuk menginisialisasi dan mengisi database;
  • mari sambungkan Spring Boot Data JPA untuk bekerja dengan database;
  • Kami menerapkan berbagai skenario perilaku bot.
Jika ini menarik bagi Anda, dan setelah membaca artikel Anda tidak ingin membuang tomat busuk, beri bintang di repositori saya , saya akan senang! Jika Anda memiliki pertanyaan, kami akan membahasnya di komentar. Inilah yang akan kita dapatkan pada akhirnya: Membuat bot telegram menggunakan Spring Boot Pt.2: Quiz Bot - 1Membuat bot telegram menggunakan Spring Boot Pt.2: Quiz Bot - 2Membuat bot telegram menggunakan Spring Boot Pt.2: Quiz Bot - 3<h2>Jadi, ayo!</h2><h3>Membuat database di Heroku</h3>Mari kita mulai dengan membuat database eksternal pertama kita. Untuk pekerjaan lokal, saya sarankan memeriksa pgAdmin . Namun saya ingin Anda dipandu oleh fakta bahwa di masa mendatang Anda akan menerapkan bot ke Heroku sehingga tidak bergantung pada mesin lokal Anda, dan untuk itu mari berkenalan dengan layanan ini. Prosedur:
  • mendaftar di Heroku ;
  • Buka dasbor kami -> Baru -> Buat aplikasi baru dan buat aplikasi baru;
  • Kami masuk ke aplikasi yang baru dibuat, kami terintimidasi oleh banyak tombol, tetapi kami berkonsentrasi pada panel "Add-on yang diinstal" - di sebelahnya ada tombol Konfigurasi Add-on, kami klik;
  • Masukkan “Heroku Postgres” ke dalam pencarian, pilih paket “Hobby Dev - Gratis” -> Kirim Formulir Pemesanan;
  • Buka database yang baru diperoleh -> Pengaturan -> Lihat Kredensial. Tab ini akan berisi kunci kami untuk mengakses database. Kami mengingat lokasinya - kami memerlukannya untuk menghubungkan database, seperti DataSource di IDEA.
<h3>Tambahkan dependensi ke pom.xml</h3>Sebagai bagian dari pengerjaan bot kami, kami akan menambahkan dependensi berikut ke pom kami: Lombok, Spring Boot Data JPA, PostgreSQL. Berhenti! Apa semua ini dan mengapa kami menambahkannya?
  • Lombok adalah perpustakaan yang berkatnya kami akan mengurangi jumlah kode yang berbeda secara signifikan. Dengannya kita dapat secara otomatis membuat konstruktor, setter, getter, dan banyak lagi.
  • Spring Data JPA adalah kerangka kerja untuk bekerja dengan database (meskipun kedengarannya terlalu sederhana). Deskripsi kemampuan Spring Data JPA akan berjumlah serangkaian artikel, dan ketergantungan yang kami tentukan juga memerlukan Hibernate dan banyak lagi, jadi mari lewati detailnya dan coba tulis sesuatu menggunakan Spring JPA hari ini.
  • PostgreSQL - kami menarik perpustakaan untuk mendapatkan driver yang akan bekerja dengan database kami.
Pom.xml kita mulai terlihat seperti ini: Properti: Membuat bot telegram menggunakan Spring Boot Pt.2: Quiz Bot - 1Ketergantungan: Membuat bot telegram menggunakan Spring Boot Pt.2: Quiz Bot - 2Jika Anda belum membaca artikel sebelumnya, harap perhatikan bahwa kami menambahkan dependensi baru di sini, jadi ini bukan struktur pom.xml yang lengkap. Jangan lupa untuk memuat dependensi ini ke dalam proyek kita (misalnya, dengan masuk ke jendela Maven -> Impor ulang semua proyek Maven).<h3>Menghubungkan database di IDEA</h3>Jika Anda menggunakan IDEA Community Edition, Anda bisa aktifkan tab DataSource sebagai berikut . Setelah menambahkan plugin, kita perlu mengkonfigurasi DataSource. Untuk melakukannya, aktifkan dulu tampilan plugin: View -> Tool Windows -> DB Browser. Di jendela yang terbuka, klik tanda plus hijau (koneksi baru) -> PostgreSQL. Di sini kita memerlukan kredensial, yang telah kita lihat di Heroku. Isi jendela: Membuat bot telegram menggunakan Spring Boot Pt.2: Quiz Bot - 3Dan klik "Uji Koneksi". Jika semuanya dilakukan dengan benar, jendela pop-up akan muncul yang menunjukkan bahwa koneksi ke database berhasil. Simpan Sumber Data kita.<h3>Buat tabel di database</h3>Sekarang mari kita buat tabel yang akan kita gunakan. Pertama, mari kita instal PostgreSQL . Setelah instalasi, buat file initDB.sql di folder src/main/resources:

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
);
Apa fungsi skrip kita? Dua baris pertama menghapus tabel, jika ada, untuk membuatnya kembali. Baris ketiga membuat urutan yang akan digunakan untuk membuat entri id unik di database kami. Selanjutnya kita membuat dua tabel: untuk pengguna dan untuk pertanyaan. Pengguna akan memiliki id unik, id obrolan telegram, nama, jumlah poin (saat ini dan maksimum), serta status bot saat ini. Pertanyaan juga akan memiliki id unik, serta bidang yang bertanggung jawab atas pertanyaan dan opsi jawaban untuk pertanyaan tersebut. Kita dapat menjalankan skrip yang dihasilkan dengan mengklik kanan padanya dan memilih "Jalankan Skrip SQL". Perhatian khusus harus diberikan pada item "Antarmuka Cmd-Line" - di sini kita memerlukan PostgreSQL yang baru diinstal. Saat mengonfigurasi bidang ini, pilih "Antarmuka Cmd-Line Baru" dan tentukan jalur ke psql.exe. Hasilnya, pengaturannya akan terlihat seperti ini: Membuat bot telegram menggunakan Spring Boot Pt.2: Quiz Bot - 4Kita jalankan skripnya dan jika kita tidak melakukan kesalahan di mana pun, hasil pekerjaan kita adalah sebagai berikut: Membuat bot telegram menggunakan Spring Boot Pt.2: Quiz Bot - 8<h3>Buat model</h3>Sekarang saatnya untuk kembali menulis kode Java. Untuk mempersingkat artikel, saya akan menghilangkan deskripsi anotasi yang digunakan untuk menulis kelas sehingga Anda dapat membiasakan diri dengannya. Mari kita buat paket model dimana kita akan memiliki tiga kelas:
  • AbstrakBaseEntity adalah kelas yang mendeskripsikan objek apa pun yang dapat memiliki id (kelas ini adalah penyederhanaan kuat dari apa yang mungkin Anda lihat dalam magang):
    
    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() {
        }
    }
  • Pengguna :
    
    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;
        }
    }
    
  • Kelas pertanyaan :
    
    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>Membuat repositori</h3>Sekarang mari kita menulis repositori Spring Data Jpa. Kami membuat paket repositori yang akan memiliki dua antarmuka : JpaUserRepository, JpaQuestionRepository. Mereka akan mewarisi dari JpaRepository, antarmuka Spring Data yang memungkinkan kita menciptakan keajaiban secara praktis. Untuk memahami pekerjaan mereka, saya sarankan menonton video Evgeny Borisov . Kelas akan sangat kecil:
  • Repositori Pengguna Jpa:
    
    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,>
  • Repositori JpaQuestion:
    
    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>Tambahkan fungsionalitas ke bot</h3>Di kelas Pengguna kami memiliki bidang kelas Negara yang belum dibuat , yang akan memberi tahu kami pada tahap apa pengguna sedang bekerja dengan bot. Mari kita buat di paket /bot:

package com.whiskels.telegram.bot;

public enum State {
    NONE,
    START,
    ENTER_NAME,
    PLAYING_QUIZ,
}
Selanjutnya, kita akan membuat paket bot/handler dimana kita akan mendeklarasikan antarmuka handler:

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<?>
Kita akan membuat penangannya nanti, tetapi untuk saat ini mari kita delegasikan pemrosesan acara ke kelas UpdateReceiver baru , yang akan kita buat di root paket bot: PERHATIAN! Di sini dan selanjutnya akan ada metode yang ditampilkan sebagai Daftar> handle(args); pada kenyataannya tampilannya seperti ini, tetapi pemformat kode merusaknya:Membuat bot telegram menggunakan 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>
Dan kami mendelegasikan pemrosesan ke kelas 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<?>
Sekarang bot kami mendelegasikan pemrosesan acara ke kelas UpdateReceiver , tetapi kami belum memiliki penangan apa pun. Mari kita ciptakan! PENAFIAN! Saya benar-benar ingin berbagi kemungkinan menulis bot seperti itu, sehingga kode selanjutnya (seperti pada prinsipnya kode UpdateReceiver) dapat difaktorkan ulang dengan baik menggunakan berbagai pola. Tapi kami sedang belajar dan tujuan kami adalah bot yang dapat berfungsi secara minimal, sehingga sebagai pekerjaan rumah lainnya, Anda dapat memfaktorkan ulang semua yang Anda lihat :) Buat paket util, dan di dalamnya - kelas 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);
    }
}
Kami akan menulis empat penangan: HelpHandler, QuizHandler, RegistrationHandler, StartHandler. Pengendali Mulai:

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<?>
Pengendali Pendaftaran:

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<?>
Penangan Bantuan:

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 (yang terburuk
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION