Вынес генерацию уведомлений в NotifyBoxAnswerGenerator
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing

This commit is contained in:
2023-01-15 16:46:32 +03:00
parent 0c0fef6276
commit 246e4235c1
72 changed files with 1354 additions and 726 deletions

View File

@@ -98,3 +98,8 @@ steps:
- echo "$DOCKER_REGISTRY_TOKEN" | docker login docker.io --username $DOCKER_REGISTRY_USER --password-stdin - echo "$DOCKER_REGISTRY_TOKEN" | docker login docker.io --username $DOCKER_REGISTRY_USER --password-stdin
- docker buildx create --use - docker buildx create --use
- docker buildx build --push --platform linux/amd64,linux/arm64/v8 -t "$DOCKER_IMAGE_NAME:latest" -t "$DOCKER_IMAGE_NAME:$DRONE_TAG" . - docker buildx build --push --platform linux/amd64,linux/arm64/v8 -t "$DOCKER_IMAGE_NAME:latest" -t "$DOCKER_IMAGE_NAME:$DRONE_TAG" .
---
kind: signature
hmac: 1bb00c9edf1c37a2f36c92ea6c1cb9023630b05550b4dc3148c6f5b30cad4af6
...

View File

@@ -1,20 +1,28 @@
package dev.struchkov.bot.gitlab.context.domain; package dev.struchkov.bot.gitlab.context.domain;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/** /**
* @author upagge 17.01.2021 * @author upagge 17.01.2021
*/ */
@Getter
@RequiredArgsConstructor
public enum PipelineStatus { public enum PipelineStatus {
CREATED, CREATED("\uD83C\uDD95"),
WAITING_FOR_RESOURCE, WAITING_FOR_RESOURCE("\uD83D\uDCA2"),
PREPARING, PREPARING("♿️"),
PENDING, PENDING("⚠️"),
RUNNING, RUNNING("\uD83D\uDD04"),
SUCCESS, SUCCESS(""),
FAILED, FAILED(""),
CANCELED, CANCELED("\uD83D\uDEAB"),
SKIPPED, SKIPPED("\uD83D\uDD18"),
MANUAL, MANUAL("\uD83D\uDD79"),
SCHEDULED SCHEDULED("\uD83D\uDD52"),
NULL("\uD83C\uDD95");
private final String icon;
} }

View File

@@ -26,4 +26,7 @@ public class AppSetting {
@Column(name = "first_start") @Column(name = "first_start")
private boolean firstStart; private boolean firstStart;
@Column(name = "enable_notify")
private boolean enableNotify;
} }

View File

@@ -38,7 +38,7 @@ public class Discussion {
@Column(name = "resolved") @Column(name = "resolved")
private Boolean resolved; private Boolean resolved;
@ManyToOne @ManyToOne(optional = false)
@JoinTable( @JoinTable(
name = "discussion_merge_request", name = "discussion_merge_request",
joinColumns = @JoinColumn(name = "discussion_id"), joinColumns = @JoinColumn(name = "discussion_id"),

View File

@@ -1,35 +1,37 @@
package dev.struchkov.bot.gitlab.context.domain.notify; package dev.struchkov.bot.gitlab.context.domain.notify;
import dev.struchkov.bot.gitlab.context.utils.Smile;
import dev.struchkov.haiti.utils.Strings;
import lombok.Builder; import lombok.Builder;
import lombok.Getter;
import java.text.MessageFormat;
import static dev.struchkov.haiti.utils.Strings.escapeMarkdown;
/** /**
* @author upagge 15.01.2021 * @author upagge 15.01.2021
*/ */
public record NewProjectNotify( @Getter
public final class NewProjectNotify implements Notify {
public static final String TYPE = "NewProjectNotify";
private final String projectName;
private final String projectUrl;
private final String projectDescription;
private final String authorName;
@Builder
public NewProjectNotify(
String projectName, String projectName,
String projectUrl, String projectUrl,
String projectDescription, String projectDescription,
String authorName String authorName
) implements Notify { ) {
this.projectName = projectName;
@Builder this.projectUrl = projectUrl;
public NewProjectNotify { this.projectDescription = projectDescription;
this.authorName = authorName;
} }
@Override @Override
public String generateMessage() { public String getType() {
return MessageFormat.format( return TYPE;
"{0} *New project*{1}[{2}]({3}){1}{4}{5}: {6}",
Smile.FUN.getValue(), Smile.HR.getValue(), projectName, projectUrl,
(projectDescription != null && !"".equals(projectDescription)) ? escapeMarkdown(projectDescription) + Smile.HR : Strings.EMPTY,
Smile.AUTHOR.getValue(), authorName
);
} }
} }

View File

@@ -2,6 +2,6 @@ package dev.struchkov.bot.gitlab.context.domain.notify;
public interface Notify { public interface Notify {
String generateMessage(); String getType();
} }

View File

@@ -1,19 +0,0 @@
package dev.struchkov.bot.gitlab.context.domain.notify;
import lombok.Builder;
/**
* @author upagge 20.09.2020
*/
public record SimpleTextNotify(String message) implements Notify {
@Builder
public SimpleTextNotify {
}
@Override
public String generateMessage() {
return message;
}
}

View File

@@ -1,52 +0,0 @@
package dev.struchkov.bot.gitlab.context.domain.notify.comment;
import dev.struchkov.bot.gitlab.context.domain.Answer;
import dev.struchkov.bot.gitlab.context.domain.notify.Notify;
import dev.struchkov.bot.gitlab.context.utils.Smile;
import dev.struchkov.haiti.utils.Strings;
import lombok.Builder;
import lombok.Getter;
import java.text.MessageFormat;
import java.util.List;
import java.util.stream.Collectors;
import static dev.struchkov.haiti.utils.Strings.TWO_NEW_LINE;
import static dev.struchkov.haiti.utils.Strings.escapeMarkdown;
@Getter
public class AnswerCommentNotify implements Notify {
private final String youMessage;
private final String url;
private final List<Answer> answers;
@Builder
protected AnswerCommentNotify(
String youMessage,
String url,
List<Answer> answers
) {
this.youMessage = youMessage;
this.url = url;
this.answers = answers;
}
@Override
public String generateMessage() {
final String answerText = answers.stream()
.map(answer -> answer.getAuthorName() + ": " + answer.getMessage().substring(0, Math.min(answer.getMessage().length(), 500)))
.collect(Collectors.joining(TWO_NEW_LINE));
return MessageFormat.format(
"{0} *Новые ответы* на [комментарий]({1}){2}" +
"{3}{2}" +
"{4}",
Smile.COMMENT,
url,
Smile.HR,
escapeMarkdown(Strings.cutoff(youMessage, 180)),
escapeMarkdown(answerText)
);
}
}

View File

@@ -1,14 +1,24 @@
package dev.struchkov.bot.gitlab.context.domain.notify.comment; package dev.struchkov.bot.gitlab.context.domain.notify.comment;
import dev.struchkov.bot.gitlab.context.domain.notify.Notify; import dev.struchkov.bot.gitlab.context.domain.notify.Notify;
import dev.struchkov.bot.gitlab.context.utils.Smile;
import lombok.Builder; import lombok.Builder;
import lombok.Getter;
import static dev.struchkov.haiti.utils.Checker.checkNotNull; @Getter
import static dev.struchkov.haiti.utils.Strings.escapeMarkdown; public final class NewCommentNotify implements Notify {
@Builder public static final String TYPE = "NewCommentNotify";
public record NewCommentNotify(
private final String url;
private final String discussionMessage;
private final String discussionAuthor;
private final String previousMessage;
private final String previousAuthor;
private final String authorName;
private final String message;
@Builder
public NewCommentNotify(
String url, String url,
String discussionMessage, String discussionMessage,
String discussionAuthor, String discussionAuthor,
@@ -16,26 +26,20 @@ public record NewCommentNotify(
String previousAuthor, String previousAuthor,
String authorName, String authorName,
String message String message
) implements Notify { ) {
this.url = url;
this.discussionMessage = discussionMessage;
this.discussionAuthor = discussionAuthor;
this.previousMessage = previousMessage;
this.previousAuthor = previousAuthor;
this.authorName = authorName;
this.message = message;
}
@Override @Override
public String generateMessage() { public String getType() {
return TYPE;
final StringBuilder builder = new StringBuilder(Smile.COMMENT.getValue()).append(" [New answer in discussion](").append(url).append(")\n--- --- --- ---");
if (checkNotNull(discussionMessage)) {
builder.append("\n-- -- discussion first message -- --\n")
.append("*").append(discussionAuthor).append("*: ").append(escapeMarkdown(discussionMessage));
}
if (checkNotNull(previousMessage)) {
builder.append("\n-- -- -- previous message -- -- --\n")
.append("*").append(previousAuthor).append("*: ").append(escapeMarkdown(previousMessage));
}
builder.append("\n-- -- -- --- new answer --- -- -- --\n")
.append("*").append(authorName).append("*: ").append(escapeMarkdown(message));
return builder.toString();
} }
} }

View File

@@ -1,14 +1,13 @@
package dev.struchkov.bot.gitlab.context.domain.notify.mergerequest; package dev.struchkov.bot.gitlab.context.domain.notify.mergerequest;
import dev.struchkov.bot.gitlab.context.utils.Smile;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import java.text.MessageFormat;
@Getter @Getter
public class ConflictPrNotify extends PrNotify { public class ConflictPrNotify extends PrNotify {
public static final String TYPE = "ConflictPrNotify";
private final String sourceBranch; private final String sourceBranch;
@Builder @Builder
@@ -23,11 +22,8 @@ public class ConflictPrNotify extends PrNotify {
} }
@Override @Override
public String generateMessage() { public String getType() {
return MessageFormat.format( return TYPE;
"{0} *Attention! MergeRequest conflict | {4}*{1}[{2}]({3})",
Smile.DANGEROUS.getValue(), Smile.HR.getValue(), title, url, projectName, sourceBranch
);
} }
} }

View File

@@ -1,36 +0,0 @@
package dev.struchkov.bot.gitlab.context.domain.notify.mergerequest;
import dev.struchkov.bot.gitlab.context.utils.Smile;
import lombok.Builder;
import lombok.Getter;
import java.text.MessageFormat;
/**
* // TODO: 11.10.2020 Добавить описание.
*
* @author upagge 11.10.2020
*/
@Getter
//TODO [28.01.2022]: Решить доработать и оставить или удалить
public class ForgottenSmartPrNotify extends PrNotify {
@Builder
protected ForgottenSmartPrNotify(
String title,
String url,
String projectName,
String repositorySlug
) {
super(projectName, title, url);
}
@Override
public String generateMessage() {
return MessageFormat.format(
"{0} *MergeRequest Review Reminder | {4}*{3}[{1}]({2})",
Smile.SMART.getValue(), title, url, Smile.HR.getValue(), projectName
);
}
}

View File

@@ -1,19 +1,15 @@
package dev.struchkov.bot.gitlab.context.domain.notify.mergerequest; package dev.struchkov.bot.gitlab.context.domain.notify.mergerequest;
import dev.struchkov.bot.gitlab.context.utils.Smile;
import dev.struchkov.haiti.utils.Strings;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import java.text.MessageFormat;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import static dev.struchkov.haiti.utils.Strings.escapeMarkdown;
@Getter @Getter
public class NewPrNotify extends PrNotify { public class NewPrNotify extends PrNotify {
public static final String TYPE = "NewPrNotify";
private final String description; private final String description;
private final String author; private final String author;
private final String targetBranch; private final String targetBranch;
@@ -40,29 +36,8 @@ public class NewPrNotify extends PrNotify {
} }
@Override @Override
public String generateMessage() { public String getType() {
String labelText = labels.stream() return TYPE;
.map(label -> "#" + label)
.collect(Collectors.joining(" "));
if (!labelText.isEmpty()) {
labelText = "\n\n" + labelText;
}
return MessageFormat.format(
"{0} *New merge request for review | {1}*{2}[{3}]({4}){5}{2}{9}: {10} {12} {11}\n{7}: {8}",
Smile.FUN.getValue(),
projectName,
Smile.HR.getValue(),
title,
url,
labelText,
(description != null && !"".equals(description)) ? escapeMarkdown(description) + Smile.HR : Strings.EMPTY,
Smile.AUTHOR.getValue(),
author,
Smile.TREE.getValue(),
sourceBranch,
targetBranch,
Smile.ARROW.getValue()
);
} }
} }

View File

@@ -1,15 +1,14 @@
package dev.struchkov.bot.gitlab.context.domain.notify.mergerequest; package dev.struchkov.bot.gitlab.context.domain.notify.mergerequest;
import dev.struchkov.bot.gitlab.context.domain.MergeRequestState; import dev.struchkov.bot.gitlab.context.domain.MergeRequestState;
import dev.struchkov.bot.gitlab.context.utils.Smile;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import java.text.MessageFormat;
@Getter @Getter
public class StatusPrNotify extends PrNotify { public class StatusPrNotify extends PrNotify {
public static final String TYPE = "StatusPrNotify";
private final MergeRequestState oldStatus; private final MergeRequestState oldStatus;
private final MergeRequestState newStatus; private final MergeRequestState newStatus;
@@ -27,11 +26,8 @@ public class StatusPrNotify extends PrNotify {
} }
@Override @Override
public String generateMessage() { public String getType() {
return MessageFormat.format( return TYPE;
"{0} *MergeRequest status changed | {7}*{1}[{2}]({3}){1}{4} {5} {6}",
Smile.PEN.getValue(), Smile.HR.getValue(), title, url, oldStatus.name(), Smile.ARROW.getValue(), newStatus.name(), projectName
);
} }
} }

View File

@@ -1,12 +1,13 @@
package dev.struchkov.bot.gitlab.context.domain.notify.mergerequest; package dev.struchkov.bot.gitlab.context.domain.notify.mergerequest;
import dev.struchkov.bot.gitlab.context.utils.Smile;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
@Getter @Getter
public class UpdatePrNotify extends PrNotify { public class UpdatePrNotify extends PrNotify {
public static final String TYPE = "UpdatePrNotify";
private final String author; private final String author;
private final Long allTasks; private final Long allTasks;
private final Long allResolvedTasks; private final Long allResolvedTasks;
@@ -32,26 +33,10 @@ public class UpdatePrNotify extends PrNotify {
this.personResolvedTasks = personResolvedTasks; this.personResolvedTasks = personResolvedTasks;
} }
@Override @Override
public String generateMessage() { public String getType() {
final StringBuilder builder = new StringBuilder(Smile.UPDATE.getValue()).append(" *MergeRequest update | ").append(projectName).append("*") return TYPE;
.append(Smile.HR.getValue())
.append("[").append(title).append("](").append(url).append(")");
if (allTasks > 0) {
builder.append(Smile.HR.getValue())
.append("All tasks: ").append(allResolvedTasks).append("/").append(allTasks);
if (personTasks > 0) {
builder.append("\nYour tasks: ").append(personResolvedTasks).append("/").append(personTasks);
}
}
builder.append(Smile.HR.getValue())
.append(Smile.AUTHOR.getValue()).append(": ").append(author);
return builder.toString();
} }
} }

