JavaRush /وبلاگ جاوا /Random-FA /ساخت ربات تلگرام با استفاده از Spring Boot Pt.2: Quiz Bot...
Whiskels
مرحله
Москва

ساخت ربات تلگرام با استفاده از Spring Boot Pt.2: Quiz Bot

در گروه منتشر شد
قسمت 1 Aloha! در مقاله قبلی یک ربات ساده ساختیم که در هر رویدادی از ما استقبال می کند. ما قبلاً هزاران خط کد نوشته‌ایم، و زمان آن رسیده که عملکردهای پیچیده‌تری را به ربات خود اضافه کنیم. امروز سعی خواهیم کرد یک ربات ساده بنویسیم تا در وقت آزادمان بتوانیم دانش خود را در مورد Java Core قبل از مصاحبه تقویت کنیم (با کمال تعجب، من حتی یک ربات فعال از این نوع پیدا نکردم). برای این کار موارد زیر را انجام خواهیم داد:
  • یک پایگاه داده خارجی Postgres را به Heroku متصل کنید.
  • بیایید اولین اسکریپت های خود را برای مقداردهی اولیه و پر کردن پایگاه داده بنویسیم.
  • اجازه دهید Spring Boot Data JPA را برای کار با پایگاه داده متصل کنیم.
  • ما سناریوهای مختلفی از رفتار ربات ها را پیاده سازی می کنیم.
اگر این برای شما جالب است و پس از خواندن مقاله نمی خواهید گوجه فرنگی های فاسد را پرتاب کنید، سپس یک ستاره در مخزن من قرار دهید ، خوشحال خواهم شد! اگر سوالی دارید، در نظرات در مورد آنها صحبت خواهیم کرد. این چیزی است که در پایان به دست می آوریم: ساخت ربات تلگرام با استفاده از Spring Boot Pt.2: Quiz Bot - 1ساخت ربات تلگرام با استفاده از Spring Boot Pt.2: Quiz Bot - 2ساخت ربات تلگرام با استفاده از Spring Boot Pt.2: Quiz Bot - 3<h2>خب، بیایید برویم!</h2><h3>ایجاد پایگاه داده در Heroku</h3> بیایید با ایجاد اولین پایگاه داده خارجی خود شروع کنیم. برای کارهای محلی، توصیه می کنم pgAdmin را بررسی کنید . اما من می خواهم شما را با این واقعیت راهنمایی کنید که در آینده ربات را در Heroku مستقر خواهید کرد تا به ماشین محلی شما وابسته نباشد و برای این کار بیایید با این سرویس آشنا شویم. روش:
  • ثبت نام در Heroku ؛
  • به داشبورد ما بروید -> جدید -> ایجاد برنامه جدید و ایجاد یک برنامه جدید.
  • ما به برنامه جدید ایجاد شده می رویم، از تعداد زیادی دکمه می ترسیم، اما روی پانل "افزونه های نصب شده" تمرکز می کنیم - در کنار آن یک دکمه پیکربندی افزونه ها وجود دارد، روی آن کلیک می کنیم.
  • "Heroku Postgres" را در جستجو وارد کنید، طرح "Hobby Dev - Free" -> Submit Order Form را انتخاب کنید.
  • پایگاه داده تازه به دست آمده -> تنظیمات -> مشاهده اعتبارنامه ها را باز کنید. این تب حاوی کلیدهای ما برای دسترسی به پایگاه داده است. ما مکان آنها را به خاطر می آوریم - برای اتصال پایگاه داده به آنها نیاز داریم، مانند DataSource در IDEA.
<h3>افزودن وابستگی ها به pom.xml</h3>به عنوان بخشی از کار بر روی ربات خود، وابستگی های زیر را به pom خود اضافه می کنیم: Lombok، Spring Boot Data JPA، PostgreSQL. متوقف کردن! همه اینها چیست و چرا آن را اضافه می کنیم؟
  • Lombok یک کتابخانه است که به لطف آن مقدار کدهای مختلف را به میزان قابل توجهی کاهش خواهیم داد. با آن می‌توانیم به‌طور خودکار سازنده‌ها، تنظیم‌کننده‌ها، دریافت‌کننده‌ها و موارد دیگر را ایجاد کنیم.
  • Spring Data JPA چارچوبی برای کار با پایگاه های داده است (اگرچه خیلی ساده به نظر می رسد). شرح قابلیت‌های Spring Data JPA مجموعه‌ای از مقالات خواهد بود، و وابستگی که ما مشخص کردیم شامل Hibernate و موارد دیگر نیز می‌شود، بنابراین بیایید از جزئیات صرف نظر کنیم و فقط سعی کنیم امروز با استفاده از Spring JPA چیزی بنویسیم.
  • PostgreSQL - ما کتابخانه را می کشیم تا درایوری به دست آوریم که با پایگاه داده ما کار کند.
