Стабильная рабочая версия

This commit is contained in:
upagge 2020-04-08 00:43:27 +03:00
parent b74f7ae7da
commit f4ab127766
No known key found for this signature in database
GPG Key ID: 15CD012E46F6BA34
17 changed files with 213 additions and 81 deletions

View File

@ -13,7 +13,7 @@ public class AppConfig {
@Bean @Bean
public TaskScheduler taskScheduler() { public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(7); taskScheduler.setPoolSize(10);
return taskScheduler; return taskScheduler;
} }

View File

@ -0,0 +1,16 @@
package com.tsc.bitbucketbot.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "bitbucketbot.init")
public class InitConfig {
private Long startCommentId;
}

View File

@ -0,0 +1,19 @@
package com.tsc.bitbucketbot.domain;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class Answer {
private final String authorName;
private final String message;
public static Answer of(@NonNull String name, @NonNull String message) {
return new Answer(name, message);
}
}

View File

@ -0,0 +1,33 @@
package com.tsc.bitbucketbot.domain.change;
import com.tsc.bitbucketbot.domain.Answer;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Singular;
import java.util.List;
import java.util.Set;
@Getter
@EqualsAndHashCode(callSuper = true)
public class AnswerCommentChange extends Change {
private final String youMessage;
private final String url;
private final List<Answer> answers;
@Builder
protected AnswerCommentChange(
@Singular("telegramId") Set<Long> telegramId,
String youMessage,
String url,
List<Answer> answers
) {
super(ChangeType.NEW_ANSWERS_COMMENT, telegramId);
this.youMessage = youMessage;
this.url = url;
this.answers = answers;
}
}

View File

@ -2,6 +2,12 @@ package com.tsc.bitbucketbot.domain.change;
public enum ChangeType { public enum ChangeType {
STATUS_PR, UPDATE_PR, REVIEWERS, NEW_PR, CONFLICT_PR STATUS_PR,
UPDATE_PR,
REVIEWERS,
NEW_PR,
CONFLICT_PR,
NEW_COMMENT,
NEW_ANSWERS_COMMENT
} }

View File

@ -0,0 +1,30 @@
package com.tsc.bitbucketbot.domain.change;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Singular;
import java.util.Set;
@Getter
@EqualsAndHashCode(callSuper = true)
public class CommentChange extends PrChange {
private final String authorName;
private final String message;
@Builder
private CommentChange(
@Singular("telegramId") Set<Long> telegramId,
String name,
String url,
String authorName,
String message
) {
super(ChangeType.NEW_COMMENT, telegramId, name, url);
this.authorName = authorName;
this.message = message;
}
}

View File

@ -15,7 +15,7 @@ import javax.persistence.Entity;
import javax.persistence.FetchType; import javax.persistence.FetchType;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
import java.time.LocalDate; import java.time.LocalDateTime;
import java.util.Set; import java.util.Set;
@Getter @Getter
@ -30,17 +30,19 @@ public class Comment {
@Id @Id
@Column(name = "id") @Column(name = "id")
// @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; private Long id;
@Column(name = "url") @Column(name = "url")
private String url; private String url;
@Column(name = "pr_url")
private String prUrl;
@Column(name = "telegram") @Column(name = "telegram")
private Long telegram; private Long telegram;
@Column(name = "date") @Column(name = "date")
private LocalDate date; private LocalDateTime date;
@ElementCollection(fetch = FetchType.EAGER) @ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(name = "comment_tree", joinColumns = @JoinColumn(name = "parent_id")) @CollectionTable(name = "comment_tree", joinColumns = @JoinColumn(name = "parent_id"))

View File

@ -3,7 +3,7 @@ package com.tsc.bitbucketbot.repository.jpa;
import com.tsc.bitbucketbot.domain.entity.Comment; import com.tsc.bitbucketbot.domain.entity.Comment;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import java.time.LocalDate; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -11,6 +11,6 @@ public interface CommentRepository extends JpaRepository<Comment, Long> {
Optional<Comment> findFirstByOrderByIdDesc(); Optional<Comment> findFirstByOrderByIdDesc();
List<Comment> findByDateBetween(LocalDate dateFrom, LocalDate dateTo); List<Comment> findByDateBetween(LocalDateTime dateFrom, LocalDateTime dateTo);
} }

View File

