Compare commits

...

7 Commits

32 changed files with 244 additions and 107 deletions

2
.github/FUNDING.yml vendored
View File

@ -1 +1 @@
custom: ["https://tinkoff.ru/sl/NSGSK7FgEI"] custom: ["https://docs.struchkov.dev/gitlab-notification/support-development/"]

View File

@ -6,7 +6,7 @@
<parent> <parent>
<groupId>dev.struchkov.bot.gitlab</groupId> <groupId>dev.struchkov.bot.gitlab</groupId>
<artifactId>gitlab-bot</artifactId> <artifactId>gitlab-bot</artifactId>
<version>2.0.0-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
</parent> </parent>
<artifactId>bot-context</artifactId> <artifactId>bot-context</artifactId>

View File

@ -85,6 +85,13 @@ public class Discussion {
return this.notes.get(0); return this.notes.get(0);
} }
public Optional<Note> getLastNote() {
if (this.notes.size() > 1) {
return Optional.ofNullable(this.notes.get(this.notes.size() - 1));
}
return Optional.empty();
}
public Optional<Note> getPrevLastNote() { public Optional<Note> getPrevLastNote() {
final int size = notes.size(); final int size = notes.size();
if (size > 2) { if (size > 2) {

View File

@ -15,26 +15,24 @@ import static dev.struchkov.bot.gitlab.context.domain.notify.task.DiscussionNewN
*/ */
@Getter @Getter
@FieldNames @FieldNames
public class DiscussionNewNotify extends TaskNotify { public class DiscussionNewNotify extends ThreadNotify {
public static final String TYPE = CLASS_NAME; public static final String TYPE = CLASS_NAME;
private final String threadId; private final String threadId;
private final String mrName;
private final List<Pair<String, String>> notes; private final List<Pair<String, String>> notes;
@Builder @Builder
public DiscussionNewNotify( public DiscussionNewNotify(
String threadId, String threadId,
String mrName, String mergeRequestName,
String authorName, String authorName,
String url, String url,
String discussionMessage, String discussionMessage,
@Singular List<Pair<String, String>> notes @Singular List<Pair<String, String>> notes
) { ) {
super(authorName, url, discussionMessage); super(mergeRequestName, authorName, url, discussionMessage);
this.threadId = threadId; this.threadId = threadId;
this.mrName = mrName;
this.notes = notes; this.notes = notes;
} }

View File

@ -11,24 +11,31 @@ import static dev.struchkov.bot.gitlab.context.domain.notify.task.TaskCloseNotif
*/ */
@Getter @Getter
@FieldNames @FieldNames
public class TaskCloseNotify extends TaskNotify { public class ThreadCloseNotify extends ThreadNotify {
public static final String TYPE = CLASS_NAME; public static final String TYPE = CLASS_NAME;
private final Long personTasks; private final Long personTasks;
private final Long personResolvedTasks; private final Long personResolvedTasks;
private final String authorLastNote;
private final String messageLastNote;
@Builder @Builder
protected TaskCloseNotify( protected ThreadCloseNotify(
String mergeRequestName,
String authorName, String authorName,
String url, String url,
String messageTask, String messageTask,
Long personTasks, Long personTasks,
Long personResolvedTasks Long personResolvedTasks,
String authorLastNote,
String messageLastNote
) { ) {
super(authorName, url, messageTask); super(mergeRequestName, authorName, url, messageTask);
this.personTasks = personTasks; this.personTasks = personTasks;
this.personResolvedTasks = personResolvedTasks; this.personResolvedTasks = personResolvedTasks;
this.authorLastNote = authorLastNote;
this.messageLastNote = messageLastNote;
} }
@Override @Override

View File

@ -4,17 +4,20 @@ import dev.struchkov.bot.gitlab.context.domain.notify.Notify;
import lombok.Getter; import lombok.Getter;
@Getter @Getter
public abstract class TaskNotify implements Notify { public abstract class ThreadNotify implements Notify {
protected final String mergeRequestName;
protected final String authorName; protected final String authorName;
protected final String url; protected final String url;
protected final String messageTask; protected final String messageTask;
protected TaskNotify( protected ThreadNotify(
String mergeRequestName,
String authorName, String authorName,
String url, String url,
String messageTask String messageTask
) { ) {
this.mergeRequestName = mergeRequestName;
this.authorName = authorName; this.authorName = authorName;
this.url = url; this.url = url;
this.messageTask = messageTask; this.messageTask = messageTask;

View File

@ -13,7 +13,7 @@ public class Icons {
public static final String AUTHOR = "\uD83D\uDC68\u200D\uD83D\uDCBB"; public static final String AUTHOR = "\uD83D\uDC68\u200D\uD83D\uDCBB";
public static final String UPDATE = "\uD83D\uDD04"; public static final String UPDATE = "\uD83D\uDD04";
public static final String COMMENT = "\uD83D\uDCAC"; public static final String COMMENT = "\uD83D\uDCAC";
public static final String TASK = "\uD83D\uDCBC"; public static final String THREAD = "\uD83E\uDDF5";
public static final String ARROW = ""; public static final String ARROW = "";
public static final String DANGEROUS = "⚠️"; public static final String DANGEROUS = "⚠️";
public static final String GREEN_CIRCLE = "\uD83D\uDFE2"; public static final String GREEN_CIRCLE = "\uD83D\uDFE2";
@ -27,7 +27,6 @@ public class Icons {
public static final String YES = ""; public static final String YES = "";
public static final String NO = ""; public static final String NO = "";
public static final String NOTIFY = "\uD83D\uDD14"; public static final String NOTIFY = "\uD83D\uDD14";
public static final String NO_PROCESSING = "\uD83D\uDDD1";
public static final String GOOD = "\uD83D\uDC4D"; public static final String GOOD = "\uD83D\uDC4D";
private Icons() { private Icons() {

View File

@ -6,7 +6,7 @@
<parent> <parent>
<groupId>dev.struchkov.bot.gitlab</groupId> <groupId>dev.struchkov.bot.gitlab</groupId>
<artifactId>gitlab-bot</artifactId> <artifactId>gitlab-bot</artifactId>
<version>2.0.0-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
</parent> </parent>
<artifactId>bot-core</artifactId> <artifactId>bot-core</artifactId>

View File

@ -9,7 +9,7 @@ import dev.struchkov.bot.gitlab.context.domain.entity.Person;
import dev.struchkov.bot.gitlab.context.domain.notify.comment.NewCommentNotify; import dev.struchkov.bot.gitlab.context.domain.notify.comment.NewCommentNotify;
import dev.struchkov.bot.gitlab.context.domain.notify.level.DiscussionLevel; import dev.struchkov.bot.gitlab.context.domain.notify.level.DiscussionLevel;
import dev.struchkov.bot.gitlab.context.domain.notify.task.DiscussionNewNotify; import dev.struchkov.bot.gitlab.context.domain.notify.task.DiscussionNewNotify;
import dev.struchkov.bot.gitlab.context.domain.notify.task.TaskCloseNotify; import dev.struchkov.bot.gitlab.context.domain.notify.task.ThreadCloseNotify;
import dev.struchkov.bot.gitlab.context.repository.DiscussionRepository; import dev.struchkov.bot.gitlab.context.repository.DiscussionRepository;
import dev.struchkov.bot.gitlab.context.service.AppSettingService; import dev.struchkov.bot.gitlab.context.service.AppSettingService;
import dev.struchkov.bot.gitlab.context.service.DiscussionService; import dev.struchkov.bot.gitlab.context.service.DiscussionService;
@ -44,7 +44,9 @@ import static dev.struchkov.bot.gitlab.context.domain.notify.level.DiscussionLev
import static dev.struchkov.bot.gitlab.context.domain.notify.level.DiscussionLevel.WITHOUT_NOTIFY; import static dev.struchkov.bot.gitlab.context.domain.notify.level.DiscussionLevel.WITHOUT_NOTIFY;
import static dev.struchkov.haiti.context.exception.NotFoundException.notFoundException; import static dev.struchkov.haiti.context.exception.NotFoundException.notFoundException;
import static dev.struchkov.haiti.utils.Checker.checkNotNull; import static dev.struchkov.haiti.utils.Checker.checkNotNull;
import static dev.struchkov.haiti.utils.Checker.checkNull;
import static java.lang.Boolean.FALSE; import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
/** /**
* Сервис для работы с дискуссиями. * Сервис для работы с дискуссиями.
@ -80,7 +82,7 @@ public class DiscussionServiceImpl implements DiscussionService {
if (isNeedNotifyNewNote(discussion)) { if (isNeedNotifyNewNote(discussion)) {
notifyNewThread(discussion); notifyNewThread(discussion);
} else { } else {
notes.forEach(note -> notificationPersonal(discussion, note)); notes.forEach(note -> notifyAboutPersonalAnswer(discussion, note));
} }
} else { } else {
discussion.setNotification(false); discussion.setNotification(false);
@ -119,14 +121,14 @@ public class DiscussionServiceImpl implements DiscussionService {
} }
} }
final boolean resolved = discussion.getNotes().stream()
.allMatch(note -> note.isResolvable() && note.getResolved());
discussion.setResolved(resolved);
if (oldDiscussion.isNotification()) { if (oldDiscussion.isNotification()) {
notifyUpdateNote(oldDiscussion, discussion); notifyUpdateNote(oldDiscussion, discussion);
} }
final boolean resolved = discussion.getNotes().stream()
.allMatch(note -> note.isResolvable() && note.getResolved());
discussion.setResolved(resolved);
return repository.save(discussion); return repository.save(discussion);
} }
@ -148,7 +150,7 @@ public class DiscussionServiceImpl implements DiscussionService {
} }
private void notifyUpdateNote(Discussion oldDiscussion, Discussion discussion) { private void notifyUpdateNote(Discussion oldDiscussion, Discussion discussion) {
final Map<Long, Note> noteMap = oldDiscussion final Map<Long, Note> oldNoteMap = oldDiscussion
.getNotes().stream() .getNotes().stream()
.collect(Collectors.toMap(Note::getId, n -> n)); .collect(Collectors.toMap(Note::getId, n -> n));
@ -156,56 +158,73 @@ public class DiscussionServiceImpl implements DiscussionService {
final boolean userParticipatedInDiscussion = oldDiscussion.getNotes().stream() final boolean userParticipatedInDiscussion = oldDiscussion.getNotes().stream()
.anyMatch(note -> personInformation.getId().equals(note.getAuthor().getId())); .anyMatch(note -> personInformation.getId().equals(note.getAuthor().getId()));
final Note threadFirstNote = discussion.getFirstNote();
if (TRUE.equals(discussion.getResolved())) {
notifyAboutCloseThread(threadFirstNote, oldNoteMap.get(threadFirstNote.getId()), discussion.getLastNote());
}
for (Note newNote : discussion.getNotes()) { for (Note newNote : discussion.getNotes()) {
final Long newNoteId = newNote.getId(); final Long newNoteId = newNote.getId();
if (noteMap.containsKey(newNoteId)) { if (!oldNoteMap.containsKey(newNoteId)) {
final Note oldNote = noteMap.get(newNoteId);
if (newNote.isResolvable()) {
updateTask(newNote, oldNote);
}
} else {
if (userParticipatedInDiscussion) { if (userParticipatedInDiscussion) {
notifyNewAnswer(discussion, newNote); notifyAboutNewAnswer(discussion, newNote);
} else { } else {
notificationPersonal(discussion, newNote); notifyAboutPersonalAnswer(discussion, newNote);
} }
} }
} }
} }
private void updateTask(Note note, Note oldNote) { private void notifyAboutCloseThread(Note newNote, Note oldNote, Optional<Note> lastNote) {
if (isResolved(note, oldNote)) { final DiscussionLevel level = settingService.getLevelDiscussionNotify();
final MergeRequestForDiscussion mergeRequest = oldNote.getDiscussion().getMergeRequest(); if (!WITHOUT_NOTIFY.equals(level)) {
final List<Discussion> discussions = getAllByMergeRequestId(mergeRequest.getId())
.stream() if (isResolved(newNote, oldNote)) {
.filter(discussion -> Objects.nonNull(discussion.getResponsible())) final MergeRequestForDiscussion mergeRequest = oldNote.getDiscussion().getMergeRequest();
.toList();
final long allYouTasks = discussions.stream() final List<Discussion> discussions = getAllByMergeRequestId(mergeRequest.getId())
.filter(discussion -> personInformation.getId().equals(discussion.getFirstNote().getAuthor().getId())) .stream()
.count(); .filter(discussion -> Objects.nonNull(discussion.getResponsible()))
final long resolvedYouTask = discussions.stream() .toList();
.filter(discussion -> personInformation.getId().equals(discussion.getFirstNote().getAuthor().getId()) && discussion.getResolved()) final long allYouTasks = discussions.stream()
.count(); .filter(discussion -> personInformation.getId().equals(discussion.getFirstNote().getAuthor().getId()))
notifyService.send( .count();
TaskCloseNotify.builder() final long resolvedYouTask = discussions.stream()
.filter(discussion -> personInformation.getId().equals(discussion.getFirstNote().getAuthor().getId()) && discussion.getResolved())
.count();
final ThreadCloseNotify.ThreadCloseNotifyBuilder notifyBuilder = ThreadCloseNotify.builder()
.mergeRequestName(mergeRequest.getTitle())
.url(oldNote.getWebUrl())
.personTasks(allYouTasks)
.personResolvedTasks(resolvedYouTask);
if (NOTIFY_WITH_CONTEXT.equals(level)) {
notifyBuilder
.authorName(oldNote.getAuthor().getName()) .authorName(oldNote.getAuthor().getName())
.messageTask(oldNote.getBody()) .messageTask(oldNote.getBody());
.url(oldNote.getWebUrl())
.personTasks(allYouTasks)
.personResolvedTasks(resolvedYouTask) lastNote.ifPresent(
.build() note -> {
); notifyBuilder.authorLastNote(note.getAuthor().getName());
notifyBuilder.messageLastNote(note.getBody());
}
);
}
notifyService.send(notifyBuilder.build());
}
} }
} }
private boolean isResolved(Note note, Note oldNote) { private boolean isResolved(Note note, Note oldNote) {
return oldNote.getResolvedBy() == null return checkNull(oldNote.getResolvedBy()) // В старом комментарии не было отметки о решении
&& note.getResolvedBy() != null && checkNotNull(note.getResolvedBy()) // А в новом есть отметка
&& personInformation.getId().equals(oldNote.getAuthor().getId()) && personInformation.getId().equals(oldNote.getAuthor().getId()) // и решающий не является пользователем бота
&& !note.getResolvedBy().getId().equals(oldNote.getAuthor().getId()); && !note.getResolvedBy().getId().equals(oldNote.getAuthor().getId()); // и решающий не является автором треда
} }
@ -288,7 +307,7 @@ public class DiscussionServiceImpl implements DiscussionService {
repository.notification(enable, discussionId); repository.notification(enable, discussionId);
} }
private void notifyNewAnswer(Discussion discussion, Note note) { private void notifyAboutNewAnswer(Discussion discussion, Note note) {
final DiscussionLevel discussionLevel = settingService.getLevelDiscussionNotify(); final DiscussionLevel discussionLevel = settingService.getLevelDiscussionNotify();
if (!WITHOUT_NOTIFY.equals(discussionLevel) if (!WITHOUT_NOTIFY.equals(discussionLevel)
@ -322,7 +341,7 @@ public class DiscussionServiceImpl implements DiscussionService {
/** /**
* Уведомляет пользователя, если его никнейм упоминается в комментарии. * Уведомляет пользователя, если его никнейм упоминается в комментарии.
*/ */
private void notificationPersonal(Discussion discussion, Note note) { private void notifyAboutPersonalAnswer(Discussion discussion, Note note) {
final DiscussionLevel discussionLevel = settingService.getLevelDiscussionNotify(); final DiscussionLevel discussionLevel = settingService.getLevelDiscussionNotify();
if (!WITHOUT_NOTIFY.equals(discussionLevel)) { if (!WITHOUT_NOTIFY.equals(discussionLevel)) {
final Matcher matcher = PATTERN.matcher(note.getBody()); final Matcher matcher = PATTERN.matcher(note.getBody());
@ -372,10 +391,10 @@ public class DiscussionServiceImpl implements DiscussionService {
final Note firstNote = discussion.getFirstNote(); final Note firstNote = discussion.getFirstNote();
final MergeRequestForDiscussion mergeRequest = discussion.getMergeRequest(); final MergeRequestForDiscussion mergeRequest = discussion.getMergeRequest();
DiscussionNewNotify.DiscussionNewNotifyBuilder messageBuilder = DiscussionNewNotify.builder() final DiscussionNewNotify.DiscussionNewNotifyBuilder messageBuilder = DiscussionNewNotify.builder()
.url(firstNote.getWebUrl()) .url(firstNote.getWebUrl())
.threadId(discussion.getId()) .threadId(discussion.getId())
.mrName(mergeRequest.getTitle()) .mergeRequestName(mergeRequest.getTitle())
.authorName(firstNote.getAuthor().getName()); .authorName(firstNote.getAuthor().getName());
if (NOTIFY_WITH_CONTEXT.equals(discussionLevel)) { if (NOTIFY_WITH_CONTEXT.equals(discussionLevel)) {

View File

@ -6,7 +6,7 @@
<parent> <parent>
<groupId>dev.struchkov.bot.gitlab</groupId> <groupId>dev.struchkov.bot.gitlab</groupId>
<artifactId>gitlab-bot</artifactId> <artifactId>gitlab-bot</artifactId>
<version>2.0.0-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
</parent> </parent>
<artifactId>bot-data</artifactId> <artifactId>bot-data</artifactId>

View File

@ -0,0 +1,20 @@
---
title: Быстрый старт
hide:
- toc
- comments
---
# GitLab Notification Персональный Telegram для GitLab
Персональный помощник для взаимодействия с GitLab. Получайте уведомления о событиях в GitLab: новые MR, где вы ревьювер, конфликты в ваших MR, уведомления о новых сообщениях в тредах, где вы являетесь участником, и многое другое.
## :ninja: Privacy
Мое решение сфокусировано на приватности и прозрачности. Код и используемые зависимости полностью открыты и доступны для изучения и самостоятельной сборки.
Для работы бота токен доступа устанавливается в переменные среды и никуда не передается, кроме запросов в GitLab.
Некоторые уведомления могут содержать чуствительную информацию. Например, уведомления о новых сообщениях в тредах. Возможно вы не захотите раскрывать столько информации о ваших репозиториях Телеграму, ведь через него идет получение уведомлений. Специально для таких случаев предусмотрены уровни конфиденциальности разных типов уведомлений.
Возьмем для примера уведомление о новом сообщении в треде. При минимальном уровне конфиденциальности вы получите уведомление с текстом коментария и сможете сразу ответить на него в телеграм, а при максимальном уровне конфиденциальности будет сообщаться только о факте нового комментария, без содержания. Все это настраивается при первом запуске.

View File

@ -0,0 +1,9 @@
---
title: Поддержать разработку
hide:
- comments
---
wefwef

View File

@ -5,15 +5,18 @@ repo_url: https://github.com/uPagge/gitlab-notification
repo_name: uPagge/gitlab-notification repo_name: uPagge/gitlab-notification
#edit_uri: edit/master/documentation/docs #edit_uri: edit/master/documentation/docs
nav: nav:
- О проекте: index.md - О проекте: ru/index.md
- Возможности: getting-started/features.md - Возможности: ru/getting-started/features.md
- Быстрый старт: - Быстрый старт:
- Запуск: getting-started/configuration.md - Запуск: ru/getting-started/configuration.md
- Архитектура: - Архитектура:
- Концепт: architecture/concept.md - Концепт: ru/architecture/concept.md
- "Поддержать разработку":
- ru/support-development/index.md
- ChangeLog: - ChangeLog:
- changelog/index.md - ru/changelog/index.md
theme: theme:
name: material name: material
@ -88,7 +91,10 @@ extra:
property: foobar property: foobar
version: version:
provider: mike provider: mike
# alternate: alternate:
# - name: Русский - name: Русский
# link: /ru/ link: /ru/
# lang: ru lang: ru
- name: English
link: /en/
lang: en

View File

@ -6,7 +6,7 @@
<parent> <parent>
<groupId>dev.struchkov.bot.gitlab</groupId> <groupId>dev.struchkov.bot.gitlab</groupId>
<artifactId>gitlab-bot</artifactId> <artifactId>gitlab-bot</artifactId>
<version>2.0.0-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
</parent> </parent>
<artifactId>gitlab-app</artifactId> <artifactId>gitlab-app</artifactId>

View File

@ -36,7 +36,7 @@ telegram:
password: ${PROXY_PASSWORD:} password: ${PROXY_PASSWORD:}
gitlab-bot: gitlab-bot:
version: 2.0.0-SNAPSHOT version: 0.0.1-SNAPSHOT
person: person:
telegram-id: ${TELEGRAM_PERSON_ID} telegram-id: ${TELEGRAM_PERSON_ID}
token: ${GITLAB_PERSONAL_TOKEN} token: ${GITLAB_PERSONAL_TOKEN}

View File

@ -3,6 +3,6 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.17.xsd"> xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.17.xsd">
<include file="v.2.0.0/changelog.xml" relativeToChangelogFile="true"/> <include file="v.0.0.1/changelog.xml" relativeToChangelogFile="true"/>
</databaseChangeLog> </databaseChangeLog>

View File

@ -6,7 +6,7 @@
<parent> <parent>
<groupId>dev.struchkov.bot.gitlab</groupId> <groupId>dev.struchkov.bot.gitlab</groupId>
<artifactId>gitlab-bot</artifactId> <artifactId>gitlab-bot</artifactId>
<version>2.0.0-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
</parent> </parent>
<artifactId>gitlab-sdk</artifactId> <artifactId>gitlab-sdk</artifactId>

View File

@ -11,7 +11,7 @@
<groupId>dev.struchkov.bot.gitlab</groupId> <groupId>dev.struchkov.bot.gitlab</groupId>
<artifactId>gitlab-bot</artifactId> <artifactId>gitlab-bot</artifactId>
<version>2.0.0-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<modules> <modules>

View File

@ -6,7 +6,7 @@
<parent> <parent>
<groupId>dev.struchkov.bot.gitlab</groupId> <groupId>dev.struchkov.bot.gitlab</groupId>
<artifactId>gitlab-bot</artifactId> <artifactId>gitlab-bot</artifactId>
<version>2.0.0-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
</parent> </parent>
<artifactId>telegram-bot</artifactId> <artifactId>telegram-bot</artifactId>

View File

@ -5,24 +5,39 @@ import dev.struchkov.bot.gitlab.context.utils.Icons;
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.bot.gitlab.core.config.properties.PersonProperty;
import dev.struchkov.godfather.simple.domain.BoxAnswer; import dev.struchkov.godfather.simple.domain.BoxAnswer;
import dev.struchkov.godfather.simple.domain.SentBox;
import dev.struchkov.godfather.telegram.simple.context.service.TelegramSending; import dev.struchkov.godfather.telegram.simple.context.service.TelegramSending;
import dev.struchkov.godfather.telegram.simple.context.service.TelegramService;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.Optional;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.DELETE_MESSAGE; import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.DELETE_MESSAGE;
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.telegram.domain.keyboard.InlineKeyBoard.inlineKeyBoard; import static dev.struchkov.godfather.telegram.domain.keyboard.InlineKeyBoard.inlineKeyBoard;
import static dev.struchkov.godfather.telegram.main.context.BoxAnswerPayload.DISABLE_WEB_PAGE_PREVIEW; import static dev.struchkov.godfather.telegram.main.context.BoxAnswerPayload.DISABLE_WEB_PAGE_PREVIEW;
import static dev.struchkov.haiti.utils.Checker.checkNotBlank;
/** /**
* @author upagge 19.01.2021 * @author upagge 19.01.2021
*/ */
@Component @Component
@Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
public class StartNotify { public class StartNotify {
private final OkHttpClient client = new OkHttpClient();
private final TelegramSending sending; private final TelegramSending sending;
private final TelegramService telegramService;
private final AppProperty appProperty; private final AppProperty appProperty;
private final AppSettingService settingService; private final AppSettingService settingService;
private final PersonProperty personProperty; private final PersonProperty personProperty;
@ -35,9 +50,9 @@ public class StartNotify {
.message( .message(
"Hello \uD83D\uDC4B\nI wish you a productive day \uD83C\uDF40" + "Hello \uD83D\uDC4B\nI wish you a productive day \uD83C\uDF40" +
"\n-- -- -- -- --\n" + "\n-- -- -- -- --\n" +
"\uD83E\uDD16 Bot Version: " + appProperty.getVersion() + "\uD83E\uDD16 Version: " + appProperty.getVersion() +
"\n\uD83D\uDC68\u200D\uD83D\uDCBB Developer: [uPagge](https://mark.struchkov.dev)\n" + "\n\uD83D\uDC68\u200D\uD83D\uDCBB Developer: [Struchkov.Dev](https://mark.struchkov.dev)\n-- -- -- -- --\n" +
"\uD83C\uDFE0 [HomePage](https://git.struchkov.dev/Telegram-Bots/gitlab-notification) • \uD83D\uDC1B [Issues](https://github.com/uPagge/gitlab-notification/issues) • \uD83D\uDEE3 [RoadMap](https://git.struchkov.dev/Telegram-Bots/gitlab-notification/issues)" "\uD83C\uDFE0 [HomePage](https://docs.struchkov.dev/gitlab-notification/) • \uD83D\uDC1B [Report Bug](https://github.com/uPagge/gitlab-notification/issues) • \uD83C\uDD98 [Donation](https://docs.struchkov.dev/gitlab-notification/support-development/)"
) )
.keyBoard( .keyBoard(
inlineKeyBoard( inlineKeyBoard(
@ -48,6 +63,40 @@ public class StartNotify {
.payload(DISABLE_WEB_PAGE_PREVIEW, true) .payload(DISABLE_WEB_PAGE_PREVIEW, true)
.build(); .build();
sending.send(boxAnswer); sending.send(boxAnswer);
sendNotice();
}
}
/**
* Используется для уведомления пользователя о выходе новой версии.
*/
private void sendNotice() {
final String requestUrl = "https://metrika.struchkov.dev/gitlab-notify/start-notice";
final Request request = new Request.Builder()
.get()
.url(requestUrl)
.build();
try {
final Response response = client.newCall(request).execute();
if (response.code() == 200) {
final String noticeMessage = response.body().string();
if (checkNotBlank(noticeMessage)) {
final BoxAnswer notice = BoxAnswer.builder()
.message(noticeMessage)
.recipientPersonId(personProperty.getTelegramId())
// .payload(DISABLE_WEB_PAGE_PREVIEW, true)
.build();
final Optional<SentBox> optSentBox = sending.send(notice);
if (optSentBox.isPresent()) {
final SentBox sentBox = optSentBox.get();
final String messageId = sentBox.getMessageId();
telegramService.pinMessage(personProperty.getTelegramId(), messageId);
}
}
}
} catch (IOException e) {
log.warn(e.getMessage());
} }
} }

View File

@ -24,26 +24,22 @@ public class NewCommentNotifyGenerator implements NotifyBoxAnswerGenerator<NewCo
@Override @Override
public BoxAnswer generate(NewCommentNotify notify) { public BoxAnswer generate(NewCommentNotify notify) {
final StringBuilder builder = new StringBuilder(Icons.COMMENT).append(" *New answer in Thread*") final StringBuilder builder = new StringBuilder(Icons.COMMENT).append(" *New answer in Thread*")
.append(Icons.HR) .append("\n-- -- -- merge request -- -- --\n")
.append(Icons.link(escapeMarkdown(notify.getMergeRequestName()), notify.getUrl())) .append(Icons.link(escapeMarkdown(notify.getMergeRequestName()), notify.getUrl()));
.append("\n");
if (checkNotNull(notify.getDiscussionMessage())) { if (checkNotNull(notify.getDiscussionMessage())) {
builder.append("\n-- -- thread first message -- --\n") builder.append("\n\n-- -- thread first message -- --\n")
.append("*").append(notify.getDiscussionAuthor()).append("*: ").append(escapeMarkdown(notify.getDiscussionMessage())) .append("*").append(notify.getDiscussionAuthor()).append("*: ").append(escapeMarkdown(notify.getDiscussionMessage()));
.append("\n");
} }
if (checkNotNull(notify.getPreviousMessage())) { if (checkNotNull(notify.getPreviousMessage())) {
builder.append("\n-- -- -- previous message -- -- --\n") builder.append("\n\n-- -- -- previous message -- -- --\n")
.append("*").append(notify.getPreviousAuthor()).append("*: ").append(escapeMarkdown(notify.getPreviousMessage())) .append("*").append(notify.getPreviousAuthor()).append("*: ").append(escapeMarkdown(notify.getPreviousMessage()));
.append("\n");
} }
if (checkNotNull(notify.getMessage())) { if (checkNotNull(notify.getMessage())) {
builder.append("\n-- -- -- --- new answer --- -- -- --\n") builder.append("\n\n-- -- -- --- new answer --- -- -- --\n")
.append("*").append(notify.getAuthorName()).append("*: ").append(escapeMarkdown(notify.getMessage())) .append("*").append(notify.getAuthorName()).append("*: ").append(escapeMarkdown(notify.getMessage()));
.append("\n");
} }
final String messageNotify = builder.toString(); final String messageNotify = builder.toString();

View File

@ -25,15 +25,15 @@ public class NewThreadNotifyGenerator implements NotifyBoxAnswerGenerator<Discus
@Override @Override
public BoxAnswer generate(DiscussionNewNotify notify) { public BoxAnswer generate(DiscussionNewNotify notify) {
final StringBuilder builder = new StringBuilder(Icons.TASK).append(" *New Thread in your MR*") final StringBuilder builder = new StringBuilder(Icons.THREAD).append(" *New Thread in your MR*")
.append(Icons.HR) .append("\n -- -- -- merge request -- -- --\n")
.append(Icons.link(escapeMarkdown(notify.getMrName()), notify.getUrl())) .append(Icons.link(escapeMarkdown(notify.getMergeRequestName()), notify.getUrl()))
.append(Icons.HR) .append("\n\n -- -- -- thread message -- -- --\n")
.append("*").append(notify.getAuthorName()).append("*: ").append(escapeMarkdown(notify.getMessageTask())); .append("*").append(notify.getAuthorName()).append("*: ").append(escapeMarkdown(notify.getMessageTask()));
final List<Pair<String, String>> notes = notify.getNotes(); final List<Pair<String, String>> notes = notify.getNotes();
if (checkNotEmpty(notes)) { if (checkNotEmpty(notes)) {
builder.append("\n-- -- -- -- comments -- -- -- --\n") builder.append("\n\n-- -- -- -- comments -- -- -- --\n")
.append(convertNotes(notes)); .append(convertNotes(notes));
} }

View File

@ -1,31 +1,55 @@
package dev.struchkov.bot.gitlab.telegram.service.notify; package dev.struchkov.bot.gitlab.telegram.service.notify;
import dev.struchkov.bot.gitlab.context.domain.notify.task.TaskCloseNotify; import dev.struchkov.bot.gitlab.context.domain.notify.task.ThreadCloseNotify;
import dev.struchkov.bot.gitlab.context.utils.Icons; import dev.struchkov.bot.gitlab.context.utils.Icons;
import dev.struchkov.godfather.simple.domain.BoxAnswer; import dev.struchkov.godfather.simple.domain.BoxAnswer;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import static dev.struchkov.bot.gitlab.context.utils.Icons.link; import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.DELETE_MESSAGE;
import static dev.struchkov.godfather.main.domain.keyboard.button.SimpleButton.simpleButton;
import static dev.struchkov.godfather.simple.domain.BoxAnswer.boxAnswer; import static dev.struchkov.godfather.simple.domain.BoxAnswer.boxAnswer;
import static dev.struchkov.godfather.telegram.domain.keyboard.InlineKeyBoard.inlineKeyBoard;
import static dev.struchkov.godfather.telegram.domain.keyboard.button.UrlButton.urlButton;
import static dev.struchkov.haiti.utils.Checker.checkNotBlank;
import static dev.struchkov.haiti.utils.Strings.escapeMarkdown; import static dev.struchkov.haiti.utils.Strings.escapeMarkdown;
@Component @Component
public class ThreadCloseNotifyGenerate implements NotifyBoxAnswerGenerator<TaskCloseNotify> { public class ThreadCloseNotifyGenerate implements NotifyBoxAnswerGenerator<ThreadCloseNotify> {
@Override @Override
public BoxAnswer generate(TaskCloseNotify notify) { public BoxAnswer generate(ThreadCloseNotify notify) {
final StringBuilder builder = new StringBuilder(Icons.TASK).append(" *Closed ").append(link("task", notify.getUrl())) final StringBuilder builder = new StringBuilder(Icons.THREAD).append(" *Closed thread*")
.append(Icons.HR) .append("\n-- -- -- merge request -- -- --\n")
.append("*").append(notify.getAuthorName()).append("*: ").append(escapeMarkdown(notify.getMessageTask())); .append(escapeMarkdown(notify.getMergeRequestName()))
.append("\n");
if (checkNotBlank(notify.getAuthorName())) {
builder
.append("\n-- -- -- thread message -- -- --\n")
.append("*").append(escapeMarkdown(notify.getAuthorName())).append("*: ").append(escapeMarkdown(notify.getMessageTask()))
.append("\n");
}
if (checkNotBlank(notify.getAuthorLastNote())) {
builder
.append("\n-- -- -- last message -- -- -- --\n")
.append("*").append(escapeMarkdown(notify.getAuthorLastNote())).append("*: ").append(escapeMarkdown(notify.getMessageLastNote()));
}
final String notifyMessage = builder.toString(); final String notifyMessage = builder.toString();
return boxAnswer(notifyMessage); return boxAnswer(
notifyMessage,
inlineKeyBoard(
simpleButton(Icons.VIEW, DELETE_MESSAGE),
urlButton(Icons.LINK, notify.getUrl())
)
);
} }
@Override @Override
public String getNotifyType() { public String getNotifyType() {
return TaskCloseNotify.TYPE; return ThreadCloseNotify.TYPE;
} }
} }