JavaRush /Java Blog /Random-JA /Spring Boot を使用したテレグラム ボットの作成 Pt.2: クイズ ボット
Whiskels
レベル 41
Москва

Spring Boot を使用したテレグラム ボットの作成 Pt.2: クイズ ボット

Random-JA グループに公開済み
パート 1 アロハ! 前回の記事では、どんなイベントでも歓迎してくれるシンプルなボットを作成しました。すでに数千行のコードを作成しました。今度は、ボットにさらに複雑な機能を追加します。今日は、面接前に自由時間で Java コアの知識を磨くことができるように、簡単なボットを作成してみます (驚くべきことに、この種の動作するボットは 1 つも見つかりませんでした)。これを行うには、次のことを行います。
  • 外部 Postgres データベースを Heroku に接続します。
  • データベースを初期化してデータを取り込むための最初のスクリプトを作成しましょう。
  • Spring Boot Data JPA を接続してデータベースを操作しましょう。
  • ボットの動作に関するさまざまなシナリオを実装します。
これがあなたにとって興味があり、記事を読んだ後、腐ったトマトを投げたくない場合は、私のリポジトリにスターを付けてください、私は喜んでいます!ご質問がございましたら、コメントで検討させていただきます。最終的には次のようになります: Spring Boot を使用したテレグラム ボットの作成 Pt.2: クイズ ボット - 1Spring Boot を使用したテレグラム ボットの作成 Pt.2: クイズ ボット - 2Spring Boot を使用したテレグラム ボットの作成 Pt.2: クイズ ボット - 3<h2>それでは、始めましょう!</h2><h3>Heraku でのデータベースの作成</h3>最初の外部データベースを作成することから始めましょう。ローカル作業については、pgAdminをチェックすることをお勧めします。ただし、将来的にはローカル マシンに依存しないようにボットを Heroku にデプロイする予定であるという事実を考慮して、このサービスについてよく知っておいてください。手順:
  • Herokuに登録する;
  • ダッシュボード-> 新規 -> 新しいアプリの作成に移動し、新しいアプリケーションを作成します。
  • 新しく作成したアプリケーションに入ります。多くのボタンに圧倒されますが、「インストールされたアドオン」パネルに集中します。その隣に「アドオンの構成」ボタンがあるので、それをクリックします。
  • 検索に「Heraku Postgres」と入力し、「Hobby Dev - Free」プランを選択し、注文フォームを送信します。
  • 新しく取得したデータベースを開き、[設定] -> [資格情報の表示] を選択します。このタブには、データベースにアクセスするためのキーが含まれます。私たちはそれらの場所を覚えています。IDEA の DataSource のように、データベースに接続するためにそれらが必要になります。
<h3>依存関係を pom.xml に追加</h3>ボットの作業の一環として、Lombok、Spring Boot Data JPA、PostgreSQL の依存関係を pom に追加します。停止!これは一体何ですか?なぜ追加するのでしょうか?
  • Lombokはさまざまなコードの量を大幅に削減できるライブラリです。これを使用すると、コンストラクター、セッター、ゲッターなどを自動的に作成できます。
  • Spring Data JPA はデータベースを操作するためのフレームワークです(単純すぎるように思えますが)。Spring Data JPA の機能を説明すると一連の記事になり、指定した依存関係には Hibernate なども関係します。そのため、詳細は省略して、今日は Spring JPA を使用して何かを書いてみることにします。
  • PostgreSQL - データベースで動作するドライバーを取得するためにライブラリをプルします。
pom.xml は次のようになります。 プロパティ:Spring Boot を使用したテレグラム ボットの作成 Pt.2: クイズ ボット - 1依存関係:Spring Boot を使用したテレグラム ボットの作成 Pt.2: クイズ ボット - 2前の記事を読んでいない場合は、ここで新しい依存関係を追加しているため、これは完全な pom.xml 構造ではないことに注意してください。これらの依存関係をプロジェクトにロードすることを忘れないでください (たとえば、[Maven] -> [すべての Maven プロジェクトを再インポート] ウィンドウに移動します)。<h3>IDEA でのデータベースの接続</h3>IDEA Community Edition を使用している場合は、次のことができます。次のように [DataSource] タブを有効にします。プラグインを追加した後、DataSource を構成する必要があります。これを行うには、まずプラグインの表示を有効にします: [表示] -> [ツール ウィンドウ] -> [DB ブラウザ]。開いたウィンドウで、緑色のプラス (新しい接続) -> PostgreSQL をクリックします。ここでは資格情報が必要になります。これは Heroku ですでに説明したとおりです。ウィンドウに必要事項を入力しSpring Boot を使用したテレグラム ボットの作成 Pt.2: クイズ ボット - 3、「接続のテスト」をクリックします。すべてが正しく行われると、データベースへの接続が成功したことを示すポップアップ ウィンドウが表示されます。データ ソースを保存します。<h3>データベースにテーブルを作成</h3>次に、作業に使用するテーブルを作成しましょう。まず、 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
);
私たちのスクリプトは何をするのでしょうか? 最初の 2 行は、テーブルが存在する場合はそれを消去して再作成します。3 行目は、データベース内に一意の ID エントリを作成するために使用されるシーケンスを作成します。次に、ユーザー用と質問用の 2 つのテーブルを作成します。ユーザーは、一意の ID、電報チャット ID、名前、ポイント数 (現在および最大)、およびボットの現在のステータスを持ちます。質問には一意の ID と、質問と回答のオプションを担当するフィールドもあります。結果のスクリプトを右クリックして「SQL スクリプトの実行」を選択すると、スクリプトを実行できます。「Cmd-Line インターフェイス」項目には特に注意を払う必要があります。ここでは、新しくインストールされた PostgreSQL が必要です。このフィールドを設定するときは、「新しいコマンドライン インターフェイス」を選択し、psql.exe へのパスを指定します。その結果、設定は次のようになります。Spring Boot を使用したテレグラム ボットの作成 Pt.2: クイズ ボット - 4スクリプトを実行します。どこにも間違いがなければ、作業の結果は次のようになります。Spring Boot を使用したテレグラム ボットの作成 Pt.2: クイズ ボット - 8<h3>モデルを作成します</h3>さあ、いよいよです。 Java コードの作成に戻ります。記事を短くするために、クラスの記述に使用されるアノテーションの説明は省略します。これにより、クラスに慣れることができます。3 つのクラスが含まれるモデル パッケージを作成しましょう。
  • AbstractBaseEntity は、ID を持つことができるオブジェクトを記述するクラスです (このクラスは、インターンシップで目にするものを大幅に簡略化したものです)。
    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 リポジトリを作成しましょう。2 つのインターフェース(JpaUserRepository、JpaQuestionRepository)を持つリポジトリ パッケージを作成します。これらは、実際に魔法を作成できるようにする Spring Data インターフェイスである 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,>
  • 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>ボットに機能を追加する</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: クイズ ボット - 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 の 4 つのハンドラーを作成します。スタートハンドラ:
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<?>
登録ハンドラ:
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