@ -1,6 +1,9 @@
package com.tsc.bitbucketbot.scheduler; package com.tsc.bitbucketbot.scheduler;
import com.tsc.bitbucketbot.domain.MessageSend;
import com.tsc.bitbucketbot.domain.change.AnswerCommentChange;
import com.tsc.bitbucketbot.domain.change.Change; import com.tsc.bitbucketbot.domain.change.Change;
import com.tsc.bitbucketbot.domain.change.CommentChange;
import com.tsc.bitbucketbot.domain.change.ConflictPrChange; import com.tsc.bitbucketbot.domain.change.ConflictPrChange;
import com.tsc.bitbucketbot.domain.change.NewPrChange; import com.tsc.bitbucketbot.domain.change.NewPrChange;
import com.tsc.bitbucketbot.domain.change.ReviewersPrChange; import com.tsc.bitbucketbot.domain.change.ReviewersPrChange;
@ -25,23 +28,21 @@ public class SchedulerChangeParsing {
private final MessageSendService messageSendService; private final MessageSendService messageSendService;
private final ChangeService changeService; private final ChangeService changeService;
// @Scheduled(cron = "0 * * * * *") @Scheduled(cron = "30 */1 * * * MON-FRI")
@Scheduled(fixedRate = 5000)
public void parsing() { public void parsing() {
final List<Change> newChange = changeService.getNew().stream() final List<Change> newChange = changeService.getNew().stream()
.filter(change -> change.getTelegramId() != null && !change.getTelegramId().isEmpty()) .filter(change -> change.getTelegramId() != null && !change.getTelegramId().isEmpty())
.collect(Collectors.toList()); .collect(Collectors.toList());
for (Change change : newChange) { for (Change change : newChange) {
final String message = generateMessage(change); final String message = generateMessage(change);
System.out.println(message); change.getTelegramId().forEach(
// change.getTelegramId().forEach( telegramId -> messageSendService.add(
// telegramId -> messageSendService.add( MessageSend.builder()
// MessageSend.builder() .telegramId(telegramId)
// .telegramId(telegramId) .message(message)
// .message(message) .build()
// .build() )
// ) );
// );
} }
} }
@ -63,6 +64,12 @@ public class SchedulerChangeParsing {
case CONFLICT_PR: case CONFLICT_PR:
message = Message.generate(((ConflictPrChange) change)); message = Message.generate(((ConflictPrChange) change));
break; break;
case NEW_COMMENT:
message = Message.generate(((CommentChange) change));
break;
case NEW_ANSWERS_COMMENT:
message = Message.generate(((AnswerCommentChange) change));
break;
default: default:
throw new NotFoundException("Нет обработчика для типа " + change.getType().name()); throw new NotFoundException("Нет обработчика для типа " + change.getType().name());
} }

View File

@ -1,17 +1,18 @@
package com.tsc.bitbucketbot.scheduler; package com.tsc.bitbucketbot.scheduler;
import com.tsc.bitbucketbot.config.BitbucketConfig; import com.tsc.bitbucketbot.config.BitbucketConfig;
import com.tsc.bitbucketbot.domain.MessageSend; import com.tsc.bitbucketbot.domain.Answer;
import com.tsc.bitbucketbot.domain.Pagination; import com.tsc.bitbucketbot.domain.Pagination;
import com.tsc.bitbucketbot.domain.change.AnswerCommentChange;
import com.tsc.bitbucketbot.domain.change.CommentChange;
import com.tsc.bitbucketbot.domain.entity.Comment; import com.tsc.bitbucketbot.domain.entity.Comment;
import com.tsc.bitbucketbot.domain.entity.PullRequest; import com.tsc.bitbucketbot.domain.entity.PullRequest;
import com.tsc.bitbucketbot.dto.bitbucket.CommentJson; import com.tsc.bitbucketbot.dto.bitbucket.CommentJson;
import com.tsc.bitbucketbot.service.ChangeService;
import com.tsc.bitbucketbot.service.CommentService; import com.tsc.bitbucketbot.service.CommentService;
import com.tsc.bitbucketbot.service.MessageSendService;
import com.tsc.bitbucketbot.service.PullRequestsService; import com.tsc.bitbucketbot.service.PullRequestsService;
import com.tsc.bitbucketbot.service.UserService; import com.tsc.bitbucketbot.service.UserService;
import com.tsc.bitbucketbot.service.Utils; import com.tsc.bitbucketbot.service.Utils;
import com.tsc.bitbucketbot.utils.Message;
import lombok.NonNull; import lombok.NonNull;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -19,7 +20,8 @@ import org.springframework.data.domain.Page;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.time.LocalDate; import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
@ -33,17 +35,17 @@ import java.util.stream.Collectors;
public class SchedulerComments { public class SchedulerComments {
private static final Integer COUNT = 100; private static final Integer COUNT = 100;
private static final Integer NO_COMMENT = 100; private static final Integer NO_COMMENT = 30;
private static final Pattern PATTERN = Pattern.compile("@[\\w]+"); private static final Pattern PATTERN = Pattern.compile("@[\\w]+");
private final CommentService commentService; private final CommentService commentService;
private final PullRequestsService pullRequestsService; private final PullRequestsService pullRequestsService;
private final MessageSendService messageSendService;
private final UserService userService; private final UserService userService;
private final ChangeService changeService;
private final BitbucketConfig bitbucketConfig; private final BitbucketConfig bitbucketConfig;
@Scheduled(cron = "0 */5 8-18 * * MON-FRI") @Scheduled(cron = "0 */4 8-18 * * MON-FRI")
public void newComments() { public void newComments() {
log.info("Начало сканирования комментариев"); log.info("Начало сканирования комментариев");
long commentId = commentService.getLastCommentId() + 1; long commentId = commentService.getLastCommentId() + 1;
@ -58,7 +60,7 @@ public class SchedulerComments {
if (optCommentJson.isPresent()) { if (optCommentJson.isPresent()) {
final CommentJson commentJson = optCommentJson.get(); final CommentJson commentJson = optCommentJson.get();
notification(commentJson, pullRequest); notification(commentJson, pullRequest);
saveComments(commentJson, commentUrl); saveComments(commentJson, commentUrl, pullRequest.getUrl());
count = 0; count = 0;
break; break;
} }
@ -73,7 +75,7 @@ public class SchedulerComments {
@Scheduled(cron = "0 */1 8-18 * * MON-FRI") @Scheduled(cron = "0 */1 8-18 * * MON-FRI")
public void oldComments() { public void oldComments() {
@NonNull final List<Comment> comments = commentService.getAllBetweenDate(LocalDate.now().minusDays(10), LocalDate.now()); @NonNull final List<Comment> comments = commentService.getAllBetweenDate(LocalDateTime.now().minusDays(10), LocalDateTime.now());
for (Comment comment : comments) { for (Comment comment : comments) {
final Optional<CommentJson> optCommentJson = Utils.urlToJson( final Optional<CommentJson> optCommentJson = Utils.urlToJson(
comment.getUrl(), comment.getUrl(),
@ -83,19 +85,23 @@ public class SchedulerComments {
if (optCommentJson.isPresent()) { if (optCommentJson.isPresent()) {
final CommentJson commentJson = optCommentJson.get(); final CommentJson commentJson = optCommentJson.get();
final Set<Long> oldAnswerIds = comment.getAnswers(); final Set<Long> oldAnswerIds = comment.getAnswers();
final List<CommentJson> answerJsons = commentJson.getComments().stream() final List<CommentJson> newAnswers = commentJson.getComments().stream()
.filter(answerJson -> !oldAnswerIds.contains(answerJson.getId())) .filter(answerJson -> !oldAnswerIds.contains(answerJson.getId()))
.collect(Collectors.toList()); .collect(Collectors.toList());
if (!answerJsons.isEmpty()) { if (!newAnswers.isEmpty()) {
userService.getTelegramIdByLogin(commentJson.getAuthor().getName()).ifPresent( changeService.add(
telegramAuthorComment -> messageSendService.add( AnswerCommentChange.builder()
MessageSend.builder() .telegramId(userService.getTelegramIdByLogin(commentJson.getAuthor().getName()).orElse(null))
.telegramId(telegramAuthorComment) .url(comment.getPrUrl())
.message(Message.answerComment(commentJson.getText(), answerJsons)) .youMessage(commentJson.getText())
.build() .answers(
) newAnswers.stream()
.map(json -> Answer.of(json.getAuthor().getName(), json.getText()))
.collect(Collectors.toList())
)
.build()
); );
comment.getAnswers().addAll(answerJsons.stream().map(CommentJson::getId).collect(Collectors.toList())); comment.getAnswers().addAll(newAnswers.stream().map(CommentJson::getId).collect(Collectors.toList()));
commentService.save(comment); commentService.save(comment);
} }
} }
@ -103,11 +109,12 @@ public class SchedulerComments {
} }
@NonNull @NonNull
private void saveComments(CommentJson comment, String commentUrl) { private void saveComments(CommentJson comment, String commentUrl, String prUrl) {
final Comment newComment = new Comment(); final Comment newComment = new Comment();
newComment.setId(comment.getId()); newComment.setId(comment.getId());
newComment.setDate(LocalDate.now()); newComment.setDate(LocalDateTime.now());
newComment.setUrl(commentUrl); newComment.setUrl(commentUrl);
newComment.setPrUrl(prUrl);
userService.getTelegramIdByLogin(comment.getAuthor().getName()).ifPresent(newComment::setTelegram); userService.getTelegramIdByLogin(comment.getAuthor().getName()).ifPresent(newComment::setTelegram);
commentService.save(newComment); commentService.save(newComment);
} }
@ -126,17 +133,21 @@ public class SchedulerComments {
private void notificationPersonal(@NonNull CommentJson comment, @NonNull PullRequest pullRequest) { private void notificationPersonal(@NonNull CommentJson comment, @NonNull PullRequest pullRequest) {
Matcher matcher = PATTERN.matcher(comment.getText()); Matcher matcher = PATTERN.matcher(comment.getText());
Set<String> recipientsLogins = new HashSet<>();
while (matcher.find()) { while (matcher.find()) {
final String login = matcher.group(0).replace("@", ""); final String login = matcher.group(0).replace("@", "");
userService.getTelegramIdByLogin(login).ifPresent( recipientsLogins.add(login);
telegramId -> messageSendService.add(
MessageSend.builder()
.telegramId(telegramId)
.message(Message.personalNotify(comment, pullRequest.getName(), pullRequest.getUrl()))
.build()
)
);
} }
final List<Long> recipientsIds = userService.getAllTelegramIdByLogin(recipientsLogins);
changeService.add(
CommentChange.builder()
.authorName(comment.getAuthor().getName())
.name(pullRequest.getName())
.url(pullRequest.getUrl())
.telegramId(recipientsIds)
.message(comment.getText())
.build()
);
} }
} }

View File

@ -43,26 +43,18 @@ public class SchedulerNotification {
} }
} }
// @Scheduled(cron = "0 0 18 * * FRI") @Scheduled(cron = "0 0 18 * * FRI")
// @Scheduled(fixedRate = 30000)
public void goodWeekEnd() { public void goodWeekEnd() {
// List<User> allRegister = userService.getAllRegister(); List<User> allRegister = userService.getAllRegister();
// for (User user : allRegister) { for (User user : allRegister) {
// messageSendService.add( messageSendService.add(
// MessageSend.builder() MessageSend.builder()
// .telegramId(user.getTelegramId()) .telegramId(user.getTelegramId())
// .message(Message.goodWeekEnd()) .message(Message.goodWeekEnd())
// .build() .build()
// ); );
// reportService.generateReport(user.getLogin()); reportService.generateReport(user.getLogin());
// } }
final User user = userService.getByLogin("mstruchkov").get();
messageSendService.add(
MessageSend.builder()
.telegramId(user.getTelegramId())
.message(reportService.generateReport(user.getLogin()))
.build()
);
} }
} }

View File

@ -5,7 +5,7 @@ import com.tsc.bitbucketbot.domain.entity.Comment;
import lombok.NonNull; import lombok.NonNull;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import java.time.LocalDate; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
public interface CommentService { public interface CommentService {
@ -15,7 +15,7 @@ public interface CommentService {
Page<Comment> getAll(@NonNull Pagination pagination); Page<Comment> getAll(@NonNull Pagination pagination);
@NonNull @NonNull
List<Comment> getAllBetweenDate(LocalDate dateFrom, LocalDate dateTo); List<Comment> getAllBetweenDate(LocalDateTime dateFrom, LocalDateTime dateTo);
void save(@NonNull Comment comment); void save(@NonNull Comment comment);

View File

@ -1,5 +1,6 @@
package com.tsc.bitbucketbot.service.impl; package com.tsc.bitbucketbot.service.impl;
import com.tsc.bitbucketbot.config.InitConfig;
import com.tsc.bitbucketbot.domain.Pagination; import com.tsc.bitbucketbot.domain.Pagination;
import com.tsc.bitbucketbot.domain.entity.Comment; import com.tsc.bitbucketbot.domain.entity.Comment;
import com.tsc.bitbucketbot.repository.jpa.CommentRepository; import com.tsc.bitbucketbot.repository.jpa.CommentRepository;
@ -10,7 +11,7 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.time.LocalDate; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
@Service @Service
@ -18,10 +19,15 @@ import java.util.List;
public class CommentServiceImpl implements CommentService { public class CommentServiceImpl implements CommentService {
private final CommentRepository commentRepository; private final CommentRepository commentRepository;
private final InitConfig initConfig;
@Override @Override
public Long getLastCommentId() { public Long getLastCommentId() {
return commentRepository.findFirstByOrderByIdDesc().map(Comment::getId).orElse(0L); return commentRepository.findFirstByOrderByIdDesc().map(Comment::getId).orElse(getInitCommentId());
}
private Long getInitCommentId() {
return initConfig.getStartCommentId() != null ? initConfig.getStartCommentId() : 0L;
} }
@Override @Override
@ -30,7 +36,7 @@ public class CommentServiceImpl implements CommentService {
} }
@Override @Override
public @NonNull List<Comment> getAllBetweenDate(LocalDate dateFrom, LocalDate dateTo) { public @NonNull List<Comment> getAllBetweenDate(LocalDateTime dateFrom, LocalDateTime dateTo) {
return commentRepository.findByDateBetween(dateFrom, dateTo); return commentRepository.findByDateBetween(dateFrom, dateTo);
} }

View File

@ -1,5 +1,7 @@
package com.tsc.bitbucketbot.utils; package com.tsc.bitbucketbot.utils;
import com.tsc.bitbucketbot.domain.change.AnswerCommentChange;
import com.tsc.bitbucketbot.domain.change.CommentChange;
import com.tsc.bitbucketbot.domain.change.ConflictPrChange; import com.tsc.bitbucketbot.domain.change.ConflictPrChange;
import com.tsc.bitbucketbot.domain.change.NewPrChange; import com.tsc.bitbucketbot.domain.change.NewPrChange;
import com.tsc.bitbucketbot.domain.change.ReviewersPrChange; import com.tsc.bitbucketbot.domain.change.ReviewersPrChange;
@ -148,19 +150,19 @@ public class Message {
comment.getAuthor().getName() + ": " + comment.getText().replaceAll("@[\\w]+", ""); comment.getAuthor().getName() + ": " + comment.getText().replaceAll("@[\\w]+", "");
} }
public static String personalNotify(@NonNull CommentJson comment, @NonNull String namePr, @NonNull String urlPr) { public static String generate(@NonNull CommentChange commentChange) {
return Smile.BELL + " *Новое упоминание* | " + comment.getAuthor().getName() + Smile.BR + return Smile.BELL + " *Новое упоминание* | " + commentChange.getAuthorName() + Smile.BR +
link(namePr, urlPr) + link(commentChange.getName(), commentChange.getUrl()) +
Smile.HR + Smile.HR +
comment.getText().replaceAll("@[\\w]+", ""); commentChange.getMessage().replaceAll("@[\\w]+", "");
} }
public static String answerComment(@NonNull String commentMessage, @NonNull List<CommentJson> answerJsons) { public static String generate(@NonNull AnswerCommentChange answerCommentChange) {
final StringBuilder message = new StringBuilder(); final StringBuilder message = new StringBuilder();
message.append(Smile.BELL).append("Новые ответы на ваш комментарий").append(Smile.HR) message.append(Smile.BELL).append("Новые ответы на ваш комментарий").append(" | ").append(link("ПР", answerCommentChange.getUrl())).append(Smile.HR)
.append(commentMessage, 0, Math.min(commentMessage.length(), 180)).append(Smile.HR); .append(answerCommentChange.getYouMessage(), 0, Math.min(answerCommentChange.getYouMessage().length(), 180)).append(Smile.HR);
answerJsons.forEach(answerJson -> message.append(answerJson.getAuthor().getName()).append(": ") answerCommentChange.getAnswers().forEach(answer -> message.append(answer.getAuthorName()).append(": ")
.append(answerJson.getText(), 0, Math.min(answerJson.getText().length(), 500)).append(Smile.TWO_BR)); .append(answer.getMessage(), 0, Math.min(answer.getMessage().length(), 500)).append(Smile.TWO_BR));
return message.toString(); return message.toString();
} }

View File

@ -17,6 +17,8 @@ spring:
lob: lob:
non_contextual_creation: true non_contextual_creation: true
bitbucketbot: bitbucketbot:
init:
start-comment-id:
server-send: server-send:
url: http://188.225.35.149:8080/api/send url: http://188.225.35.149:8080/api/send
bitbucket: bitbucket:

View File

@ -15,4 +15,10 @@
</update> </update>
</changeSet> </changeSet>
<changeSet id="add-column-comment-pr_url" author="upagge">
<addColumn tableName="comment" schemaName="public" catalogName="pg_catalog">
<column name="pr_url" type="varchar(300)"/>
</addColumn>
</changeSet>
</databaseChangeLog> </databaseChangeLog>

View File

@ -18,7 +18,7 @@
</appender> </appender>
<root level="info"> <root level="info">
<appender-ref ref="STDOUT"/> <appender-ref ref="FILE"/>
</root> </root>
</configuration> </configuration>