View File

@@ -1,43 +1,47 @@
package dev.struchkov.bot.gitlab.context.domain.notify.pipeline; package dev.struchkov.bot.gitlab.context.domain.notify.pipeline;
import dev.struchkov.bot.gitlab.context.domain.PipelineStatus;
import dev.struchkov.bot.gitlab.context.domain.notify.Notify; import dev.struchkov.bot.gitlab.context.domain.notify.Notify;
import dev.struchkov.bot.gitlab.context.utils.Smile;
import lombok.Builder; import lombok.Builder;
import lombok.Getter;
import java.text.MessageFormat;
import static dev.struchkov.haiti.utils.Strings.escapeMarkdown;
/** /**
* @author upagge 17.01.2021 * @author upagge 17.01.2021
*/ */
//TODO [16.12.2022|uPagge]: Нужно реализовать заполнение projectName //TODO [16.12.2022|uPagge]: Нужно реализовать заполнение projectName
public record PipelineNotify(
Long pipelineId, @Getter
String projectName, public final class PipelineNotify implements Notify {
String refName,
String oldStatus, public static final String TYPE = "PipelineNotify";
String newStatus,
String webUrl private final Long projectId;
) implements Notify { private final Long pipelineId;
private final String refName;
private final PipelineStatus oldStatus;
private final PipelineStatus newStatus;
private final String webUrl;
@Builder @Builder
public PipelineNotify { public PipelineNotify(
Long projectId,
Long pipelineId,
String refName,
PipelineStatus oldStatus,
PipelineStatus newStatus,
String webUrl
) {
this.projectId = projectId;
this.pipelineId = pipelineId;
this.refName = refName;
this.oldStatus = oldStatus;
this.newStatus = newStatus;
this.webUrl = webUrl;
} }
@Override @Override
public String generateMessage() { public String getType() {
return MessageFormat.format( return TYPE;
"{0} *Pipeline {1,number,#}* | {2}{3}[{4}]({5}){3}{6} {7} {8}",
Smile.BUILD,
pipelineId,
escapeMarkdown(projectName),
Smile.HR,
refName,
webUrl,
oldStatus,
Smile.ARROW,
newStatus
);
} }
} }

View File

@@ -1,16 +1,11 @@
package dev.struchkov.bot.gitlab.context.domain.notify.task; package dev.struchkov.bot.gitlab.context.domain.notify.task;
import dev.struchkov.bot.gitlab.context.utils.Smile;
import dev.struchkov.haiti.utils.Pair; import dev.struchkov.haiti.utils.Pair;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.Singular; import lombok.Singular;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import static dev.struchkov.haiti.utils.Checker.checkNotNull;
import static dev.struchkov.haiti.utils.Strings.escapeMarkdown;
/** /**
* @author upagge 10.09.2020 * @author upagge 10.09.2020
@@ -18,6 +13,8 @@ import static dev.struchkov.haiti.utils.Strings.escapeMarkdown;
@Getter @Getter
public class DiscussionNewNotify extends TaskNotify { public class DiscussionNewNotify extends TaskNotify {
public static final String TYPE = "DiscussionNewNotify";
private final String mrName; private final String mrName;
private final List<Pair<String, String>> notes; private final List<Pair<String, String>> notes;
@@ -35,25 +32,8 @@ public class DiscussionNewNotify extends TaskNotify {
} }
@Override @Override
public String generateMessage() { public String getType() {
final StringBuilder builder = new StringBuilder(Smile.TASK.getValue()).append(" [New discussion](").append(url).append(")") return TYPE;
.append(Smile.HR.getValue())
.append(escapeMarkdown(mrName))
.append(Smile.HR.getValue())
.append("*").append(authorName).append("*: ").append(escapeMarkdown(messageTask));
if (checkNotNull(notes)) {
builder.append("\n-- -- -- -- comments -- -- -- --\n")
.append(convertNotes(notes));
}
return builder.toString();
}
private String convertNotes(List<Pair<String, String>> notes) {
return notes.stream()
.map(note -> "*" + note.getKey() + "*: " + note.getValue())
.collect(Collectors.joining("\n"));
} }
} }

View File

@@ -1,17 +1,16 @@
package dev.struchkov.bot.gitlab.context.domain.notify.task; package dev.struchkov.bot.gitlab.context.domain.notify.task;
import dev.struchkov.bot.gitlab.context.utils.Smile;
import lombok.Builder; import lombok.Builder;
import lombok.Getter;
import java.text.MessageFormat;
import static dev.struchkov.haiti.utils.Strings.escapeMarkdown;
/** /**
* @author upagge 10.09.2020 * @author upagge 10.09.2020
*/ */
@Getter
public class TaskCloseNotify extends TaskNotify { public class TaskCloseNotify extends TaskNotify {
public static final String TYPE = "TaskCloseNotify";
private final Long personTasks; private final Long personTasks;
private final Long personResolvedTasks; private final Long personResolvedTasks;
@@ -29,11 +28,8 @@ public class TaskCloseNotify extends TaskNotify {
} }
@Override @Override
public String generateMessage() { public String getType() {
return MessageFormat.format( return TYPE;
"{0} *Closed [task]({1}){2}*{3}*: {4}",
Smile.TASK.getValue(), url, Smile.HR.getValue(), authorName, escapeMarkdown(messageTask), personTasks, personResolvedTasks
);
} }
} }

View File

@@ -24,4 +24,6 @@ public interface DiscussionRepository {
List<Discussion> findAllById(Set<String> discussionIds); List<Discussion> findAllById(Set<String> discussionIds);
Set<String> findAllIds();
} }

View File

@@ -26,4 +26,6 @@ public interface MergeRequestRepository {
void deleteByStates(Set<MergeRequestState> states); void deleteByStates(Set<MergeRequestState> states);
Set<Long> findAllIds();
} }

View File

@@ -23,4 +23,6 @@ public interface PipelineRepository {
void deleteByCreatedBefore(LocalDateTime date); void deleteByCreatedBefore(LocalDateTime date);
Set<Long> findAllIds();
} }

View File

@@ -25,4 +25,6 @@ public interface ProjectRepository {
Set<Long> findAllIds(); Set<Long> findAllIds();
Optional<String> findProjectNameById(Long projectId);
} }

View File

@@ -21,4 +21,8 @@ public interface AppSettingService {
*/ */
void disableFirstStart(); void disableFirstStart();
boolean isEnableAllNotify();
void turnOnAllNotify();
} }

View File

@@ -37,4 +37,6 @@ public interface DiscussionService {
List<Discussion> getAll(); List<Discussion> getAll();
Set<String> getAllIds();
} }

View File

@@ -36,4 +36,6 @@ public interface MergeRequestsService {
void cleanOld(); void cleanOld();
Set<Long> getAllIds();
} }

View File

@@ -12,8 +12,4 @@ public interface NotifyService {
<T extends Notify> void send(T notify); <T extends Notify> void send(T notify);
void enableAllNotify();
void disableAllNotify();
} }

View File

@@ -29,4 +29,6 @@ public interface PipelineService {
void cleanOld(); void cleanOld();
Set<Long> getAllIds();
} }

View File

@@ -5,6 +5,7 @@ import dev.struchkov.bot.gitlab.context.domain.entity.Project;
import lombok.NonNull; import lombok.NonNull;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.Set; import java.util.Set;
/** /**
@@ -25,4 +26,7 @@ public interface ProjectService {
ExistContainer<Project, Long> existsById(Set<Long> projectIds); ExistContainer<Project, Long> existsById(Set<Long> projectIds);
Set<Long> getAllIds(); Set<Long> getAllIds();
Optional<String> getProjectNameById(Long projectId);
} }

View File

@@ -0,0 +1,31 @@
package dev.struchkov.bot.gitlab.context.utils;
import static dev.struchkov.haiti.utils.Exceptions.utilityClass;
import static dev.struchkov.haiti.utils.Strings.escapeMarkdown;
public class Icons {
public static final String HR = "\n-- -- -- -- --\n";
public static final String FUN = "\uD83C\uDF89";
public static final String VIEW = "\uD83D\uDC40";
public static final String TREE = "\uD83C\uDF33";
public static final String AUTHOR = "\uD83D\uDC68\u200D\uD83D\uDCBB";
public static final String UPDATE = "\uD83D\uDD04";
public static final String COMMENT = "\uD83D\uDCAC";
public static final String TASK = "\uD83D\uDCBC";
public static final String ARROW = "";
public static final String DANGEROUS = "⚠️";
public static final String PEN = "✏️";
public static final String BUILD = "\uD83D\uDEE0";
public static final String LINK = "\uD83D\uDD17";
private Icons() {
utilityClass();
}
public static String link(String title, String url) {
return "[" + escapeMarkdown(title) + "](" + url + ")";
}
}

View File

@@ -1,55 +0,0 @@
package dev.struchkov.bot.gitlab.context.utils;
import dev.struchkov.bot.gitlab.context.domain.entity.MergeRequest;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* Генерирует сообщения для отправки.
*
* @author upagge [07.02.2020]
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class MessageUtils {
public static Optional<String> mergeRequestForReview(@NonNull List<MergeRequest> mergeRequestsReviews) {
if (!mergeRequestsReviews.isEmpty()) {
return Optional.of(
mergeRequestsReviews.stream()
.map(MessageUtils::generateStringItemMergeRequestReview)
.collect(Collectors.joining("\n"))
);
}
return Optional.empty();
}
public static Optional<String> mergeRequestForNeedWork(@NonNull List<MergeRequest> mergeRequestNeedWork) {
if (!mergeRequestNeedWork.isEmpty()) {
return Optional.of(
mergeRequestNeedWork.stream()
.map(MessageUtils::generateStringItemMergeRequestNeedWork)
.collect(Collectors.joining("\n"))
);
}
return Optional.empty();
}
private static String generateStringItemMergeRequestNeedWork(MergeRequest mergeRequest) {
return "-- " + link(mergeRequest.getTitle(), mergeRequest.getWebUrl());
}
private static String generateStringItemMergeRequestReview(MergeRequest mergeRequest) {
return Smile.statusPr(mergeRequest.getUpdatedDate()) + " " +
link(mergeRequest.getTitle(), mergeRequest.getWebUrl());
}
private static String link(String name, String url) {
return "[" + name + "](" + url + ")";
}
}

View File

@@ -3,10 +3,6 @@ package dev.struchkov.bot.gitlab.context.utils;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
@AllArgsConstructor @AllArgsConstructor
public enum Smile { public enum Smile {
@@ -47,15 +43,6 @@ public enum Smile {
@Getter @Getter
private final String value; private final String value;
public static Smile statusPr(LocalDateTime updateDate) {
int periodDay = Period.between(updateDate.toLocalDate(), LocalDate.now()).getDays();
if (periodDay < 5) {
return Smile.valueOf("DAY_" + periodDay);
} else {
return Smile.DAY_5;
}
}
@Override @Override
public String toString() { public String toString() {
return value; return value;

View File

@@ -5,8 +5,8 @@ import dev.struchkov.bot.gitlab.context.repository.AppSettingRepository;
import dev.struchkov.bot.gitlab.context.service.AppSettingService; import dev.struchkov.bot.gitlab.context.service.AppSettingService;
import dev.struchkov.haiti.context.exception.NotFoundException; import dev.struchkov.haiti.context.exception.NotFoundException;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.function.Supplier; import java.util.function.Supplier;
@@ -25,18 +25,30 @@ public class AppSettingServiceImpl implements AppSettingService {
public static final Supplier<NotFoundException> NOT_FOUND_SETTINGS = notFoundException("Ошибка, невозможно найти настройки приложения, проверьте базу данных."); public static final Supplier<NotFoundException> NOT_FOUND_SETTINGS = notFoundException("Ошибка, невозможно найти настройки приложения, проверьте базу данных.");
private final AppSettingRepository appSettingRepository; private final AppSettingRepository appSettingRepository;
private final MessageSource messageSource;
@Override @Override
@Transactional(readOnly = true)
public boolean isFirstStart() { public boolean isFirstStart() {
return getAppSetting().isFirstStart(); return getAppSetting().isFirstStart();
} }
@Override @Override
@Transactional
public void disableFirstStart() { public void disableFirstStart() {
final AppSetting appSetting = getAppSetting(); final AppSetting appSetting = getAppSetting();
appSetting.setFirstStart(false); appSetting.setFirstStart(false);
appSettingRepository.save(appSetting); }
@Override
@Transactional(readOnly = true)
public boolean isEnableAllNotify() {
return getAppSetting().isEnableNotify();
}
@Override
@Transactional
public void turnOnAllNotify() {
final AppSetting appSetting = getAppSetting();
appSetting.setEnableNotify(true);
} }
private AppSetting getAppSetting() { private AppSetting getAppSetting() {

View File

@@ -300,6 +300,11 @@ public class DiscussionServiceImpl implements DiscussionService {
return repository.findAll(); return repository.findAll();
} }
@Override
public Set<String> getAllIds() {
return repository.findAllIds();
}
/** /**
* Уведомляет пользователя, если его никнейм упоминается в комментарии. * Уведомляет пользователя, если его никнейм упоминается в комментарии.
*/ */