pom.xml ما شروع به شکل زیر می کند: ویژگی ها: ساخت ربات تلگرام با استفاده از Spring Boot Pt.2: Quiz Bot - 1وابستگی ها: ساخت ربات تلگرام با استفاده از Spring Boot Pt.2: Quiz Bot - 2اگر مقاله قبلی را نخوانده اید، لطفاً توجه داشته باشید که ما در اینجا وابستگی های جدیدی اضافه می کنیم، بنابراین این ساختار کامل pom.xml نیست. فراموش نکنید که این وابستگی ها را در پروژه خود بارگیری کنید (به عنوان مثال، با رفتن به پنجره Maven -> Reimport Reimport all Maven projects).<h3>اتصال پایگاه داده در IDEA</h3>اگر از IDEA Community Edition استفاده می کنید، می توانید تب DataSource را به صورت زیر فعال کنید . پس از افزودن افزونه، باید DataSource را پیکربندی کنیم. برای این کار ابتدا نمایش پلاگین را فعال کنید: View -> Tool Windows -> DB Browser. در پنجره ای که باز می شود، روی علامت سبز سبز (اتصال جدید) -> PostgreSQL کلیک کنید. در اینجا ما به اعتبارنامه هایی نیاز خواهیم داشت که قبلاً در Heroku دیده ایم. پنجره را پر کنید: ساخت ربات تلگرام با استفاده از Spring Boot Pt.2: Quiz Bot - 3و روی "Test Connection" کلیک کنید. اگر همه چیز به درستی انجام شود، یک پنجره پاپ آپ ظاهر می شود که نشان می دهد اتصال به پایگاه داده با موفقیت انجام شده است. منبع داده خود را ذخیره کنید.<h3>جدول هایی را در پایگاه داده ایجاد کنید</h3>حالا اجازه دهید جداولی را ایجاد کنیم که با آنها کار خواهیم کرد. ابتدا، اجازه دهید PostgreSQL را نصب کنیم . پس از نصب، فایل initDB.sql را در پوشه 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
);
اسکریپت ما چه می کند؟ دو خط اول جداول را در صورت وجود پاک می کنند تا دوباره ایجاد شوند. خط سوم یک دنباله ایجاد می کند که برای ایجاد ورودی های شناسه منحصر به فرد در پایگاه داده ما استفاده می شود. سپس دو جدول ایجاد می کنیم: برای کاربران و برای سوالات. کاربر دارای شناسه منحصر به فرد، آیدی چت تلگرام، نام، تعداد امتیازات (فعالی و حداکثر) و همچنین وضعیت فعلی ربات خواهد بود. سوالات همچنین دارای شناسه منحصر به فرد و همچنین فیلدهای مسئول گزینه های پرسش و پاسخ برای آن خواهند بود. ما می توانیم اسکریپت به دست آمده را با کلیک راست روی آن و انتخاب "Execute SQL Script" اجرا کنیم. توجه ویژه ای باید به مورد "واسط Cmd-Line" شود - در اینجا به PostgreSQL تازه نصب شده نیاز داریم. هنگام پیکربندی این فیلد، "New Cmd-Line interface" را انتخاب کرده و مسیر psql.exe را مشخص کنید. در نتیجه، تنظیمات باید چیزی شبیه به این باشد: ساخت ربات تلگرام با استفاده از Spring Boot Pt.2: Quiz Bot - 4ما اسکریپت را اجرا می کنیم و اگر جایی اشتباهی مرتکب نشدیم، نتیجه کار ما به صورت زیر خواهد بود: ساخت ربات تلگرام با استفاده از Spring Boot Pt.2: Quiz Bot - 8<h3>Create a model</h3>اکنون زمان آن است. برای بازگشت به نوشتن کد جاوا. برای کوتاه‌تر شدن مقاله، شرح حاشیه‌نویسی‌های مورد استفاده در نوشتن کلاس‌ها را حذف می‌کنم تا بتوانید با آن‌ها آشنا شوید. بیایید یک بسته مدل ایجاد کنیم که در آن سه کلاس خواهیم داشت:
  • AbstractBaseEntity کلاسی است که هر شیئی را که می تواند شناسه داشته باشد را توصیف می کند (این کلاس ساده سازی قوی از آنچه ممکن است در یک دوره کارآموزی ببینید) است:
    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، یک رابط Spring Data که به ما اجازه می دهد عملا جادو ایجاد کنیم، ارث می برند. برای درک کار آنها، توصیه می کنم ویدیوی اوگنی بوریسوف را تماشا کنید . کلاس ها بسیار کوچک خواهند بود:
  • 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>در کلاس User یک فیلد از کلاس 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) نمایش داده می شوند. در واقع آنها به این شکل هستند، اما فرمت کننده کد آنها را شکست:ساخت ربات تلگرام با استفاده از 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 واگذار می کند، اما ما هنوز هیچ کنترل کننده ای نداریم. بیایید آنها را ایجاد کنیم! سلب مسئولیت! من واقعاً می‌خواستم امکانات نوشتن چنین رباتی را به اشتراک بگذارم، بنابراین کدهای بیشتر (مانند کد UpdateReceiver) را می‌توان به خوبی با استفاده از الگوهای مختلف بازسازی کرد. اما ما در حال یادگیری هستیم و هدف ما یک ربات حداقلی است، بنابراین به عنوان یک تکلیف دیگر، می‌توانید هر آنچه را که دیدید را اصلاح کنید :) یک بسته util ایجاد کنید و در آن - کلاس 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<?>
Registration Handler:
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