From 892cb7bed1feda954dbc78b706aa9873048772e5 Mon Sep 17 00:00:00 2001 From: upagge Date: Thu, 1 Oct 2020 20:44:01 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=87=D0=B5=D1=82=20=D1=82?= =?UTF-8?q?=D0=B0=D0=B1=D0=BB=D0=B8=D1=86=D1=8B=20=D1=80=D0=B5=D0=B9=D1=82?= =?UTF-8?q?=D0=B8=D0=BD=D0=B3=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/scheduler/RatingScheduler.java | 25 +++++++ .../src/main/resources/application-dev.yaml | 2 +- .../liquibase/v.2.0.0/2020-10-01-rating.xml | 15 ++++ .../vcs/core/domain/entity/RatingList.java | 73 +++++++++++-------- .../repository/RatingHistoryRepository.java | 6 ++ .../core/repository/RatingListRepository.java | 24 ++++++ .../impl/RatingHistoryRepositoryImpl.java | 9 +++ .../impl/RatingListRepositoryImpl.java | 53 ++++++++++++++ .../jpa/RatingHistoryJpaRepository.java | 5 ++ .../jpa/RatingListJpaRepository.java | 13 ++++ .../bot/vcs/core/service/RatingService.java | 4 + .../core/service/impl/RatingServiceImpl.java | 72 ++++++++++++++++++ .../org/sadtech/bot/vcs/core/utils/Smile.java | 4 + .../service/unit/RatingTopProcessing.java | 33 +++++++++ .../bot/vcs/telegram/unit/UnitConfig.java | 15 +++- .../telegram/utils/GeneratorKeyBoards.java | 6 ++ 16 files changed, 326 insertions(+), 33 deletions(-) create mode 100644 bitbucket-app/src/main/java/org/sadtech/bot/vcs/bitbucket/app/scheduler/RatingScheduler.java create mode 100644 bot-core/src/main/java/org/sadtech/bot/vcs/core/repository/RatingListRepository.java create mode 100644 bot-core/src/main/java/org/sadtech/bot/vcs/core/repository/impl/RatingListRepositoryImpl.java create mode 100644 bot-core/src/main/java/org/sadtech/bot/vcs/core/repository/jpa/RatingListJpaRepository.java create mode 100644 telegram-bot/src/main/java/org/sadtech/bot/vcs/telegram/service/unit/RatingTopProcessing.java diff --git a/bitbucket-app/src/main/java/org/sadtech/bot/vcs/bitbucket/app/scheduler/RatingScheduler.java b/bitbucket-app/src/main/java/org/sadtech/bot/vcs/bitbucket/app/scheduler/RatingScheduler.java new file mode 100644 index 0000000..8855191 --- /dev/null +++ b/bitbucket-app/src/main/java/org/sadtech/bot/vcs/bitbucket/app/scheduler/RatingScheduler.java @@ -0,0 +1,25 @@ +package org.sadtech.bot.vcs.bitbucket.app.scheduler; + +/** + * // TODO: 01.10.2020 Добавить описание. + * + * @author upagge 01.10.2020 + */ + +import lombok.RequiredArgsConstructor; +import org.sadtech.bot.vcs.core.service.RatingService; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class RatingScheduler { + + private final RatingService ratingService; + + @Scheduled(cron = "0 */1 * * * *") + private void ratingRecalculation() { + ratingService.ratingRecalculation(); + } + +} diff --git a/bitbucket-app/src/main/resources/application-dev.yaml b/bitbucket-app/src/main/resources/application-dev.yaml index 2f6f089..b803227 100644 --- a/bitbucket-app/src/main/resources/application-dev.yaml +++ b/bitbucket-app/src/main/resources/application-dev.yaml @@ -25,7 +25,7 @@ bitbucketbot: comment-count: 100 init: start-comment-id: 8157 - use: false + use: true bitbucket: token: ${BITBUCKET_ADMIN_TOKEN} url-pull-request-open: http://192.168.236.164:7990/rest/api/1.0/dashboard/pull-requests?limit=150&state=OPEN diff --git a/bitbucket-app/src/main/resources/liquibase/v.2.0.0/2020-10-01-rating.xml b/bitbucket-app/src/main/resources/liquibase/v.2.0.0/2020-10-01-rating.xml index 95711a7..e41bb20 100644 --- a/bitbucket-app/src/main/resources/liquibase/v.2.0.0/2020-10-01-rating.xml +++ b/bitbucket-app/src/main/resources/liquibase/v.2.0.0/2020-10-01-rating.xml @@ -22,4 +22,19 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bot-core/src/main/java/org/sadtech/bot/vcs/core/domain/entity/RatingList.java b/bot-core/src/main/java/org/sadtech/bot/vcs/core/domain/entity/RatingList.java index c4c9d12..90aa74d 100644 --- a/bot-core/src/main/java/org/sadtech/bot/vcs/core/domain/entity/RatingList.java +++ b/bot-core/src/main/java/org/sadtech/bot/vcs/core/domain/entity/RatingList.java @@ -1,31 +1,42 @@ -//package org.sadtech.bot.vcs.core.domain.entity; -// -//import lombok.EqualsAndHashCode; -//import lombok.Getter; -//import lombok.Setter; -// -//import javax.persistence.Column; -//import javax.persistence.Entity; -//import javax.persistence.Table; -// -///** -// * // TODO: 01.10.2020 Добавить описание. -// * -// * @author upagge 01.10.2020 -// */ -// -//@Getter -//@Setter -//@Entity -//@Table(name = "rating_list") -//@EqualsAndHashCode(onlyExplicitlyIncluded = true) -//public class RatingList { -// -// @Column(name = "login") -// @EqualsAndHashCode.Include -// private String login; -// -// @Column(name = "points") -// private Integer points; -// -//} +package org.sadtech.bot.vcs.core.domain.entity; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * // TODO: 01.10.2020 Добавить описание. + * + * @author upagge 01.10.2020 + */ + +@Getter +@Setter +@Entity +@Table(name = "rating_list") +@EqualsAndHashCode(onlyExplicitlyIncluded = true) +public class RatingList implements Comparable { + + @Id + @Column(name = "login") + @EqualsAndHashCode.Include + private String login; + + @Column(name = "points") + private Integer points; + + @Column(name = "number") + private Integer number; + + @Override + public int compareTo(@NotNull RatingList ratingList) { + return Integer.compare(ratingList.getPoints(), points); + } + +} diff --git a/bot-core/src/main/java/org/sadtech/bot/vcs/core/repository/RatingHistoryRepository.java b/bot-core/src/main/java/org/sadtech/bot/vcs/core/repository/RatingHistoryRepository.java index d962b53..e03b5a6 100644 --- a/bot-core/src/main/java/org/sadtech/bot/vcs/core/repository/RatingHistoryRepository.java +++ b/bot-core/src/main/java/org/sadtech/bot/vcs/core/repository/RatingHistoryRepository.java @@ -1,8 +1,12 @@ package org.sadtech.bot.vcs.core.repository; +import lombok.NonNull; import org.sadtech.basic.context.repository.SimpleManagerRepository; import org.sadtech.bot.vcs.core.domain.entity.RatingHistory; +import java.time.LocalDateTime; +import java.util.List; + /** * // TODO: 01.10.2020 Добавить описание. * @@ -10,4 +14,6 @@ import org.sadtech.bot.vcs.core.domain.entity.RatingHistory; */ public interface RatingHistoryRepository extends SimpleManagerRepository { + List findAllByDateAddBetween(@NonNull LocalDateTime from, @NonNull LocalDateTime to); + } diff --git a/bot-core/src/main/java/org/sadtech/bot/vcs/core/repository/RatingListRepository.java b/bot-core/src/main/java/org/sadtech/bot/vcs/core/repository/RatingListRepository.java new file mode 100644 index 0000000..11a9115 --- /dev/null +++ b/bot-core/src/main/java/org/sadtech/bot/vcs/core/repository/RatingListRepository.java @@ -0,0 +1,24 @@ +package org.sadtech.bot.vcs.core.repository; + +import org.sadtech.basic.context.repository.SimpleManagerRepository; +import org.sadtech.bot.vcs.core.domain.entity.RatingList; + +import java.util.List; +import java.util.Optional; + +/** + * // TODO: 01.10.2020 Добавить описание. + * + * @author upagge 01.10.2020 + */ +public interface RatingListRepository extends SimpleManagerRepository { + + Optional getByLogin(String login); + + List findFirstThree(); + + List findLastThree(); + + long count(); + +} diff --git a/bot-core/src/main/java/org/sadtech/bot/vcs/core/repository/impl/RatingHistoryRepositoryImpl.java b/bot-core/src/main/java/org/sadtech/bot/vcs/core/repository/impl/RatingHistoryRepositoryImpl.java index 4ea02b1..eb57c5c 100644 --- a/bot-core/src/main/java/org/sadtech/bot/vcs/core/repository/impl/RatingHistoryRepositoryImpl.java +++ b/bot-core/src/main/java/org/sadtech/bot/vcs/core/repository/impl/RatingHistoryRepositoryImpl.java @@ -1,11 +1,15 @@ package org.sadtech.bot.vcs.core.repository.impl; +import lombok.NonNull; import org.sadtech.basic.database.repository.manager.AbstractSimpleManagerRepository; import org.sadtech.bot.vcs.core.domain.entity.RatingHistory; import org.sadtech.bot.vcs.core.repository.RatingHistoryRepository; import org.sadtech.bot.vcs.core.repository.jpa.RatingHistoryJpaRepository; import org.springframework.stereotype.Repository; +import java.time.LocalDateTime; +import java.util.List; + /** * // TODO: 01.10.2020 Добавить описание. * @@ -21,4 +25,9 @@ public class RatingHistoryRepositoryImpl extends AbstractSimpleManagerRepository this.jpaRepository = jpaRepository; } + @Override + public List findAllByDateAddBetween(@NonNull LocalDateTime from, @NonNull LocalDateTime to) { + return jpaRepository.findAllByDateAddBetween(from, to); + } + } diff --git a/bot-core/src/main/java/org/sadtech/bot/vcs/core/repository/impl/RatingListRepositoryImpl.java b/bot-core/src/main/java/org/sadtech/bot/vcs/core/repository/impl/RatingListRepositoryImpl.java new file mode 100644 index 0000000..91becbd --- /dev/null +++ b/bot-core/src/main/java/org/sadtech/bot/vcs/core/repository/impl/RatingListRepositoryImpl.java @@ -0,0 +1,53 @@ +package org.sadtech.bot.vcs.core.repository.impl; + +import org.sadtech.basic.database.repository.manager.AbstractSimpleManagerRepository; +import org.sadtech.bot.vcs.core.domain.entity.RatingList; +import org.sadtech.bot.vcs.core.repository.RatingListRepository; +import org.sadtech.bot.vcs.core.repository.jpa.RatingListJpaRepository; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +/** + * // TODO: 01.10.2020 Добавить описание. + * + * @author upagge 01.10.2020 + */ +@Repository +public class RatingListRepositoryImpl extends AbstractSimpleManagerRepository implements RatingListRepository { + + private final RatingListJpaRepository jpaRepository; + + public RatingListRepositoryImpl(RatingListJpaRepository jpaRepository) { + super(jpaRepository); + this.jpaRepository = jpaRepository; + } + + @Override + public Optional getByLogin(String login) { + return jpaRepository.findById(login); + } + + @Override + public List findFirstThree() { + return jpaRepository.findAll( + PageRequest.of(0, 3, Sort.by(Sort.Direction.ASC, "number")) + ).getContent(); + } + + @Override + public List findLastThree() { + return jpaRepository.findAll( + PageRequest.of(0, 3, Sort.by(Sort.Direction.DESC, "number")) + ).getContent(); + } + + @Override + public long count() { + return jpaRepository.count(); + } + +} diff --git a/bot-core/src/main/java/org/sadtech/bot/vcs/core/repository/jpa/RatingHistoryJpaRepository.java b/bot-core/src/main/java/org/sadtech/bot/vcs/core/repository/jpa/RatingHistoryJpaRepository.java index 9d1ee0b..22fe0ce 100644 --- a/bot-core/src/main/java/org/sadtech/bot/vcs/core/repository/jpa/RatingHistoryJpaRepository.java +++ b/bot-core/src/main/java/org/sadtech/bot/vcs/core/repository/jpa/RatingHistoryJpaRepository.java @@ -3,6 +3,9 @@ package org.sadtech.bot.vcs.core.repository.jpa; import org.sadtech.bot.vcs.core.domain.entity.RatingHistory; import org.springframework.data.jpa.repository.JpaRepository; +import java.time.LocalDateTime; +import java.util.List; + /** * // TODO: 01.10.2020 Добавить описание. * @@ -10,4 +13,6 @@ import org.springframework.data.jpa.repository.JpaRepository; */ public interface RatingHistoryJpaRepository extends JpaRepository { + List findAllByDateAddBetween(LocalDateTime from, LocalDateTime to); + } diff --git a/bot-core/src/main/java/org/sadtech/bot/vcs/core/repository/jpa/RatingListJpaRepository.java b/bot-core/src/main/java/org/sadtech/bot/vcs/core/repository/jpa/RatingListJpaRepository.java new file mode 100644 index 0000000..2b0a662 --- /dev/null +++ b/bot-core/src/main/java/org/sadtech/bot/vcs/core/repository/jpa/RatingListJpaRepository.java @@ -0,0 +1,13 @@ +package org.sadtech.bot.vcs.core.repository.jpa; + +import org.sadtech.bot.vcs.core.domain.entity.RatingList; +import org.springframework.data.jpa.repository.JpaRepository; + +/** + * // TODO: 01.10.2020 Добавить описание. + * + * @author upagge 01.10.2020 + */ +public interface RatingListJpaRepository extends JpaRepository { + +} diff --git a/bot-core/src/main/java/org/sadtech/bot/vcs/core/service/RatingService.java b/bot-core/src/main/java/org/sadtech/bot/vcs/core/service/RatingService.java index 53c1b81..a52c01b 100644 --- a/bot-core/src/main/java/org/sadtech/bot/vcs/core/service/RatingService.java +++ b/bot-core/src/main/java/org/sadtech/bot/vcs/core/service/RatingService.java @@ -12,4 +12,8 @@ public interface RatingService { void addRating(@NonNull String login, @NonNull PointType type, @NonNull Integer points); + void ratingRecalculation(); + + String getRatingTop(@NonNull String login); + } diff --git a/bot-core/src/main/java/org/sadtech/bot/vcs/core/service/impl/RatingServiceImpl.java b/bot-core/src/main/java/org/sadtech/bot/vcs/core/service/impl/RatingServiceImpl.java index 60ca6a7..d9ddb3c 100644 --- a/bot-core/src/main/java/org/sadtech/bot/vcs/core/service/impl/RatingServiceImpl.java +++ b/bot-core/src/main/java/org/sadtech/bot/vcs/core/service/impl/RatingServiceImpl.java @@ -4,11 +4,19 @@ import lombok.NonNull; import lombok.RequiredArgsConstructor; import org.sadtech.bot.vcs.core.domain.PointType; import org.sadtech.bot.vcs.core.domain.entity.RatingHistory; +import org.sadtech.bot.vcs.core.domain.entity.RatingList; +import org.sadtech.bot.vcs.core.exception.NotFoundException; import org.sadtech.bot.vcs.core.repository.RatingHistoryRepository; +import org.sadtech.bot.vcs.core.repository.RatingListRepository; import org.sadtech.bot.vcs.core.service.RatingService; +import org.sadtech.bot.vcs.core.utils.Smile; import org.springframework.stereotype.Service; import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; /** * // TODO: 01.10.2020 Добавить описание. @@ -20,6 +28,7 @@ import java.time.LocalDateTime; public class RatingServiceImpl implements RatingService { private final RatingHistoryRepository ratingHistoryRepository; + private final RatingListRepository ratingListRepository; @Override public void addRating(@NonNull String login, @NonNull PointType type, @NonNull Integer points) { @@ -31,4 +40,67 @@ public class RatingServiceImpl implements RatingService { ratingHistoryRepository.save(ratingHistory); } + @Override + public void ratingRecalculation() { + AtomicInteger i = new AtomicInteger(); + final List newRatingList = ratingHistoryRepository.findAllByDateAddBetween(LocalDateTime.now().minusDays(30L), LocalDateTime.now()).stream() + .collect(Collectors.groupingBy(RatingHistory::getLogin, Collectors.summingInt(RatingHistory::getPoints))) + .entrySet().stream() + .map(this::createRatingList) + .sorted() + .peek(ratingList -> ratingList.setNumber(i.getAndIncrement())) + .collect(Collectors.toList()); + ratingListRepository.saveAll(newRatingList); + } + + private RatingList createRatingList(Map.Entry entry) { + final RatingList ratingList = new RatingList(); + ratingList.setLogin(entry.getKey()); + ratingList.setPoints(entry.getValue()); + return ratingList; + } + + @Override + public String getRatingTop(@NonNull String login) { + final RatingList personRating = ratingListRepository.getByLogin(login) + .orElseThrow(() -> new NotFoundException("Пользователь не найден")); + final Integer numberRatingList = personRating.getNumber(); + final long countPerson = ratingListRepository.count(); + final String threeMessage = ratingListRepository.findFirstThree().stream() + .map(this::createString) + .collect(Collectors.joining("\n")); + final String lastMessage = ratingListRepository.findLastThree().stream() + .map(this::createString) + .limit(countPerson - 3) + .collect(Collectors.joining("\n")); + String message = threeMessage; + + if (numberRatingList <= 2) { + if (countPerson > 3) { + message += "\n... ... ...\n"; + } + } else if (numberRatingList > 3 && numberRatingList <= (countPerson - 3)) { + message += "\n... ... ...\n" + personRating.getNumber() + ": " + personRating.getLogin() + "\n... ... ...\n"; + } else { + message += "\n... ... ...\n"; + } + message += lastMessage; + return message; + } + + private String createString(RatingList ratingList) { + String message = ""; + final Integer number = ratingList.getNumber(); + if (number == 0) { + message += Smile.TOP_ONE + " " + ratingList.getLogin() + " " + Smile.TOP_ONE; + } else if (number == 1) { + message += Smile.TOP_TWO + " " + ratingList.getLogin() + " " + Smile.TOP_TWO; + } else if (number == 2) { + message += Smile.TOP_THREE + " " + ratingList.getLogin() + " " + Smile.TOP_THREE; + } else { + message += Smile.KAKASHKA + " " + ratingList.getLogin(); + } + return message; + } + } diff --git a/bot-core/src/main/java/org/sadtech/bot/vcs/core/utils/Smile.java b/bot-core/src/main/java/org/sadtech/bot/vcs/core/utils/Smile.java index 8da9305..4ea12f5 100644 --- a/bot-core/src/main/java/org/sadtech/bot/vcs/core/utils/Smile.java +++ b/bot-core/src/main/java/org/sadtech/bot/vcs/core/utils/Smile.java @@ -27,6 +27,10 @@ public enum Smile { DAY_4("\uD83C\uDF11"), DAY_5("\uD83C\uDF1A"), TASK("\uD83D\uDCBC"), + TOP_ONE("\uD83C\uDF1F\uD83C\uDF1F\uD83C\uDF1F"), + TOP_TWO("\uD83D\uDE0E"), + TOP_THREE("\uD83E\uDD49"), + KAKASHKA("\uD83D\uDCA9"), MEGA_FUN("\uD83D\uDE02"), DANGEROUS("⚠️"), BELL("\uD83D\uDECE"), diff --git a/telegram-bot/src/main/java/org/sadtech/bot/vcs/telegram/service/unit/RatingTopProcessing.java b/telegram-bot/src/main/java/org/sadtech/bot/vcs/telegram/service/unit/RatingTopProcessing.java new file mode 100644 index 0000000..6f59d3c --- /dev/null +++ b/telegram-bot/src/main/java/org/sadtech/bot/vcs/telegram/service/unit/RatingTopProcessing.java @@ -0,0 +1,33 @@ +package org.sadtech.bot.vcs.telegram.service.unit; + +import lombok.RequiredArgsConstructor; +import org.sadtech.bot.vcs.core.domain.entity.Person; +import org.sadtech.bot.vcs.core.exception.NotFoundException; +import org.sadtech.bot.vcs.core.service.PersonService; +import org.sadtech.bot.vcs.core.service.RatingService; +import org.sadtech.social.bot.service.usercode.ProcessingData; +import org.sadtech.social.core.domain.BoxAnswer; +import org.sadtech.social.core.domain.content.Message; +import org.springframework.stereotype.Component; + +/** + * // TODO: 01.10.2020 Добавить описание. + * + * @author upagge 01.10.2020 + */ +@Component +@RequiredArgsConstructor +public class RatingTopProcessing implements ProcessingData { + + private final RatingService ratingService; + private final PersonService personService; + + @Override + public BoxAnswer processing(Message content) { + final Person person = personService.getByTelegramId(content.getPersonId()) + .orElseThrow(() -> new NotFoundException("Пользователь не найден")); + return BoxAnswer.builder() + .message(ratingService.getRatingTop(person.getLogin())) + .build(); + } +} diff --git a/telegram-bot/src/main/java/org/sadtech/bot/vcs/telegram/unit/UnitConfig.java b/telegram-bot/src/main/java/org/sadtech/bot/vcs/telegram/unit/UnitConfig.java index e3307af..a2eca09 100644 --- a/telegram-bot/src/main/java/org/sadtech/bot/vcs/telegram/unit/UnitConfig.java +++ b/telegram-bot/src/main/java/org/sadtech/bot/vcs/telegram/unit/UnitConfig.java @@ -3,6 +3,7 @@ package org.sadtech.bot.vcs.telegram.unit; import lombok.RequiredArgsConstructor; import org.sadtech.bot.vcs.core.service.PersonService; import org.sadtech.bot.vcs.telegram.service.unit.PullRequestProcessing; +import org.sadtech.bot.vcs.telegram.service.unit.RatingTopProcessing; import org.sadtech.bot.vcs.telegram.service.unit.TaskProcessing; import org.sadtech.bot.vcs.telegram.utils.GeneratorKeyBoards; import org.sadtech.social.bot.domain.unit.AnswerCheck; @@ -44,7 +45,8 @@ public class UnitConfig { public AnswerText menu( AnswerProcessing getTasks, AnswerProcessing getPr, - AnswerText settings + AnswerText settings, + AnswerProcessing getTopRating ) { return AnswerText.builder() .boxAnswer( @@ -56,6 +58,7 @@ public class UnitConfig { .nextUnit(getTasks) .nextUnit(getPr) .nextUnit(settings) + .nextUnit(getTopRating) .build(); } @@ -97,6 +100,16 @@ public class UnitConfig { .build(); } + @Bean + AnswerProcessing getTopRating( + RatingTopProcessing ratingTopProcessing + ) { + return AnswerProcessing.builder() + .processingData(ratingTopProcessing) + .keyWord("таблица") + .keyWord("рейтинга") + .build(); + } @Bean public AnswerProcessing noRegister() { diff --git a/telegram-bot/src/main/java/org/sadtech/bot/vcs/telegram/utils/GeneratorKeyBoards.java b/telegram-bot/src/main/java/org/sadtech/bot/vcs/telegram/utils/GeneratorKeyBoards.java index 61bf43e..a5cb80c 100644 --- a/telegram-bot/src/main/java/org/sadtech/bot/vcs/telegram/utils/GeneratorKeyBoards.java +++ b/telegram-bot/src/main/java/org/sadtech/bot/vcs/telegram/utils/GeneratorKeyBoards.java @@ -17,6 +17,7 @@ public class GeneratorKeyBoards { public static KeyBoard menu() { final KeyBoardButtonText tasks = KeyBoardButtonText.builder().label("Мои задачи").build(); final KeyBoardButtonText pr = KeyBoardButtonText.builder().label("Проверить ПР").build(); + final KeyBoardButtonText top = KeyBoardButtonText.builder().label("\uD83C\uDF1F Таблица рейтинга \uD83C\uDF1F").build(); final KeyBoardButtonText settings = KeyBoardButtonText.builder().label("Настройки").build(); final KeyBoardLine oneLine = KeyBoardLine.builder() @@ -25,12 +26,17 @@ public class GeneratorKeyBoards { .build(); final KeyBoardLine twoLine = KeyBoardLine.builder() + .buttonKeyBoard(top) + .build(); + + final KeyBoardLine threeLine = KeyBoardLine.builder() .buttonKeyBoard(settings) .build(); return KeyBoard.builder() .lineKeyBoard(oneLine) .lineKeyBoard(twoLine) + .lineKeyBoard(threeLine) .build(); }