View File

@@ -250,6 +250,12 @@ public class MergeRequestsServiceImpl implements MergeRequestsService {
log.debug("Конец очистки старых MR"); log.debug("Конец очистки старых MR");
} }
@Override
@Transactional(readOnly = true)
public Set<Long> getAllIds() {
return repository.findAllIds();
}
private void notifyAboutUpdate(MergeRequest oldMergeRequest, MergeRequest mergeRequest, Project project) { private void notifyAboutUpdate(MergeRequest oldMergeRequest, MergeRequest mergeRequest, Project project) {
final Long botUserGitlabId = personInformation.getId(); final Long botUserGitlabId = personInformation.getId();

View File

@@ -1,33 +1,31 @@
package dev.struchkov.bot.gitlab.core.service.impl; package dev.struchkov.bot.gitlab.core.service.impl;
import dev.struchkov.bot.gitlab.context.domain.notify.Notify; import dev.struchkov.bot.gitlab.context.domain.notify.Notify;
import dev.struchkov.bot.gitlab.context.service.AppSettingService;
import dev.struchkov.bot.gitlab.context.service.MessageSendService; import dev.struchkov.bot.gitlab.context.service.MessageSendService;
import dev.struchkov.bot.gitlab.context.service.NotifyService; import dev.struchkov.bot.gitlab.context.service.NotifyService;
import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Service @Service
@RequiredArgsConstructor
public class NotifyServiceImpl implements NotifyService { public class NotifyServiceImpl implements NotifyService {
private boolean enableAllNotify = true;
private final MessageSendService messageSendService; private final MessageSendService messageSendService;
private final AppSettingService settingService;
public NotifyServiceImpl(
@Lazy MessageSendService messageSendService,
AppSettingService settingService
) {
this.messageSendService = messageSendService;
this.settingService = settingService;
}
@Override @Override
public <T extends Notify> void send(T notify) { public <T extends Notify> void send(T notify) {
if (enableAllNotify) { if (settingService.isEnableAllNotify()) {
messageSendService.send(notify); messageSendService.send(notify);
} }
} }
@Override
public void enableAllNotify() {
enableAllNotify = true;
}
@Override
public void disableAllNotify() {
enableAllNotify = false;
}
} }

View File

@@ -49,7 +49,7 @@ public class PipelineServiceImpl implements PipelineService {
@Transactional @Transactional
public Pipeline create(@NonNull Pipeline pipeline) { public Pipeline create(@NonNull Pipeline pipeline) {
final Pipeline newPipeline = repository.save(pipeline); final Pipeline newPipeline = repository.save(pipeline);
notifyNewPipeline(pipeline, "n/a"); notifyNewPipeline(pipeline, PipelineStatus.NULL);
return newPipeline; return newPipeline;
} }
@@ -61,11 +61,12 @@ public class PipelineServiceImpl implements PipelineService {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
private void notifyNewPipeline(Pipeline pipeline, String oldStatus) { private void notifyNewPipeline(Pipeline pipeline, PipelineStatus oldStatus) {
if (isNeedNotifyNewPipeline(pipeline)) { if (isNeedNotifyNewPipeline(pipeline)) {
notifyService.send( notifyService.send(
PipelineNotify.builder() PipelineNotify.builder()
.newStatus(pipeline.getStatus().name()) .projectId(pipeline.getProjectId())
.newStatus(pipeline.getStatus())
.pipelineId(pipeline.getId()) .pipelineId(pipeline.getId())
.refName(pipeline.getRef()) .refName(pipeline.getRef())
.webUrl(pipeline.getWebUrl()) .webUrl(pipeline.getWebUrl())
@@ -83,7 +84,7 @@ public class PipelineServiceImpl implements PipelineService {
pipeline.setProjectId(oldPipeline.getProjectId()); pipeline.setProjectId(oldPipeline.getProjectId());
if (!oldPipeline.getUpdated().equals(pipeline.getUpdated())) { if (!oldPipeline.getUpdated().equals(pipeline.getUpdated())) {
notifyNewPipeline(pipeline, oldPipeline.getStatus().name()); notifyNewPipeline(pipeline, oldPipeline.getStatus());
return repository.save(pipeline); return repository.save(pipeline);
} }
@@ -131,4 +132,9 @@ public class PipelineServiceImpl implements PipelineService {
log.debug("Конец очистки старых пайплайнов"); log.debug("Конец очистки старых пайплайнов");
} }
@Override
public Set<Long> getAllIds() {
return repository.findAllIds();
}
} }

View File

@@ -14,6 +14,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -92,6 +93,11 @@ public class ProjectServiceImpl implements ProjectService {
return repository.findAllIds(); return repository.findAllIds();
} }
@Override
public Optional<String> getProjectNameById(@NonNull Long projectId) {
return repository.findProjectNameById(projectId);
}
private void notifyAboutNewProject(Project newProject, String authorName) { private void notifyAboutNewProject(Project newProject, String authorName) {
notifyService.send( notifyService.send(
NewProjectNotify.builder() NewProjectNotify.builder()

View File

@@ -29,7 +29,7 @@ public class GetAllDiscussionForMergeRequestTask extends RecursiveTask<List<Disc
@Override @Override
@SneakyThrows @SneakyThrows
protected List<DiscussionJson> compute() { protected List<DiscussionJson> compute() {
Thread.sleep(100); Thread.sleep(200);
final List<DiscussionJson> jsons = getDiscussionJson(); final List<DiscussionJson> jsons = getDiscussionJson();
if (checkNotEmpty(jsons) && jsons.size() == PAGE_COUNT) { if (checkNotEmpty(jsons) && jsons.size() == PAGE_COUNT) {
final var newTask = new GetAllDiscussionForMergeRequestTask(discussionsUrl, projectId, mergeRequestTwoId, personalGitlabToken, page + 1); final var newTask = new GetAllDiscussionForMergeRequestTask(discussionsUrl, projectId, mergeRequestTwoId, personalGitlabToken, page + 1);

View File

@@ -30,7 +30,7 @@ public class GetAllMergeRequestForProjectTask extends RecursiveTask<List<MergeRe
@Override @Override
@SneakyThrows @SneakyThrows
protected List<MergeRequestJson> compute() { protected List<MergeRequestJson> compute() {
Thread.sleep(100); Thread.sleep(200);
final List<MergeRequestJson> mergeRequestJsons = getMergeRequestJsons(); final List<MergeRequestJson> mergeRequestJsons = getMergeRequestJsons();
if (checkNotEmpty(mergeRequestJsons) && mergeRequestJsons.size() == PAGE_COUNT) { if (checkNotEmpty(mergeRequestJsons) && mergeRequestJsons.size() == PAGE_COUNT) {
final GetAllMergeRequestForProjectTask newTask = new GetAllMergeRequestForProjectTask(projectId, pageNumber + 1, urlMrOpen, gitlabToken); final GetAllMergeRequestForProjectTask newTask = new GetAllMergeRequestForProjectTask(projectId, pageNumber + 1, urlMrOpen, gitlabToken);

View File

@@ -31,7 +31,7 @@ public class GetPipelineShortTask extends RecursiveTask<List<PipelineShortJson>>
@Override @Override
@SneakyThrows @SneakyThrows
protected List<PipelineShortJson> compute() { protected List<PipelineShortJson> compute() {
Thread.sleep(100); Thread.sleep(200);
final List<PipelineShortJson> jsons = getPipelineJsons(); final List<PipelineShortJson> jsons = getPipelineJsons();
if (jsons.size() == PAGE_COUNT) { if (jsons.size() == PAGE_COUNT) {
final GetPipelineShortTask newTask = new GetPipelineShortTask(urlPipelines, projectId, pageNumber + 1, lastUpdate, gitlabToken); final GetPipelineShortTask newTask = new GetPipelineShortTask(urlPipelines, projectId, pageNumber + 1, lastUpdate, gitlabToken);

View File

@@ -25,7 +25,7 @@ public class GetPipelineTask extends RecursiveTask<Optional<PipelineJson>> {
@Override @Override
@SneakyThrows @SneakyThrows
protected Optional<PipelineJson> compute() { protected Optional<PipelineJson> compute() {
Thread.sleep(100); Thread.sleep(200);
return HttpParse.request(MessageFormat.format(urlPipeline, projectId, pipelineId)) return HttpParse.request(MessageFormat.format(urlPipeline, projectId, pipelineId))
.header(ACCEPT) .header(ACCEPT)
.header(StringUtils.H_PRIVATE_TOKEN, gitlabToken) .header(StringUtils.H_PRIVATE_TOKEN, gitlabToken)

View File

@@ -25,7 +25,7 @@ public class GetSingleMergeRequestTask extends RecursiveTask<Optional<MergeReque
@Override @Override
@SneakyThrows @SneakyThrows
protected Optional<MergeRequestJson> compute() { protected Optional<MergeRequestJson> compute() {
Thread.sleep(100); Thread.sleep(200);
final String mrUrl = MessageFormat.format(urlMr, projectId, mrTwoId); final String mrUrl = MessageFormat.format(urlMr, projectId, mrTwoId);
return HttpParse.request(mrUrl) return HttpParse.request(mrUrl)
.header(ACCEPT) .header(ACCEPT)

View File

@@ -47,4 +47,9 @@ public class DiscussionRepositoryImpl implements DiscussionRepository {
return jpaRepository.findAllById(discussionIds); return jpaRepository.findAllById(discussionIds);
} }
@Override
public Set<String> findAllIds() {
return jpaRepository.findAllIds();
}
} }

View File

@@ -65,4 +65,9 @@ public class MergeRequestRepositoryImpl implements MergeRequestRepository {
jpaRepository.deleteAllByStateIn(states); jpaRepository.deleteAllByStateIn(states);
} }
@Override
public Set<Long> findAllIds() {
return jpaRepository.findAllIds();
}
} }

View File

@@ -48,4 +48,9 @@ public class PipelineRepositoryImpl implements PipelineRepository {
jpaRepository.deleteAllByCreatedBefore(date); jpaRepository.deleteAllByCreatedBefore(date);
} }
@Override
public Set<Long> findAllIds() {
return jpaRepository.findAllIds();
}
} }

View File

@@ -51,4 +51,9 @@ public class ProjectRepositoryImpl implements ProjectRepository {
return jpaRepository.findAllIds(); return jpaRepository.findAllIds();
} }
@Override
public Optional<String> findProjectNameById(Long projectId) {
return jpaRepository.findProjectNameById(projectId);
}
} }

View File

@@ -2,8 +2,10 @@ package dev.struchkov.bot.gitlab.data.jpa;
import dev.struchkov.bot.gitlab.context.domain.entity.Discussion; import dev.struchkov.bot.gitlab.context.domain.entity.Discussion;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List; import java.util.List;
import java.util.Set;
/** /**
* @author upagge 11.02.2021 * @author upagge 11.02.2021
@@ -15,4 +17,7 @@ public interface DiscussionJpaRepository extends JpaRepository<Discussion, Strin
*/ */
List<Discussion> findAllByMergeRequestId(Long mergeRequestId); List<Discussion> findAllByMergeRequestId(Long mergeRequestId);
@Query("SELECT d.id FROM Discussion d")
Set<String> findAllIds();
} }

View File

@@ -24,4 +24,7 @@ public interface MergeRequestJpaRepository extends JpaRepositoryImplementation<M
void deleteAllByStateIn(Set<MergeRequestState> states); void deleteAllByStateIn(Set<MergeRequestState> states);
@Query("SELECT mr.id FROM MergeRequest mr")
Set<Long> findAllIds();
} }

View File

@@ -2,6 +2,7 @@ package dev.struchkov.bot.gitlab.data.jpa;
import dev.struchkov.bot.gitlab.context.domain.PipelineStatus; import dev.struchkov.bot.gitlab.context.domain.PipelineStatus;
import dev.struchkov.bot.gitlab.context.domain.entity.Pipeline; import dev.struchkov.bot.gitlab.context.domain.entity.Pipeline;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.jpa.repository.support.JpaRepositoryImplementation; import org.springframework.data.jpa.repository.support.JpaRepositoryImplementation;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@@ -17,4 +18,7 @@ public interface PipelineJpaRepository extends JpaRepositoryImplementation<Pipel
void deleteAllByCreatedBefore(LocalDateTime date); void deleteAllByCreatedBefore(LocalDateTime date);
@Query("SELECT p.id FROM Pipeline p")
Set<Long> findAllIds();
} }

View File

@@ -3,7 +3,9 @@ package dev.struchkov.bot.gitlab.data.jpa;
import dev.struchkov.bot.gitlab.context.domain.entity.Project; import dev.struchkov.bot.gitlab.context.domain.entity.Project;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.Optional;
import java.util.Set; import java.util.Set;
/** /**
@@ -14,4 +16,7 @@ public interface ProjectJpaRepository extends JpaRepository<Project, Long> {
@Query("SELECT p.id FROM Project p") @Query("SELECT p.id FROM Project p")
Set<Long> findAllIds(); Set<Long> findAllIds();
@Query("SELECT p.name FROM Project p WHERE p.id = :projectId")
Optional<String> findProjectNameById(@Param("projectId") Long projectId);
} }

View File

@@ -10,13 +10,9 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static dev.struchkov.haiti.context.exception.NotFoundException.notFoundException; import static dev.struchkov.haiti.context.exception.NotFoundException.notFoundException;
import static dev.struchkov.haiti.utils.network.HttpParse.ACCEPT; import static dev.struchkov.haiti.utils.network.HttpParse.ACCEPT;
@@ -30,20 +26,15 @@ import static dev.struchkov.haiti.utils.network.HttpParse.ACCEPT;
@EnableScheduling @EnableScheduling
public class AppConfig { public class AppConfig {
/** // /**
* Отвечает за работу шедулеров в паралельном режиме // * Отвечает за работу шедулеров в паралельном режиме
*/ // */
@Bean // @Bean
public TaskScheduler taskScheduler() { // public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); // ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(12); // taskScheduler.setPoolSize(12);
return taskScheduler; // return taskScheduler;
} // }
@Bean
public ExecutorService executorService() {
return Executors.newFixedThreadPool(3);
}
@Bean @Bean
public ConversionService conversionService(Converter... converters) { public ConversionService conversionService(Converter... converters) {

View File

@@ -1,5 +1,6 @@
package dev.struchkov.bot.gitlab.scheduler; package dev.struchkov.bot.gitlab.scheduler;
import dev.struchkov.bot.gitlab.context.service.AppSettingService;
import dev.struchkov.bot.gitlab.context.service.MergeRequestsService; import dev.struchkov.bot.gitlab.context.service.MergeRequestsService;
import dev.struchkov.bot.gitlab.context.service.PipelineService; import dev.struchkov.bot.gitlab.context.service.PipelineService;
import dev.struchkov.bot.gitlab.core.service.parser.DiscussionParser; import dev.struchkov.bot.gitlab.core.service.parser.DiscussionParser;
@@ -18,6 +19,8 @@ import org.springframework.stereotype.Service;
@RequiredArgsConstructor @RequiredArgsConstructor
public class SchedulerService { public class SchedulerService {
private final AppSettingService settingService;
private final PipelineParser pipelineParser; private final PipelineParser pipelineParser;
private final MergeRequestParser mergeRequestParser; private final MergeRequestParser mergeRequestParser;
private final DiscussionParser discussionParser; private final DiscussionParser discussionParser;
@@ -28,6 +31,7 @@ public class SchedulerService {
@Scheduled(cron = "0 */1 * * * *") @Scheduled(cron = "0 */1 * * * *")
public void newMergeRequest() { public void newMergeRequest() {
log.debug("Запуск процесса обновления данных"); log.debug("Запуск процесса обновления данных");
if (!settingService.isFirstStart()) {
mergeRequestParser.parsingOldMergeRequest(); mergeRequestParser.parsingOldMergeRequest();
mergeRequestParser.parsingNewMergeRequest(); mergeRequestParser.parsingNewMergeRequest();
pipelineParser.scanOldPipeline(); pipelineParser.scanOldPipeline();
@@ -36,6 +40,9 @@ public class SchedulerService {
discussionParser.scanNewDiscussion(); discussionParser.scanNewDiscussion();
mergeRequestsService.cleanOld(); mergeRequestsService.cleanOld();
pipelineService.cleanOld(); pipelineService.cleanOld();
} else {
log.warn("Процесс обновления данных не был выполнен, так как пользователь не выполнил первичную настройку.");
}
log.debug("Конец процесса обновления данных"); log.debug("Конец процесса обновления данных");
} }

View File

@@ -8,7 +8,12 @@
<column name="id" type="int"> <column name="id" type="int">
<constraints primaryKey="true"/> <constraints primaryKey="true"/>
</column> </column>
<column name="first_start" type="boolean"/> <column name="first_start" type="boolean" defaultValue="true">
<constraints nullable="false"/>
</column>
<column name="enable_notify" type="boolean" defaultValue="false">
<constraints nullable="false"/>
</column>
</createTable> </createTable>
</changeSet> </changeSet>

38
pom.xml
View File

@@ -258,6 +258,44 @@
</profile> </profile>
</profiles> </profiles>
<repositories>
<repository>
<id>struchkov-nexus-release</id>
<url>https://nexus.struchkov.dev/repository/maven-releases/</url>
<releases>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
<checksumPolicy>fail</checksumPolicy>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>struchkov-nexus-snapshot</id>
<url>https://nexus.struchkov.dev/repository/maven-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
<checksumPolicy>warn</checksumPolicy>
</snapshots>
</repository>
</repositories>
<distributionManagement>
<repository>
<id>struchkov-nexus-release</id>
<url>https://nexus.struchkov.dev/repository/maven-releases/</url>
</repository>
<snapshotRepository>
<id>struchkov-nexus-snapshot</id>
<url>https://nexus.struchkov.dev/repository/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>
<developers> <developers>
<developer> <developer>
<id>uPagge</id> <id>uPagge</id>

View File

@@ -1,17 +1,20 @@
package dev.struchkov.bot.gitlab.telegram.config; package dev.struchkov.bot.gitlab.telegram.config;
import dev.struchkov.bot.gitlab.telegram.service.ReplaceUrlLocalhost; import dev.struchkov.bot.gitlab.telegram.service.ReplaceUrlLocalhost;
import dev.struchkov.bot.gitlab.telegram.unit.CommandUnit;
import dev.struchkov.bot.gitlab.telegram.unit.MenuConfig; import dev.struchkov.bot.gitlab.telegram.unit.MenuConfig;
import dev.struchkov.bot.gitlab.telegram.unit.UnitConfig; import dev.struchkov.bot.gitlab.telegram.unit.flow.InitSettingFlow;
import dev.struchkov.godfather.main.domain.content.Mail; import dev.struchkov.godfather.main.domain.content.Mail;
import dev.struchkov.godfather.simple.context.service.EventHandler; import dev.struchkov.godfather.simple.context.service.EventHandler;
import dev.struchkov.godfather.simple.context.service.PersonSettingService; import dev.struchkov.godfather.simple.context.service.PersonSettingService;
import dev.struchkov.godfather.simple.context.service.UnitPointerService; import dev.struchkov.godfather.simple.context.service.UnitPointerService;
import dev.struchkov.godfather.simple.core.provider.StoryLineHandler; import dev.struchkov.godfather.simple.core.provider.StoryLineHandler;
import dev.struchkov.godfather.simple.core.service.PersonSettingServiceImpl; import dev.struchkov.godfather.simple.core.service.PersonSettingServiceImpl;
import dev.struchkov.godfather.simple.core.service.StorylineContextMapImpl;
import dev.struchkov.godfather.simple.core.service.StorylineMailService; import dev.struchkov.godfather.simple.core.service.StorylineMailService;
import dev.struchkov.godfather.simple.core.service.StorylineService; import dev.struchkov.godfather.simple.core.service.StorylineService;
import dev.struchkov.godfather.simple.core.service.UnitPointerServiceImpl; import dev.struchkov.godfather.simple.core.service.UnitPointerServiceImpl;
import dev.struchkov.godfather.simple.data.StorylineContext;
import dev.struchkov.godfather.simple.data.repository.impl.PersonSettingLocalRepository; import dev.struchkov.godfather.simple.data.repository.impl.PersonSettingLocalRepository;
import dev.struchkov.godfather.simple.data.repository.impl.StorylineMapRepository; import dev.struchkov.godfather.simple.data.repository.impl.StorylineMapRepository;
import dev.struchkov.godfather.simple.data.repository.impl.UnitPointLocalRepository; import dev.struchkov.godfather.simple.data.repository.impl.UnitPointLocalRepository;
@@ -25,6 +28,7 @@ import dev.struchkov.godfather.telegram.simple.core.MailAutoresponderTelegram;
import dev.struchkov.godfather.telegram.simple.core.TelegramConnectBot; import dev.struchkov.godfather.telegram.simple.core.TelegramConnectBot;
import dev.struchkov.godfather.telegram.simple.core.service.SenderMapRepository; import dev.struchkov.godfather.telegram.simple.core.service.SenderMapRepository;
import dev.struchkov.godfather.telegram.simple.sender.TelegramSender; import dev.struchkov.godfather.telegram.simple.sender.TelegramSender;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@@ -32,6 +36,8 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.EnableScheduling;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/** /**
* @author upagge [30.01.2020] * @author upagge [30.01.2020]
@@ -40,6 +46,16 @@ import java.util.List;
@EnableScheduling @EnableScheduling
public class TelegramBotConfig { public class TelegramBotConfig {
@Bean("messageExecutorService")
public ExecutorService executorService() {
return Executors.newFixedThreadPool(3);
}
@Bean
public StorylineContext storylineContext() {
return new StorylineContextMapImpl();
}
@Bean @Bean
public UnitPointerService unitPointerService() { public UnitPointerService unitPointerService() {
return new UnitPointerServiceImpl(new UnitPointLocalRepository()); return new UnitPointerServiceImpl(new UnitPointLocalRepository());
@@ -55,9 +71,10 @@ public class TelegramBotConfig {
UnitPointerService unitPointerService, UnitPointerService unitPointerService,
MenuConfig menuConfig, MenuConfig menuConfig,
UnitConfig unitConfig InitSettingFlow unitConfig,
CommandUnit commandUnit
) { ) {
final List<Object> config = List.of(menuConfig, unitConfig); final List<Object> config = List.of(menuConfig, unitConfig, commandUnit);
return new StorylineMailService( return new StorylineMailService(
unitPointerService, unitPointerService,
@@ -68,6 +85,7 @@ public class TelegramBotConfig {
@Bean @Bean
public MailAutoresponderTelegram messageAutoresponderTelegram( public MailAutoresponderTelegram messageAutoresponderTelegram(
@Qualifier("messageExecutorService") ExecutorService executorService,
TelegramSending sending, TelegramSending sending,
PersonSettingService personSettingService, PersonSettingService personSettingService,
@@ -76,6 +94,7 @@ public class TelegramBotConfig {
final MailAutoresponderTelegram autoresponder = new MailAutoresponderTelegram( final MailAutoresponderTelegram autoresponder = new MailAutoresponderTelegram(
sending, personSettingService, mailStorylineService sending, personSettingService, mailStorylineService
); );
autoresponder.setExecutorService(executorService);
return autoresponder; return autoresponder;
} }

View File

@@ -3,13 +3,16 @@ package dev.struchkov.bot.gitlab.telegram.service;
import dev.struchkov.bot.gitlab.context.domain.PersonInformation; import dev.struchkov.bot.gitlab.context.domain.PersonInformation;
import dev.struchkov.bot.gitlab.context.domain.notify.Notify; import dev.struchkov.bot.gitlab.context.domain.notify.Notify;
import dev.struchkov.bot.gitlab.context.service.MessageSendService; import dev.struchkov.bot.gitlab.context.service.MessageSendService;
import dev.struchkov.bot.gitlab.telegram.service.notify.NotifyBoxAnswerGenerator;
import dev.struchkov.godfather.main.domain.BoxAnswer; import dev.struchkov.godfather.main.domain.BoxAnswer;
import dev.struchkov.godfather.telegram.simple.context.service.TelegramSending; import dev.struchkov.godfather.telegram.simple.context.service.TelegramSending;
import lombok.NonNull; import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import static dev.struchkov.godfather.main.domain.BoxAnswer.boxAnswer; import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
/** /**
* Отправляет сообщение в телеграмм. * Отправляет сообщение в телеграмм.
@@ -17,18 +20,36 @@ import static dev.struchkov.godfather.main.domain.BoxAnswer.boxAnswer;
* @author upagge 17.09.2020 * @author upagge 17.09.2020
*/ */
@Service @Service
@RequiredArgsConstructor
public class MessageSendTelegramService implements MessageSendService { public class MessageSendTelegramService implements MessageSendService {
private final Map<String, NotifyBoxAnswerGenerator> generatorMap;
private final TelegramSending sending; private final TelegramSending sending;
private final PersonInformation personInformation; private final PersonInformation personInformation;
public MessageSendTelegramService(
List<NotifyBoxAnswerGenerator> generators,
TelegramSending sending,
PersonInformation personInformation
) {
this.generatorMap = generators.stream().collect(Collectors.toMap(NotifyBoxAnswerGenerator::getNotifyType, n -> n));
this.sending = sending;
this.personInformation = personInformation;
}
@Override @Override
public void send(@NonNull Notify notify) { public void send(@NonNull Notify notify) {
final BoxAnswer boxAnswer = boxAnswer(notify.generateMessage()); getGenerator(notify.getType())
boxAnswer.setRecipientIfNull(personInformation.getTelegramId()); .map(generator -> {
sending.send(boxAnswer); final BoxAnswer answer = generator.generate(notify);
answer.setRecipientIfNull(personInformation.getTelegramId());
return answer;
})
.ifPresent(sending::send);
}
private Optional<NotifyBoxAnswerGenerator> getGenerator(String notifyType) {
return Optional.ofNullable(generatorMap.get(notifyType));
} }
} }

View File

@@ -1,9 +1,10 @@
package dev.struchkov.bot.gitlab.telegram.service; package dev.struchkov.bot.gitlab.telegram.service;
import dev.struchkov.bot.gitlab.context.domain.notify.SimpleTextNotify;
import dev.struchkov.bot.gitlab.context.service.AppSettingService; import dev.struchkov.bot.gitlab.context.service.AppSettingService;
import dev.struchkov.bot.gitlab.context.service.NotifyService;
import dev.struchkov.bot.gitlab.core.config.properties.AppProperty; import dev.struchkov.bot.gitlab.core.config.properties.AppProperty;
import dev.struchkov.bot.gitlab.core.config.properties.PersonProperty;
import dev.struchkov.godfather.main.domain.BoxAnswer;
import dev.struchkov.godfather.telegram.simple.context.service.TelegramSending;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -16,20 +17,25 @@ import javax.annotation.PostConstruct;
@RequiredArgsConstructor @RequiredArgsConstructor
public class StartNotify { public class StartNotify {
private final NotifyService notifyService; private final TelegramSending sending;
private final AppProperty appProperty; private final AppProperty appProperty;
private final AppSettingService settingService; private final AppSettingService settingService;
private final PersonProperty personProperty;
@PostConstruct @PostConstruct
public void sendStartNotification() { public void sendStartNotification() {
if (!settingService.isFirstStart()) { if (!settingService.isFirstStart()) {
notifyService.send( final BoxAnswer boxAnswer = BoxAnswer.builder()
SimpleTextNotify.builder() .recipientPersonId(personProperty.getTelegramId())
.message("Hello. I wish you a productive day :)" + .message(
"\n-- -- -- -- --\n" + new StringBuilder()
"Version " + appProperty.getVersion() + " | Developer: [uPagge](https://mark.struchkov.dev)") .append("Hello. I wish you a productive day :)")
.build() .append("\n-- -- -- -- --\n")
); .append("Version ").append(appProperty.getVersion()).append(" | Developer: [uPagge](https://mark.struchkov.dev)")
.toString()
).build();
sending.send(boxAnswer);
} }
} }

View File

@@ -0,0 +1,31 @@
package dev.struchkov.bot.gitlab.telegram.service.notify;
import dev.struchkov.bot.gitlab.context.domain.notify.mergerequest.ConflictPrNotify;
import dev.struchkov.bot.gitlab.context.utils.Icons;
import dev.struchkov.godfather.main.domain.BoxAnswer;
import org.springframework.stereotype.Component;
import static dev.struchkov.bot.gitlab.context.utils.Icons.link;
import static dev.struchkov.godfather.main.domain.BoxAnswer.boxAnswer;
import static dev.struchkov.haiti.utils.Strings.escapeMarkdown;
@Component
public class ConflictPrNotifyGenerator implements NotifyBoxAnswerGenerator<ConflictPrNotify> {
@Override
public BoxAnswer generate(ConflictPrNotify notify) {
final StringBuilder builder = new StringBuilder(Icons.DANGEROUS).append(" *Attention! MergeRequest conflict | ").append(escapeMarkdown(notify.getProjectName())).append("*")
.append(Icons.HR)
.append(link(notify.getTitle(), notify.getUrl()));
final String notifyMessage = builder.toString();
return boxAnswer(notifyMessage);
}
@Override
public String getNotifyType() {
return ConflictPrNotify.TYPE;
}
}

View File

@@ -0,0 +1,48 @@
package dev.struchkov.bot.gitlab.telegram.service.notify;
import dev.struchkov.bot.gitlab.context.domain.notify.task.DiscussionNewNotify;
import dev.struchkov.bot.gitlab.context.utils.Icons;
import dev.struchkov.godfather.main.domain.BoxAnswer;
import dev.struchkov.haiti.utils.Pair;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
import static dev.struchkov.godfather.main.domain.BoxAnswer.boxAnswer;
import static dev.struchkov.haiti.utils.Checker.checkNotNull;
import static dev.struchkov.haiti.utils.Strings.escapeMarkdown;
@Component
public class DiscussionNewNotifyGenerator implements NotifyBoxAnswerGenerator<DiscussionNewNotify> {
@Override
public BoxAnswer generate(DiscussionNewNotify notify) {
final StringBuilder builder = new StringBuilder(Icons.TASK).append(" [New discussion](").append(notify.getUrl()).append(")")
.append(Icons.HR)
.append(escapeMarkdown(notify.getMrName()))
.append(Icons.HR)
.append("*").append(notify.getAuthorName()).append("*: ").append(escapeMarkdown(notify.getMessageTask()));
final List<Pair<String, String>> notes = notify.getNotes();
if (checkNotNull(notes)) {
builder.append("\n-- -- -- -- comments -- -- -- --\n")
.append(convertNotes(notes));
}
final String notifyMessage = builder.toString();
return boxAnswer(notifyMessage);
}
private String convertNotes(List<Pair<String, String>> notes) {
return notes.stream()
.map(note -> "*" + note.getKey() + "*: " + note.getValue())
.collect(Collectors.joining("\n"));
}
@Override
public String getNotifyType() {
return DiscussionNewNotify.TYPE;
}
}

View File

@@ -0,0 +1,41 @@
package dev.struchkov.bot.gitlab.telegram.service.notify;
import dev.struchkov.bot.gitlab.context.domain.notify.comment.NewCommentNotify;
import dev.struchkov.bot.gitlab.context.utils.Smile;
import dev.struchkov.godfather.main.domain.BoxAnswer;
import org.springframework.stereotype.Component;
import static dev.struchkov.godfather.main.domain.BoxAnswer.boxAnswer;
import static dev.struchkov.haiti.utils.Checker.checkNotNull;
import static dev.struchkov.haiti.utils.Strings.escapeMarkdown;
@Component
public class NewCommentNotifyGenerator implements NotifyBoxAnswerGenerator<NewCommentNotify> {
@Override
public BoxAnswer generate(NewCommentNotify notify) {
final StringBuilder builder = new StringBuilder(Smile.COMMENT.getValue()).append(" [New answer in discussion](").append(notify.getUrl()).append(")\n--- --- --- ---");
if (checkNotNull(notify.getDiscussionMessage())) {
builder.append("\n-- -- discussion first message -- --\n")
.append("*").append(notify.getDiscussionAuthor()).append("*: ").append(escapeMarkdown(notify.getDiscussionMessage()));
}
if (checkNotNull(notify.getPreviousMessage())) {
builder.append("\n-- -- -- previous message -- -- --\n")
.append("*").append(notify.getPreviousAuthor()).append("*: ").append(escapeMarkdown(notify.getPreviousMessage()));
}
builder.append("\n-- -- -- --- new answer --- -- -- --\n")
.append("*").append(notify.getAuthorName()).append("*: ").append(escapeMarkdown(notify.getMessage()));
final String messageNotify = builder.toString();
return boxAnswer(messageNotify);
}
@Override
public String getNotifyType() {
return NewCommentNotify.TYPE;
}
}

View File

@@ -0,0 +1,45 @@
package dev.struchkov.bot.gitlab.telegram.service.notify;
import dev.struchkov.bot.gitlab.context.domain.notify.mergerequest.NewPrNotify;
import dev.struchkov.bot.gitlab.context.utils.Icons;
import dev.struchkov.godfather.main.domain.BoxAnswer;
import org.springframework.stereotype.Component;
import java.util.stream.Collectors;
import static dev.struchkov.bot.gitlab.context.utils.Icons.link;
import static dev.struchkov.godfather.main.domain.BoxAnswer.boxAnswer;
import static dev.struchkov.haiti.utils.Strings.escapeMarkdown;
@Component
public class NewPrNotifyGenerator implements NotifyBoxAnswerGenerator<NewPrNotify> {
@Override
public BoxAnswer generate(NewPrNotify notify) {
final String labelText = notify.getLabels().stream()
.map(label -> "#" + label)
.collect(Collectors.joining(" "));
final StringBuilder builder = new StringBuilder(Icons.FUN).append(" *New merge request for review | ").append(escapeMarkdown(notify.getProjectName())).append("*")
.append(Icons.HR)
.append(link(notify.getType(), notify.getUrl()));
if (!labelText.isEmpty()) {
builder.append("\n\n").append(labelText);
}
builder.append(Icons.HR)
.append(Icons.TREE).append(": ").append(notify.getSourceBranch()).append(Icons.ARROW).append(notify.getTargetBranch()).append("\n")
.append(Icons.AUTHOR).append(": ").append(notify.getAuthor());
final String notifyMessage = builder.toString();
return boxAnswer(notifyMessage);
}
@Override
public String getNotifyType() {
return NewPrNotify.TYPE;
}
}

View File

@@ -0,0 +1,45 @@
package dev.struchkov.bot.gitlab.telegram.service.notify;
import dev.struchkov.bot.gitlab.context.domain.notify.NewProjectNotify;
import dev.struchkov.bot.gitlab.context.utils.Icons;
import dev.struchkov.godfather.main.domain.BoxAnswer;
import dev.struchkov.haiti.utils.Strings;
import org.springframework.stereotype.Component;
import java.util.Optional;
import static dev.struchkov.bot.gitlab.context.utils.Icons.link;
import static dev.struchkov.godfather.main.domain.BoxAnswer.boxAnswer;
@Component
public class NewProjectNotifyGenerator implements NotifyBoxAnswerGenerator<NewProjectNotify> {
@Override
public BoxAnswer generate(NewProjectNotify notify) {
final Optional<String> optDescription = Optional.ofNullable(notify.getProjectDescription())
.map(Strings::escapeMarkdown);
final StringBuilder builder = new StringBuilder(Icons.FUN).append("*New project*")
.append(Icons.HR)
.append(link(notify.getProjectName(), notify.getProjectUrl()))
.append(Icons.HR);
if (optDescription.isPresent() || !Strings.EMPTY.equals(optDescription.get())) {
final String description = optDescription.get();
builder.append(description)
.append(Icons.HR);
}
builder.append(Icons.AUTHOR).append(": ").append(notify.getAuthorName());
final String notifyMessage = builder.toString();
return boxAnswer(notifyMessage);
}
@Override
public String getNotifyType() {
return NewProjectNotify.TYPE;
}
}

View File

@@ -0,0 +1,18 @@
package dev.struchkov.bot.gitlab.telegram.service.notify;
import dev.struchkov.godfather.main.domain.BoxAnswer;
import java.util.List;
import java.util.stream.Collectors;
public interface NotifyBoxAnswerGenerator<T> {
BoxAnswer generate(T notify);
default List<BoxAnswer> generate(List<T> notify) {
return notify.stream().map(this::generate).collect(Collectors.toList());
}
String getNotifyType();
}

View File

@@ -0,0 +1,60 @@
package dev.struchkov.bot.gitlab.telegram.service.notify;
import dev.struchkov.bot.gitlab.context.domain.notify.pipeline.PipelineNotify;
import dev.struchkov.bot.gitlab.context.service.ProjectService;
import dev.struchkov.bot.gitlab.context.utils.Icons;
import dev.struchkov.godfather.main.domain.BoxAnswer;
import dev.struchkov.haiti.utils.Strings;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Optional;
import static dev.struchkov.godfather.main.domain.BoxAnswer.boxAnswer;
import static dev.struchkov.godfather.main.domain.keyboard.button.SimpleButton.simpleButton;
import static dev.struchkov.godfather.main.domain.keyboard.simple.SimpleKeyBoardLine.simpleLine;
import static dev.struchkov.godfather.telegram.domain.keyboard.InlineKeyBoard.inlineKeyBoard;
import static dev.struchkov.godfather.telegram.domain.keyboard.button.UrlButton.urlButton;
@Service
@RequiredArgsConstructor
public class PipelineNotifyGenerator implements NotifyBoxAnswerGenerator<PipelineNotify> {
private final ProjectService projectService;
@Override
public BoxAnswer generate(PipelineNotify notify) {
final Optional<String> optProjectName = projectService.getProjectNameById(notify.getProjectId())
.map(Strings::escapeMarkdown);
final StringBuilder builder = new StringBuilder(Icons.BUILD).append(" *Pipeline ").append(notify.getPipelineId()).append("*");
if (optProjectName.isPresent()) {
final String projectName = optProjectName.get();
builder.append(" | ").append(projectName);
}
final String notifyMessage = builder
.append(Icons.HR)
.append(Icons.TREE).append(": ").append(notify.getRefName())
.append(Icons.HR)
.append(notify.getOldStatus().getIcon()).append(" ").append(notify.getOldStatus()).append(Icons.ARROW).append(notify.getNewStatus().getIcon()).append(" ").append(notify.getNewStatus())
.toString();
return boxAnswer(
notifyMessage,
inlineKeyBoard(
simpleLine(
simpleButton(Icons.VIEW, "DELETE_MESSAGE"),
urlButton(Icons.LINK, notify.getWebUrl())
)
)
);
}
@Override
public String getNotifyType() {
return PipelineNotify.TYPE;
}
}

View File

@@ -0,0 +1,33 @@
package dev.struchkov.bot.gitlab.telegram.service.notify;
import dev.struchkov.bot.gitlab.context.domain.notify.mergerequest.StatusPrNotify;
import dev.struchkov.bot.gitlab.context.utils.Icons;
import dev.struchkov.godfather.main.domain.BoxAnswer;
import org.springframework.stereotype.Component;
import static dev.struchkov.bot.gitlab.context.utils.Icons.link;
import static dev.struchkov.godfather.main.domain.BoxAnswer.boxAnswer;
@Component
public class StatusPrNotifyGenerator implements NotifyBoxAnswerGenerator<StatusPrNotify> {
@Override
public BoxAnswer generate(StatusPrNotify notify) {
final StringBuilder builder = new StringBuilder(Icons.PEN).append(" *MergeRequest status changed | ").append(notify.getProjectName()).append("*")
.append(Icons.HR)
.append(link(notify.getTitle(), notify.getUrl()))
.append(Icons.HR)
.append(notify.getOldStatus().name()).append(Icons.ARROW).append(notify.getNewStatus().name());
final String notifyMessage = builder.toString();
return boxAnswer(notifyMessage);
}
@Override
public String getNotifyType() {
return StatusPrNotify.TYPE;
}
}

View File

@@ -0,0 +1,31 @@
package dev.struchkov.bot.gitlab.telegram.service.notify;
import dev.struchkov.bot.gitlab.context.domain.notify.task.TaskCloseNotify;
import dev.struchkov.bot.gitlab.context.utils.Icons;
import dev.struchkov.godfather.main.domain.BoxAnswer;
import org.springframework.stereotype.Component;
import static dev.struchkov.bot.gitlab.context.utils.Icons.link;
import static dev.struchkov.godfather.main.domain.BoxAnswer.boxAnswer;
import static dev.struchkov.haiti.utils.Strings.escapeMarkdown;
@Component
public class TaskCloseNotifyGenerate implements NotifyBoxAnswerGenerator<TaskCloseNotify> {
@Override
public BoxAnswer generate(TaskCloseNotify notify) {
final StringBuilder builder = new StringBuilder(Icons.TASK).append(" *Closed ").append(link("task", notify.getUrl()))
.append(Icons.HR)
.append("*").append(notify.getAuthorName()).append("*: ").append(escapeMarkdown(notify.getMessageTask()));
final String notifyMessage = builder.toString();
return boxAnswer(notifyMessage);
}
@Override
public String getNotifyType() {
return TaskCloseNotify.TYPE;
}
}

View File

@@ -0,0 +1,44 @@
package dev.struchkov.bot.gitlab.telegram.service.notify;
import dev.struchkov.bot.gitlab.context.domain.notify.mergerequest.UpdatePrNotify;
import dev.struchkov.bot.gitlab.context.utils.Icons;
import dev.struchkov.bot.gitlab.context.utils.Smile;
import dev.struchkov.godfather.main.domain.BoxAnswer;
import org.springframework.stereotype.Component;
import static dev.struchkov.bot.gitlab.context.utils.Icons.link;
import static dev.struchkov.godfather.main.domain.BoxAnswer.boxAnswer;
import static dev.struchkov.haiti.utils.Strings.escapeMarkdown;
@Component
public class UpdatePrNotifyGenerator implements NotifyBoxAnswerGenerator<UpdatePrNotify> {
@Override
public BoxAnswer generate(UpdatePrNotify notify) {
final StringBuilder builder = new StringBuilder(Icons.UPDATE).append(" *MergeRequest update | ").append(escapeMarkdown(notify.getProjectName())).append("*")
.append(Smile.HR.getValue())
.append(link(notify.getTitle(), notify.getUrl()));
if (notify.getAllTasks() > 0) {
builder.append(Smile.HR.getValue())
.append("All tasks: ").append(notify.getAllResolvedTasks()).append("/").append(notify.getAllTasks());
if (notify.getPersonTasks() > 0) {
builder.append("\nYour tasks: ").append(notify.getPersonResolvedTasks()).append("/").append(notify.getPersonTasks());
}
}
builder.append(Icons.HR)
.append(Icons.AUTHOR).append(": ").append(notify.getAuthor());
final String notifyMessage = builder.toString();
return boxAnswer(notifyMessage);
}
@Override
public String getNotifyType() {
return UpdatePrNotify.TYPE;
}
}

View File

@@ -0,0 +1,110 @@
package dev.struchkov.bot.gitlab.telegram.unit;
import dev.struchkov.bot.gitlab.context.domain.PersonInformation;
import dev.struchkov.bot.gitlab.context.domain.entity.Note;
import dev.struchkov.bot.gitlab.context.service.AppSettingService;
import dev.struchkov.bot.gitlab.context.service.DiscussionService;
import dev.struchkov.bot.gitlab.context.service.NoteService;
import dev.struchkov.godfather.main.domain.BoxAnswer;
import dev.struchkov.godfather.main.domain.annotation.Unit;
import dev.struchkov.godfather.main.domain.content.Attachment;
import dev.struchkov.godfather.main.domain.content.Mail;
import dev.struchkov.godfather.simple.core.unit.AnswerText;
import dev.struchkov.godfather.telegram.domain.attachment.ButtonClickAttachment;
import dev.struchkov.godfather.telegram.domain.attachment.LinkAttachment;
import dev.struchkov.godfather.telegram.domain.attachment.TelegramAttachmentType;
import dev.struchkov.godfather.telegram.main.core.util.Attachments;
import dev.struchkov.godfather.telegram.simple.context.service.TelegramSending;
import dev.struchkov.haiti.utils.Checker;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.text.MessageFormat;
import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.ANSWER_NOTE;
import static dev.struchkov.godfather.main.domain.BoxAnswer.boxAnswer;
@Component
@RequiredArgsConstructor
public class CommandUnit {
private static final Pattern NOTE_LINK = Pattern.compile("#note_\\d+$");
private final PersonInformation personInformation;
private final AppSettingService settingService;
private final NoteService noteService;
private final DiscussionService discussionService;
private final TelegramSending telegramSending;
@Unit(value = ANSWER_NOTE, main = true)
public AnswerText<Mail> answerNote() {
return AnswerText.<Mail>builder()
.triggerCheck(
mail -> {
final boolean isAccess = personInformation.getTelegramId().equals(mail.getPersonId());
if (isAccess) {
final boolean isFirstStart = settingService.isFirstStart();
if (!isFirstStart) {
final List<Mail> forwardMails = mail.getForwardMail();
if (Checker.checkNotNull(forwardMails) && forwardMails.size() == 1) {
final Mail forwardMail = forwardMails.get(0);
return Attachments.findFirstLink(forwardMail.getAttachments()).isPresent();
}
}
}
return false;
}
)
.answer(
mail -> {
final List<Attachment> attachments = mail.getForwardMail().get(0).getAttachments();
for (Attachment attachment : attachments) {
if (TelegramAttachmentType.LINK.name().equals(attachment.getType())) {
final String url = ((LinkAttachment) attachment).getUrl();
final Matcher matcher = NOTE_LINK.matcher(url);
if (matcher.find()) {
final String noteText = url.substring(matcher.start(), matcher.end());
final Long noteId = Long.valueOf(noteText.replaceAll("#note_", ""));
final Note note = noteService.getByIdOrThrow(noteId);
final String discussionId = note.getDiscussion().getId();
discussionService.answer(discussionId, MessageFormat.format("@{0}, {1}", note.getAuthor().getUserName(), mail.getText()));
return BoxAnswer.builder().build();
}
}
}
return boxAnswer("Error");
}
)
.build();
}
@Unit(value = "DELETE_MESSAGE", main = true)
public AnswerText<Mail> deleteMessage() {
return AnswerText.<Mail>builder()
.triggerCheck(mail -> {
final boolean isAccess = personInformation.getTelegramId().equals(mail.getPersonId());
if (isAccess) {
final boolean isFirstStart = settingService.isFirstStart();
if (!isFirstStart) {
final Optional<ButtonClickAttachment> optButtonClick = Attachments.findFirstButtonClick(mail.getAttachments());
if (optButtonClick.isPresent()) {
final ButtonClickAttachment buttonClick = optButtonClick.get();
final String rawData = buttonClick.getRawCallBackData();
return rawData.equals("DELETE_MESSAGE");
}
}
}
return false;
})
.answer(mail -> {
final ButtonClickAttachment clickButton = Attachments.findFirstButtonClick(mail.getAttachments()).orElseThrow();
telegramSending.deleteMessage(mail.getPersonId(), clickButton.getMessageId());
})
.build();
}
}

View File

@@ -2,15 +2,22 @@ package dev.struchkov.bot.gitlab.telegram.unit;
import dev.struchkov.bot.gitlab.context.domain.PersonInformation; import dev.struchkov.bot.gitlab.context.domain.PersonInformation;
import dev.struchkov.bot.gitlab.context.domain.entity.MergeRequest; import dev.struchkov.bot.gitlab.context.domain.entity.MergeRequest;
import dev.struchkov.bot.gitlab.context.service.AppSettingService;
import dev.struchkov.bot.gitlab.context.service.MergeRequestsService; import dev.struchkov.bot.gitlab.context.service.MergeRequestsService;
import dev.struchkov.bot.gitlab.context.service.NoteService; import dev.struchkov.bot.gitlab.context.service.NoteService;
import dev.struchkov.bot.gitlab.context.utils.Smile;
import dev.struchkov.bot.gitlab.core.config.properties.GitlabProperty; import dev.struchkov.bot.gitlab.core.config.properties.GitlabProperty;
import dev.struchkov.bot.gitlab.core.service.parser.ProjectParser; import dev.struchkov.bot.gitlab.core.service.parser.ProjectParser;
import dev.struchkov.bot.gitlab.telegram.utils.Keys;
import dev.struchkov.bot.gitlab.telegram.utils.UnitName; import dev.struchkov.bot.gitlab.telegram.utils.UnitName;
import dev.struchkov.godfather.main.domain.BoxAnswer;
import dev.struchkov.godfather.main.domain.annotation.Unit; import dev.struchkov.godfather.main.domain.annotation.Unit;
import dev.struchkov.godfather.main.domain.content.Mail; import dev.struchkov.godfather.main.domain.content.Mail;
import dev.struchkov.godfather.simple.core.unit.AnswerText; import dev.struchkov.godfather.simple.core.unit.AnswerText;
import dev.struchkov.godfather.simple.core.unit.MainUnit; import dev.struchkov.godfather.simple.core.unit.MainUnit;
import dev.struchkov.godfather.simple.data.StorylineContext;
import dev.struchkov.godfather.telegram.domain.keyboard.InlineKeyBoard;
import dev.struchkov.godfather.telegram.simple.context.service.TelegramSending;
import dev.struchkov.haiti.utils.Checker; import dev.struchkov.haiti.utils.Checker;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -19,6 +26,7 @@ import java.text.MessageFormat;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.ACCESS_ERROR;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.ADD_NEW_PROJECT; import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.ADD_NEW_PROJECT;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.GENERAL_MENU; import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.GENERAL_MENU;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.GET_ASSIGNEE_MERGE_REQUEST; import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.GET_ASSIGNEE_MERGE_REQUEST;
@@ -26,6 +34,7 @@ import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.GET_TASKS;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.SETTINGS; import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.SETTINGS;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.TEXT_ADD_NEW_PROJECT; import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.TEXT_ADD_NEW_PROJECT;
import static dev.struchkov.godfather.main.domain.BoxAnswer.boxAnswer; import static dev.struchkov.godfather.main.domain.BoxAnswer.boxAnswer;
import static dev.struchkov.godfather.main.domain.BoxAnswer.replaceBoxAnswer;
import static dev.struchkov.godfather.main.domain.keyboard.button.SimpleButton.simpleButton; import static dev.struchkov.godfather.main.domain.keyboard.button.SimpleButton.simpleButton;
import static dev.struchkov.godfather.main.domain.keyboard.simple.SimpleKeyBoardLine.simpleLine; import static dev.struchkov.godfather.main.domain.keyboard.simple.SimpleKeyBoardLine.simpleLine;
import static dev.struchkov.godfather.telegram.domain.keyboard.InlineKeyBoard.inlineKeyBoard; import static dev.struchkov.godfather.telegram.domain.keyboard.InlineKeyBoard.inlineKeyBoard;
@@ -40,13 +49,32 @@ import static dev.struchkov.godfather.telegram.simple.core.util.TriggerChecks.cl
@RequiredArgsConstructor @RequiredArgsConstructor
public class MenuConfig { public class MenuConfig {
private final StorylineContext context;
private final TelegramSending sending;
private final ProjectParser projectParser; private final ProjectParser projectParser;
private final GitlabProperty gitlabProperty; private final GitlabProperty gitlabProperty;
private final PersonInformation personInformation; private final PersonInformation personInformation;
private final NoteService noteService; private final NoteService noteService;
private final MergeRequestsService mergeRequestsService; private final MergeRequestsService mergeRequestsService;
private final AppSettingService settingService;
@Unit(GENERAL_MENU) @Unit(value = ACCESS_ERROR, main = true)
public AnswerText<Mail> accessError() {
return AnswerText.<Mail>builder()
.triggerCheck(mail -> !personInformation.getTelegramId().equals(mail.getPersonId()))
.answer(message -> {
final String messageText = new StringBuilder("\uD83D\uDEA8 *Попытка несанкционированного доступа к боту*")
.append(Smile.HR.getValue())
.append("\uD83E\uDDB9\u200D♂: ").append(message.getPersonId()).append("\n")
.append("\uD83D\uDCAC: ").append(message.getText())
.toString();
return BoxAnswer.builder().recipientPersonId(personInformation.getTelegramId()).message(messageText).build();
})
.build();
}
@Unit(value = GENERAL_MENU, main = true)
public AnswerText<Mail> menu( public AnswerText<Mail> menu(
@Unit(SETTINGS) MainUnit<Mail> settings, @Unit(SETTINGS) MainUnit<Mail> settings,
@Unit(TEXT_ADD_NEW_PROJECT) MainUnit<Mail> textAddNewProject, @Unit(TEXT_ADD_NEW_PROJECT) MainUnit<Mail> textAddNewProject,
@@ -54,17 +82,34 @@ public class MenuConfig {
@Unit(GET_ASSIGNEE_MERGE_REQUEST) MainUnit<Mail> getAssigneeMergeRequest @Unit(GET_ASSIGNEE_MERGE_REQUEST) MainUnit<Mail> getAssigneeMergeRequest
) { ) {
return AnswerText.<Mail>builder() return AnswerText.<Mail>builder()
.answer(boxAnswer( .priority(5)
"This is the bot menu, select a new item", .triggerCheck(mail -> {
inlineKeyBoard( final boolean isAccess = personInformation.getTelegramId().equals(mail.getPersonId());
if (isAccess) {
final boolean firstStart = settingService.isFirstStart();
return !firstStart;
}
return false;
})
.answer(mail -> {
final String messageText = "This is the bot menu, select a new item";
final InlineKeyBoard generalMenuKeyBoard = inlineKeyBoard(
simpleLine(simpleButton("Add project", TEXT_ADD_NEW_PROJECT)), simpleLine(simpleButton("Add project", TEXT_ADD_NEW_PROJECT)),
simpleLine( simpleLine(
simpleButton("My tasks", GET_TASKS), simpleButton("My tasks", GET_TASKS),
simpleButton("Merge Request", GET_ASSIGNEE_MERGE_REQUEST) simpleButton("Merge Request", GET_ASSIGNEE_MERGE_REQUEST)
), ),
simpleLine(simpleButton("Settings", SETTINGS)) simpleLine(simpleButton("Settings", SETTINGS))
) );
) final String personId = mail.getPersonId();
final var initSettingFinish = context.removeKey(personId, Keys.INIT_SETTING_FINISH);
if (initSettingFinish.isPresent()) {
context.removeKey(personId, Keys.INIT_SETTING_PRIVATE_PROJECT_MESSAGE_ID).ifPresent(messageId -> sending.deleteMessage(personId, messageId));
context.removeKey(personId, Keys.INIT_SETTING_PUBLIC_PROJECT_MESSAGE_ID).ifPresent(messageId -> sending.deleteMessage(personId, messageId));
return replaceBoxAnswer(messageText, generalMenuKeyBoard);
}
return boxAnswer(messageText, generalMenuKeyBoard);
}
) )
.next(settings) .next(settings)
.next(textAddNewProject) .next(textAddNewProject)

View File

@@ -1,265 +0,0 @@
package dev.struchkov.bot.gitlab.telegram.unit;
import dev.struchkov.bot.gitlab.context.domain.PersonInformation;
import dev.struchkov.bot.gitlab.context.domain.entity.Note;
import dev.struchkov.bot.gitlab.context.service.AppSettingService;
import dev.struchkov.bot.gitlab.context.service.DiscussionService;
import dev.struchkov.bot.gitlab.context.service.NoteService;
import dev.struchkov.bot.gitlab.context.service.NotifyService;
import dev.struchkov.bot.gitlab.context.utils.Smile;
import dev.struchkov.bot.gitlab.core.service.parser.ProjectParser;
import dev.struchkov.godfather.main.core.unit.UnitActiveType;
import dev.struchkov.godfather.main.domain.BoxAnswer;
import dev.struchkov.godfather.main.domain.annotation.Unit;
import dev.struchkov.godfather.main.domain.content.Attachment;
import dev.struchkov.godfather.main.domain.content.Mail;
import dev.struchkov.godfather.simple.core.unit.AnswerCheck;
import dev.struchkov.godfather.simple.core.unit.AnswerText;
import dev.struchkov.godfather.simple.core.unit.MainUnit;
import dev.struchkov.godfather.telegram.domain.attachment.LinkAttachment;
import dev.struchkov.godfather.telegram.domain.attachment.TelegramAttachmentType;
import dev.struchkov.godfather.telegram.main.core.util.Attachments;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.text.MessageFormat;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.ACCESS_ERROR;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.ANSWER_NOTE;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.AUTHORIZATION;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.CHECK_FIRST_START;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.CHECK_MENU_OR_ANSWER;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.CHECK_PARSER_PRIVATE_PROJECT;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.CHECK_PARSE_OWNER_PROJECT;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.END_SETTING;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.GENERAL_MENU;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.PARSER_PRIVATE_PROJECT;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.PARSE_OWNER_PROJECT;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.TEXT_PARSER_PRIVATE_PROJECT;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.TEXT_PARSE_OWNER_PROJECT;
import static dev.struchkov.godfather.main.domain.BoxAnswer.boxAnswer;
import static dev.struchkov.godfather.main.domain.BoxAnswer.replaceBoxAnswer;
import static dev.struchkov.godfather.main.domain.keyboard.button.SimpleButton.simpleButton;
import static dev.struchkov.godfather.main.domain.keyboard.simple.SimpleKeyBoardLine.simpleLine;
import static dev.struchkov.godfather.telegram.domain.keyboard.InlineKeyBoard.inlineKeyBoard;
/**
* TODO: Добавить описание класса.
*
* @author upagge [30.01.2020]
*/
@Component
@RequiredArgsConstructor
public class UnitConfig {
private static final Pattern NOTE_LINK = Pattern.compile("#note_\\d+$");
private final PersonInformation personInformation;
private final AppSettingService settingService;
private final NoteService noteService;
private final DiscussionService discussionService;
private final NotifyService notifyService;
private final ProjectParser projectParser;
@Unit(value = AUTHORIZATION, main = true)
public AnswerCheck<Mail> auth(
@Unit(CHECK_FIRST_START) MainUnit<Mail> checkFirstStart,
@Unit(ACCESS_ERROR) MainUnit<Mail> accessError
) {
return AnswerCheck.<Mail>builder()
.check(mail -> personInformation.getTelegramId().equals(mail.getPersonId()))
.unitTrue(checkFirstStart)
.unitFalse(accessError)
.build();
}
@Unit(value = ACCESS_ERROR)
public AnswerText<Mail> accessError() {
return AnswerText.<Mail>builder()
.answer(message -> {
final String messageText = new StringBuilder("\uD83D\uDEA8 *Попытка несанкционированного доступа к боту*")
.append(Smile.HR.getValue())
.append("\uD83E\uDDB9\u200D♂: ").append(message.getPersonId()).append("\n")
.append("\uD83D\uDCAC: ").append(message.getText())
.toString();
return BoxAnswer.builder().recipientPersonId(personInformation.getTelegramId()).message(messageText).build();
})
.build();
}
@Unit(value = CHECK_FIRST_START)
public AnswerCheck<Mail> checkFirstStart(
@Unit(TEXT_PARSER_PRIVATE_PROJECT) MainUnit<Mail> textParserPrivateProject,
@Unit(CHECK_MENU_OR_ANSWER) MainUnit<Mail> checkMenuOrAnswer
) {
return AnswerCheck.<Mail>builder()
.check(message -> settingService.isFirstStart())
.unitFalse(checkMenuOrAnswer)
.unitTrue(textParserPrivateProject)
.build();
}
@Unit(value = CHECK_MENU_OR_ANSWER)
public AnswerCheck<Mail> checkMenuOrAnswer(
@Unit(GENERAL_MENU) MainUnit<Mail> menu,
@Unit(ANSWER_NOTE) MainUnit<Mail> answerNote
) {
return AnswerCheck.<Mail>builder()
.check(
mail -> {
final List<Mail> forwardMails = mail.getForwardMail();
if (forwardMails != null && forwardMails.size() == 1) {
final Mail forwardMail = forwardMails.get(0);
return Attachments.findFirstLink(forwardMail.getAttachments()).isPresent();
}
return false;
}
)
.unitTrue(answerNote)
.unitFalse(menu)
.build();
}
@Unit(ANSWER_NOTE)
public AnswerText<Mail> answerNote() {
return AnswerText.<Mail>builder()
.answer(
mail -> {
final List<Attachment> attachments = mail.getForwardMail().get(0).getAttachments();
for (Attachment attachment : attachments) {
if (TelegramAttachmentType.LINK.name().equals(attachment.getType())) {
final String url = ((LinkAttachment) attachment).getUrl();
final Matcher matcher = NOTE_LINK.matcher(url);
if (matcher.find()) {
final String noteText = url.substring(matcher.start(), matcher.end());
final Long noteId = Long.valueOf(noteText.replaceAll("#note_", ""));
final Note note = noteService.getByIdOrThrow(noteId);
final String discussionId = note.getDiscussion().getId();
discussionService.answer(discussionId, MessageFormat.format("@{0}, {1}", note.getAuthor().getUserName(), mail.getText()));
return BoxAnswer.builder().build();
}
}
}
return boxAnswer("Error");
}
)
.build();
}
@Unit(TEXT_PARSER_PRIVATE_PROJECT)
public AnswerText<Mail> textParserPrivateProject(
@Unit(CHECK_PARSER_PRIVATE_PROJECT) MainUnit<Mail> checkParserPrivateProject
) {
return AnswerText.<Mail>builder()
.answer(() -> boxAnswer(
"Start tracking private projects?",
inlineKeyBoard(
simpleLine(
simpleButton("Yes", "YES"),
simpleButton("No", "NO")
)
)
)
)
.activeType(UnitActiveType.AFTER)
.next(checkParserPrivateProject)
.build();
}
@Unit(CHECK_PARSER_PRIVATE_PROJECT)
public AnswerCheck<Mail> checkParserPrivateProject(
@Unit(PARSER_PRIVATE_PROJECT) AnswerText<Mail> parserPrivateProject,
@Unit(TEXT_PARSE_OWNER_PROJECT) MainUnit<Mail> textParseOwnerProject
) {
return AnswerCheck.<Mail>builder()
.check(mail -> "YES".equalsIgnoreCase(mail.getText()))
.intermediateAnswerIfTrue(replaceBoxAnswer("Scanning of private projects has begun. Wait..."))
.unitTrue(parserPrivateProject)
.unitFalse(textParseOwnerProject)
.build();
}
@Unit(PARSER_PRIVATE_PROJECT)
public AnswerText<Mail> parserPrivateProject(
@Unit(TEXT_PARSE_OWNER_PROJECT) MainUnit<Mail> textParseOwnerProject
) {
return AnswerText.<Mail>builder()
.answer(() -> {
notifyService.disableAllNotify();
projectParser.parseAllPrivateProject();
return replaceBoxAnswer("Projects have been successfully added to tracking");
})
.next(textParseOwnerProject)
.build();
}
@Unit(TEXT_PARSE_OWNER_PROJECT)
public AnswerText<Mail> textParseOwnerProject(
@Unit(CHECK_PARSE_OWNER_PROJECT) MainUnit<Mail> checkParseOwnerProject
) {
return AnswerText.<Mail>builder()
.answer(
boxAnswer(
"Start tracking public projects that you own?",
inlineKeyBoard(
simpleLine(
simpleButton("Yes", "YES"),
simpleButton("No", "NO")
)
)
)
)
.activeType(UnitActiveType.AFTER)
.next(checkParseOwnerProject)
.build();
}
@Unit(CHECK_PARSE_OWNER_PROJECT)
public AnswerCheck<Mail> checkParseOwnerProject(
@Unit(PARSE_OWNER_PROJECT) MainUnit<Mail> parseOwnerProject,
@Unit(END_SETTING) MainUnit<Mail> endSetting
) {
return AnswerCheck.<Mail>builder()
.check(message -> "YES".equalsIgnoreCase(message.getText()))
.intermediateAnswerIfTrue(replaceBoxAnswer("Scanning of public projects has begun. Wait..."))
.unitTrue(parseOwnerProject)
.unitFalse(endSetting)
.build();
}
@Unit(PARSE_OWNER_PROJECT)
public AnswerText<Mail> parseOwnerProject(
@Unit(END_SETTING) MainUnit<Mail> endSetting
) {
return AnswerText.<Mail>builder()
.answer(() -> {
projectParser.parseAllProjectOwner();
return replaceBoxAnswer("Projects have been successfully added to tracking");
})
.next(endSetting)
.build();
}
@Unit(END_SETTING)
public AnswerText<Mail> endSetting() {
return AnswerText.<Mail>builder()
.answer(
() -> {
settingService.disableFirstStart();
notifyService.enableAllNotify();
return replaceBoxAnswer("""
Configuration completed successfully
Developer: [uPagge](https://mark.struchkov.dev)
""");
}
)
.activeType(UnitActiveType.AFTER)
.build();
}
}

View File

@@ -0,0 +1,345 @@
package dev.struchkov.bot.gitlab.telegram.unit.flow;
import dev.struchkov.bot.gitlab.context.domain.PersonInformation;
import dev.struchkov.bot.gitlab.context.service.AppSettingService;
import dev.struchkov.bot.gitlab.context.service.DiscussionService;
import dev.struchkov.bot.gitlab.context.service.MergeRequestsService;
import dev.struchkov.bot.gitlab.context.service.PipelineService;
import dev.struchkov.bot.gitlab.context.service.ProjectService;
import dev.struchkov.bot.gitlab.core.service.parser.DiscussionParser;
import dev.struchkov.bot.gitlab.core.service.parser.MergeRequestParser;
import dev.struchkov.bot.gitlab.core.service.parser.PipelineParser;
import dev.struchkov.bot.gitlab.core.service.parser.ProjectParser;
import dev.struchkov.bot.gitlab.telegram.utils.Keys;
import dev.struchkov.godfather.main.domain.annotation.Unit;
import dev.struchkov.godfather.main.domain.content.Mail;
import dev.struchkov.godfather.simple.core.unit.AnswerText;
import dev.struchkov.godfather.simple.core.unit.MainUnit;
import dev.struchkov.godfather.simple.data.StorylineContext;
import dev.struchkov.godfather.telegram.domain.attachment.ButtonClickAttachment;
import dev.struchkov.godfather.telegram.main.core.util.Attachments;
import dev.struchkov.godfather.telegram.simple.context.service.TelegramSending;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.CHECK_PARSER_PRIVATE_PROJECT_NO;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.CHECK_PARSER_PRIVATE_PROJECT_YES;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.CHECK_PARSE_OWNER_PROJECT_NO;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.CHECK_PARSE_OWNER_PROJECT_YES;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.END_SETTING;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.FIRST_START;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.TEXT_PARSER_PRIVATE_PROJECT;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.TEXT_PARSE_OWNER_PROJECT;
import static dev.struchkov.godfather.main.core.unit.UnitActiveType.AFTER;
import static dev.struchkov.godfather.main.domain.BoxAnswer.boxAnswer;
import static dev.struchkov.godfather.main.domain.BoxAnswer.replaceBoxAnswer;
import static dev.struchkov.godfather.main.domain.keyboard.button.SimpleButton.simpleButton;
import static dev.struchkov.godfather.main.domain.keyboard.simple.SimpleKeyBoardLine.simpleLine;
import static dev.struchkov.godfather.telegram.domain.keyboard.InlineKeyBoard.inlineKeyBoard;
import static dev.struchkov.godfather.telegram.simple.core.util.TriggerChecks.clickButtonRaw;
import static java.text.MessageFormat.format;
/**
* TODO: Добавить описание класса.
*
* @author upagge [30.01.2020]
*/
@Component
@RequiredArgsConstructor
public class InitSettingFlow {
private final TelegramSending sending;
private final StorylineContext context;
private final PersonInformation personInformation;
private final AppSettingService settingService;
private final ProjectParser projectParser;
private final MergeRequestParser mergeRequestParser;
private final PipelineParser pipelineParser;
private final DiscussionParser discussionParser;
private final ProjectService projectService;
private final MergeRequestsService mergeRequestsService;
private final PipelineService pipelineService;
private final DiscussionService discussionService;
@Unit(value = FIRST_START, main = true)
public AnswerText<Mail> firstStart(
@Unit(value = TEXT_PARSER_PRIVATE_PROJECT) MainUnit<Mail> textParserPrivateProject
) {
return AnswerText.<Mail>builder()
.triggerCheck(mail -> {
final boolean isAccess = personInformation.getTelegramId().equals(mail.getPersonId());
if (isAccess) {
return settingService.isFirstStart();
}
return false;
})
.answer(
boxAnswer(
"""
Hello!
This bot will help you keep your finger on the pulse of all your GitLab projects.
Press start to start initial setup 👇
""",
inlineKeyBoard(simpleButton("start", TEXT_PARSER_PRIVATE_PROJECT))
)
)
.next(textParserPrivateProject)
.build();
}
@Unit(value = TEXT_PARSER_PRIVATE_PROJECT)
public AnswerText<Mail> textParserPrivateProject(
@Unit(CHECK_PARSER_PRIVATE_PROJECT_YES) MainUnit<Mail> checkParserPrivateProject
) {
return AnswerText.<Mail>builder()
.answer(() -> replaceBoxAnswer(
"Start tracking private projects?",
inlineKeyBoard(
simpleLine(
simpleButton("Yes", "YES"),
simpleButton("No", "NO")
)
)
)
)
.next(checkParserPrivateProject)
.build();
}
@Unit(CHECK_PARSER_PRIVATE_PROJECT_YES)
public AnswerText<Mail> checkParserPrivateProjectYes(
@Unit(TEXT_PARSE_OWNER_PROJECT) MainUnit<Mail> textParseOwnerProject
) {
final String step1 = """
-- -- -- -- --
🔘 Scanning of private projects has begun.
⌛ Wait...
-- -- -- -- --
""";
final String step2 = """
-- -- -- -- --
🟢 Projects have been successfully added to tracking. Found: {0}
🔘 Scanning merge requests in found projects.
⌛ Wait...
-- -- -- -- --
""";
final String step3 = """
-- -- -- -- --
🟢 Projects have been successfully added to tracking. Found: {0}
🟢 Merge requests have been successfully added. Found: {1}
🔘 Scanning pipelines in found merge requests.
⌛ Wait...
-- -- -- -- --
""";
final String step4 = """
-- -- -- -- --
🟢 Projects have been successfully added to tracking. Found: {0}
🟢 Merge requests have been successfully added. Found: {1}
🟢 Pipelines have been successfully added. Found: {2}
🔘 Scanning threads in merge requests.
⌛ Wait...
-- -- -- -- --
""";
final String finalAnswer = """
-- -- -- -- --
🟢 Projects have been successfully added to tracking. Found: {0}
🟢 Merge requests have been successfully added. Found: {1}
🟢 Pipelines have been successfully added. Found: {2}
🟢 Threads have been successfully added. Found: {3}
-- -- -- -- --
""";
return AnswerText.<Mail>builder()
.triggerCheck(clickButtonRaw("YES"))
.answer(mail -> {
final String personId = mail.getPersonId();
final Integer messageId = Attachments.findFirstButtonClick(mail.getAttachments())
.map(ButtonClickAttachment::getMessageId)
.orElseThrow();
sending.replaceMessage(personId, messageId, boxAnswer(step1));
projectParser.parseAllPrivateProject();
final int projectCount = projectService.getAllIds().size();
sending.replaceMessage(personId, messageId, boxAnswer(format(step2, projectCount)));
mergeRequestParser.parsingNewMergeRequest();
final int mrCount = mergeRequestsService.getAllIds().size();
sending.replaceMessage(personId, messageId, boxAnswer(format(step3, projectCount, mrCount)));
pipelineParser.scanNewPipeline();
final int pipelineCount = pipelineService.getAllIds().size();
sending.replaceMessage(personId, messageId, boxAnswer(format(step4, projectCount, mrCount, pipelineCount)));
discussionParser.scanNewDiscussion();
final int discussionCount = discussionService.getAllIds().size();
context.save(mail.getPersonId(), Keys.INIT_SETTING_PRIVATE_PROJECT_MESSAGE_ID, messageId);
return replaceBoxAnswer(format(finalAnswer, pipelineCount, mrCount, pipelineCount, discussionCount));
})
.next(textParseOwnerProject)
.build();
}
@Unit(CHECK_PARSER_PRIVATE_PROJECT_NO)
public AnswerText<Mail> checkParserPrivateProjectNo(
@Unit(TEXT_PARSE_OWNER_PROJECT) MainUnit<Mail> textParseOwnerProject
) {
return AnswerText.<Mail>builder()
.triggerPhrase("NO")
.answer(mail -> {
final Integer messageId = Attachments.findFirstButtonClick(mail.getAttachments())
.map(ButtonClickAttachment::getMessageId)
.orElseThrow();
context.save(mail.getPersonId(), Keys.INIT_SETTING_PRIVATE_PROJECT_MESSAGE_ID, messageId);
replaceBoxAnswer("Okay, I won't scan private projects.");
})
.next(textParseOwnerProject)
.build();
}
@Unit(TEXT_PARSE_OWNER_PROJECT)
public AnswerText<Mail> textParseOwnerProject(
@Unit(CHECK_PARSE_OWNER_PROJECT_YES) MainUnit<Mail> checkParseOwnerProjectYes,
@Unit(CHECK_PARSE_OWNER_PROJECT_NO) MainUnit<Mail> checkParseOwnerProjectNo
) {
return AnswerText.<Mail>builder()
.answer(
boxAnswer(
"Start tracking public projects that you own?",
inlineKeyBoard(
simpleLine(
simpleButton("Yes", "YES"),
simpleButton("No", "NO")
)
)
)
)
.activeType(AFTER)
.next(checkParseOwnerProjectYes)
.next(checkParseOwnerProjectNo)
.build();
}
@Unit(CHECK_PARSE_OWNER_PROJECT_YES)
public AnswerText<Mail> checkParseOwnerProjectYes(
@Unit(END_SETTING) MainUnit<Mail> endSetting
) {
final String step1 = """
-- -- -- -- --
🔘 Scanning of public projects has begun.
⌛ Wait...
-- -- -- -- --
""";
final String step2 = """
-- -- -- -- --
🟢 Projects have been successfully added to tracking. Found: {0}
🔘 Scanning merge requests in found projects.
⌛ Wait...
-- -- -- -- --
""";
final String step3 = """
-- -- -- -- --
🟢 Projects have been successfully added to tracking. Found: {0}
🟢 Merge requests have been successfully added. Found: {1}
🔘 Scanning pipelines in found merge requests.
⌛ Wait...
-- -- -- -- --
""";
final String step4 = """
-- -- -- -- --
🟢 Projects have been successfully added to tracking. Found: {0}
🟢 Merge requests have been successfully added. Found: {1}
🟢 Pipelines have been successfully added. Found: {2}
🔘 Scanning threads in merge requests.
⌛ Wait...
-- -- -- -- --
""";
final String finalAnswer = """
-- -- -- -- --
🟢 Projects have been successfully added to tracking. Found: {0}
🟢 Merge requests have been successfully added. Found: {1}
🟢 Pipelines have been successfully added. Found: {2}
🟢 Threads have been successfully added. Found: {3}
-- -- -- -- --
""";
return AnswerText.<Mail>builder()
.triggerCheck(clickButtonRaw("YES"))
.answer(mail -> {
final String personId = mail.getPersonId();
final Integer messageId = Attachments.findFirstButtonClick(mail.getAttachments())
.map(ButtonClickAttachment::getMessageId)
.orElseThrow();
sending.replaceMessage(personId, messageId, boxAnswer(step1));
projectParser.parseAllProjectOwner();
final int projectCount = projectService.getAllIds().size();
sending.replaceMessage(personId, messageId, boxAnswer(format(step2, projectCount)));
mergeRequestParser.parsingNewMergeRequest();
final int mrCount = mergeRequestsService.getAllIds().size();
sending.replaceMessage(personId, messageId, boxAnswer(format(step3, projectCount, mrCount)));
pipelineParser.scanNewPipeline();
final int pipelineCount = pipelineService.getAllIds().size();
sending.replaceMessage(personId, messageId, boxAnswer(format(step4, projectCount, mrCount, pipelineCount)));
discussionParser.scanNewDiscussion();
final int discussionCount = discussionService.getAllIds().size();
context.save(mail.getPersonId(), Keys.INIT_SETTING_PUBLIC_PROJECT_MESSAGE_ID, messageId);
return replaceBoxAnswer(format(finalAnswer, pipelineCount, mrCount, pipelineCount, discussionCount));
})
.next(endSetting)
.build();
}
@Unit(CHECK_PARSE_OWNER_PROJECT_NO)
public AnswerText<Mail> checkParseOwnerProjectNo(
@Unit(END_SETTING) MainUnit<Mail> endSetting
) {
return AnswerText.<Mail>builder()
.triggerCheck(clickButtonRaw("NO"))
.answer(mail -> {
final Integer messageId = Attachments.findFirstButtonClick(mail.getAttachments())
.map(ButtonClickAttachment::getMessageId)
.orElseThrow();
context.save(mail.getPersonId(), Keys.INIT_SETTING_PUBLIC_PROJECT_MESSAGE_ID, messageId);
return replaceBoxAnswer("Okay, I won't scan public projects.");
})
.next(endSetting)
.build();
}
@Unit(END_SETTING)
public AnswerText<Mail> endSetting() {
return AnswerText.<Mail>builder()
.activeType(AFTER)
.answer(
mail -> {
context.save(mail.getPersonId(), Keys.INIT_SETTING_FINISH, Boolean.TRUE);
settingService.isEnableAllNotify();
settingService.disableFirstStart();
return boxAnswer("""
Configuration completed successfully
Developer: [uPagge](https://mark.struchkov.dev)
""",
inlineKeyBoard(simpleButton("open menu", "INIT_SETTING_OPEN_MENU"))
);
}
)
.build();
}
}

View File

@@ -0,0 +1,13 @@
package dev.struchkov.bot.gitlab.telegram.utils;
import dev.struchkov.godfather.main.domain.ContextKey;
import lombok.experimental.UtilityClass;
@UtilityClass
public class Keys {
public static final ContextKey<Boolean> INIT_SETTING_FINISH = ContextKey.of("INIT_SETTING_FINISH", Boolean.class);
public static final ContextKey<Integer> INIT_SETTING_PRIVATE_PROJECT_MESSAGE_ID = ContextKey.of("INIT_SETTING_PRIVATE_PROJECT_MESSAGE_ID", Integer.class);
public static final ContextKey<Integer> INIT_SETTING_PUBLIC_PROJECT_MESSAGE_ID = ContextKey.of("INIT_SETTING_PUBLIC_PROJECT_MESSAGE_ID", Integer.class);
}

View File

@@ -10,18 +10,17 @@ public final class UnitName {
public static final String SETTINGS = "settings"; public static final String SETTINGS = "settings";
public static final String GET_TASKS = "getTasks"; public static final String GET_TASKS = "getTasks";
public static final String GET_ASSIGNEE_MERGE_REQUEST = "getAssigneeMergeRequest"; public static final String GET_ASSIGNEE_MERGE_REQUEST = "getAssigneeMergeRequest";
public static final String CHECK_FIRST_START = "checkFirstStart"; public static final String FIRST_START = "checkFirstStart";
public static final String CHECK_MENU_OR_ANSWER = "checkMenuOrAnswer";
public static final String ANSWER_NOTE = "answerNote"; public static final String ANSWER_NOTE = "answerNote";
public static final String TEXT_PARSER_PRIVATE_PROJECT = "textParserPrivateProject"; public static final String TEXT_PARSER_PRIVATE_PROJECT = "textParserPrivateProject";
public static final String CHECK_PARSER_PRIVATE_PROJECT = "checkParserPrivateProject"; public static final String CHECK_PARSER_PRIVATE_PROJECT_YES = "checkParserPrivateProject";
public static final String PARSER_PRIVATE_PROJECT = "parserPrivateProject";
public static final String TEXT_PARSE_OWNER_PROJECT = "textParseOwnerProject"; public static final String TEXT_PARSE_OWNER_PROJECT = "textParseOwnerProject";
public static final String CHECK_PARSE_OWNER_PROJECT = "checkParseOwnerProject"; public static final String CHECK_PARSE_OWNER_PROJECT_YES = "checkParseOwnerProject";
public static final String PARSE_OWNER_PROJECT = "parseOwnerProject";
public static final String END_SETTING = "endSetting"; public static final String END_SETTING = "endSetting";
public static final String AUTHORIZATION = "AUTHORIZATION";
public static final String ACCESS_ERROR = "ACCESS_ERROR"; public static final String ACCESS_ERROR = "ACCESS_ERROR";
public static final String CHECK_PARSER_PRIVATE_PROJECT_NO = "CHECK_PARSER_PRIVATE_PROJECT_NO";
public static final String CHECK_PARSE_OWNER_PROJECT_NO = "CHECK_PARSE_OWNER_PROJECT_NO";
public static final String REPLACE_GENERAL_MENU = "REPLACE_GENERAL_MENU";
private UnitName() { private UnitName() {
utilityClass(); utilityClass();