Переработка получения событий от телеграма для поддержки микросервисной архитектуры
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Struchkov Mark 2023-03-26 17:37:34 +03:00
parent ab227779e1
commit cd01b078a9
Signed by: upagge
GPG Key ID: D3018BE7BA428CA6
41 changed files with 943 additions and 227 deletions

View File

@ -0,0 +1,11 @@
package dev.struchkov.godfather.quarkus.context.service;
import dev.struchkov.godfather.main.domain.EventContainer;
import io.smallrye.mutiny.Uni;
public interface EventDispatching {
Uni<Void> dispatch(EventContainer event);
}

View File

@ -1,11 +1,12 @@
package dev.struchkov.godfather.quarkus.context.service;
import dev.struchkov.godfather.main.domain.EventContainer;
import io.smallrye.mutiny.Uni;
public interface EventHandler<T> {
Uni<Void> handle(T event);
Uni<Void> handle(EventContainer<T> event);
String getEventType();
Class<T> getEventClass();
}

View File

@ -0,0 +1,9 @@
package dev.struchkov.godfather.simple.context.service;
import dev.struchkov.godfather.main.domain.EventContainer;
public interface EventDispatching {
void dispatch(EventContainer event);
}

View File

@ -1,9 +1,11 @@
package dev.struchkov.godfather.simple.context.service;
import dev.struchkov.godfather.main.domain.EventContainer;
public interface EventHandler<T> {
void handle(T event);
void handle(EventContainer<T> event);
String getEventType();
Class<T> getEventClass();
}

View File

@ -20,10 +20,6 @@
<description>Доменные сущности, интерфейсы, для библиотеки Godfather</description>
<dependencies>
<dependency>
<groupId>dev.struchkov</groupId>
<artifactId>autoresponder</artifactId>
</dependency>
<dependency>
<groupId>dev.struchkov.haiti</groupId>
<artifactId>haiti-utils</artifactId>

View File

@ -0,0 +1,41 @@
package dev.struchkov.godfather.main.core.utils;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
import static dev.struchkov.haiti.utils.Exceptions.utilityClass;
/**
* Разбивает строку на множество слов, удаляя предлоги.
*
* @author upagge [07/07/2019]
*/
public class Parser {
/**
* Множество предлогов.
*/
private static final Set<String> pretexts = Set.of(
"в", "без", "до", "из", "к", "на", "по", "о", "от", "перед", "при", "с", "у", "за", "над", "об",
"под", "про", "для"
);
private Parser() {
utilityClass();
}
/**
* Метод по разбиению строки на множество слов.
*
* @param text Строка
* @return Множество слов
*/
public static Set<String> splitWords(String text) {
final String[] split = text.split("\\P{L}+");
final Set<String> words = Arrays.stream(split).map(String::toLowerCase).collect(Collectors.toSet());
words.removeAll(pretexts);
return words;
}
}

View File

@ -0,0 +1,49 @@
package dev.struchkov.godfather.quarkus.core;
import dev.struchkov.godfather.main.domain.EventContainer;
import dev.struchkov.godfather.quarkus.context.service.EventDispatching;
import dev.struchkov.godfather.quarkus.context.service.EventHandler;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import static dev.struchkov.haiti.utils.Checker.checkNotNull;
public class EventDispatchingImpl implements EventDispatching {
private final Map<Class, List<EventHandler>> eventHandlerMap;
public EventDispatchingImpl(List<EventHandler> eventProviders) {
this.eventHandlerMap = eventProviders.stream().collect(Collectors.groupingBy(EventHandler::getEventClass));
}
@Override
public Uni<Void> dispatch(EventContainer event) {
if (checkNotNull(event)) {
return Uni.createFrom().voidItem()
.onItem().transformToUni(
v -> {
// запросы к боту из чатов: https://core.telegram.org/bots/inline
final Optional<List<EventHandler>> optHandlers = getHandler(event.getType());
if (optHandlers.isPresent()) {
return Multi.createFrom().iterable(optHandlers.get())
.onItem().transformToUni(
eventHandler -> eventHandler.handle(event)
).concatenate().collect().asList().replaceWithVoid();
}
return Uni.createFrom().voidItem();
}
);
}
return Uni.createFrom().voidItem();
}
private Optional<List<EventHandler>> getHandler(Class type) {
return Optional.ofNullable(eventHandlerMap.get(type));
}
}

View File

@ -1,6 +1,5 @@
package dev.struchkov.godfather.quarkus.core;
import dev.struchkov.autoresponder.Responder;
import dev.struchkov.godfather.main.domain.content.Message;
import dev.struchkov.godfather.main.domain.unit.UnitActiveType;
import dev.struchkov.godfather.quarkus.context.service.ErrorHandler;
@ -112,11 +111,9 @@ public class GeneralAutoResponder<M extends Message> {
.onItem().transformToUni(mModifiable -> mModifiable.change(message))
.concatenate().toUni().replaceWith(
storyLineService.getNextUnitByPersonId(message.getFromPersonId())
.onItem().ifNotNull().transformToUni(
nextUnits -> Uni.createFrom().optional(
Responder.nextUnit(message, nextUnits).or(storyLineService::getDefaultUnit)
)
).onItem().ifNotNull().transformToUni(answerUnit -> answer(UnitRequest.of(answerUnit, message)))
.onItem().ifNotNull().transformToUni(nextUnits -> Responder.nextUnit(message, nextUnits))
.onItem().ifNull().switchTo(Uni.createFrom().optional(storyLineService.getDefaultUnit()))
.onItem().ifNotNull().transformToUni(answerUnit -> answer(UnitRequest.of(answerUnit, message)))
);
}

View File

@ -0,0 +1,148 @@
package dev.struchkov.godfather.quarkus.core;
import dev.struchkov.godfather.main.domain.content.Message;
import dev.struchkov.godfather.main.domain.unit.KeyWord;
import dev.struchkov.godfather.quarkus.core.util.UnitPriorityComparator;
import dev.struchkov.godfather.quarkus.domain.unit.MainUnit;
import dev.struchkov.godfather.quarkus.domain.unit.func.UniPredicate;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import lombok.extern.slf4j.Slf4j;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static dev.struchkov.godfather.main.core.utils.Parser.splitWords;
import static dev.struchkov.haiti.utils.Checker.checkEmpty;
import static dev.struchkov.haiti.utils.Checker.checkNotEmpty;
import static dev.struchkov.haiti.utils.Checker.checkNotNull;
import static dev.struchkov.haiti.utils.Checker.checkNull;
import static dev.struchkov.haiti.utils.Exceptions.utilityClass;
import static dev.struchkov.haiti.utils.Inspector.isNotNull;
import static java.lang.Boolean.TRUE;
/**
* Реализуют основную логику автоответчика.
*
* @author upagge [07/07/2019]
*/
@Slf4j
public final class Responder {
/**
* Компаратор для сортировки Unit-ов
*/
private static final UnitPriorityComparator UNIT_PRIORITY_COMPARATOR = new UnitPriorityComparator();
private Responder() {
utilityClass();
}
/**
* Выбирает, какой Unit будет отдан для обработки
*
* @param nextUnits Множество следующих Unit-ов
* @param message Запрос пользователя - текстовое сообщение
* @return Юнит, который нуждается в обработке в соответствии с запросом пользователя
*/
public static <M extends Message> Uni<MainUnit<M>> nextUnit(M message, Collection<MainUnit<M>> nextUnits) {
isNotNull(nextUnits);
if (checkNotEmpty(nextUnits)) {
return Multi.createFrom().iterable(nextUnits)
.onItem().transformToUniAndMerge(unit -> {
final String text = message.getText();
if (checkNotNull(text)) {
final Set<String> unitPhrases = unit.getPhrases();
final Set<Pattern> patterns = unit.getTriggerPatterns();
final boolean triggerPhrases = checkNotEmpty(unitPhrases) && unitPhrases.contains(text);
final boolean triggerPatterns = checkNotEmpty(patterns) && patterns.stream().anyMatch(pattern -> patternReg(pattern, text));
final boolean triggerKeyWords = percentageMatch(unit, text) >= unit.getMatchThreshold();
final boolean trigger = triggerPhrases || triggerPatterns || triggerKeyWords;
if (trigger) {
return Uni.createFrom().item(unit);
}
}
final UniPredicate<M> triggerCheck = unit.getTriggerCheck();
if (checkNotNull(triggerCheck)) {
return triggerCheck.test(message)
.onItem().transform(matchesTrigger -> TRUE.equals(matchesTrigger) ? unit : null);
}
return Uni.createFrom().nullItem();
})
.collect().asList()
.onItem().transform(searchUnit -> {
if (searchUnit.isEmpty()) {
return nextUnits.stream().filter(Responder::isNotTrigger).collect(Collectors.toList());
} else {
return searchUnit;
}
})
.onItem().transform(list -> list.stream().max(UNIT_PRIORITY_COMPARATOR).orElse(null));
} else {
return Uni.createFrom().nullItem();
}
}
private static <M extends Message> boolean isNotTrigger(MainUnit<M> nextUnit) {
return isNotPattern(nextUnit) && isNotKeyWords(nextUnit) && isNotPhrase(nextUnit) && isNotCheck(nextUnit);
}
private static <M extends Message> boolean isNotCheck(MainUnit<M> unit) {
return checkNull(unit.getTriggerCheck());
}
private static <M extends Message> boolean isNotPhrase(MainUnit<M> unit) {
return checkEmpty(unit.getPhrases());
}
private static <M extends Message> boolean isNotPattern(MainUnit<M> unit) {
return checkEmpty(unit.getTriggerPatterns());
}
private static <M extends Message> boolean isNotKeyWords(MainUnit<M> unit) {
return checkEmpty(unit.getTriggerWords());
}
private static boolean patternReg(Pattern pattern, String message) {
isNotNull(pattern);
return message.matches(pattern.pattern());
}
private static <M extends Message> double percentageMatch(MainUnit<M> unit, String message) {
final Set<KeyWord> unitKeyWords = unit.getTriggerWords();
if (checkNotEmpty(unitKeyWords)) {
final Set<String> messageWords = splitWords(message);
final Set<KeyWord> intersection = getIntersection(unitKeyWords, messageWords);
final double intersectionWeight = getIntersectionWeight(intersection);
log.trace("Ключевые слова юнита: {} ({})", unitKeyWords, unitKeyWords.size());
log.trace("Слова, которые прислал пользователь: {}", messageWords);
log.trace("Пересечение: {} ({})", intersection, intersectionWeight);
log.trace("Процент: {} Необходимо: {}", intersectionWeight / unitKeyWords.size() * 100.0, unit.getMatchThreshold());
return (double) intersection.size() / (double) unitKeyWords.size() * 100.0;
} else {
return 0.0;
}
}
private static double getIntersectionWeight(Set<KeyWord> intersection) {
return intersection.stream().mapToInt(KeyWord::getImportant).sum();
}
private static Set<KeyWord> getIntersection(Set<KeyWord> unitKeyWords, Set<String> messageWords) {
final Set<KeyWord> intersection = new HashSet<>();
for (KeyWord unitKeyWord : unitKeyWords) {
if (messageWords.contains(unitKeyWord.getWord())) {
intersection.add(unitKeyWord);
}
}
return intersection;
}
}

View File

@ -1,5 +1,6 @@
package dev.struchkov.godfather.quarkus.core.provider;
import dev.struchkov.godfather.main.domain.EventContainer;
import dev.struchkov.godfather.main.domain.content.Mail;
import dev.struchkov.godfather.quarkus.context.service.EventHandler;
import dev.struchkov.godfather.quarkus.core.GeneralAutoResponder;
@ -14,13 +15,13 @@ public class StoryLineHandler implements EventHandler<Mail> {
}
@Override
public Uni<Void> handle(Mail message) {
return generalAutoResponder.processingNewMessage(message);
public Uni<Void> handle(EventContainer<Mail> event) {
return generalAutoResponder.processingNewMessage(event.getObject());
}
@Override
public String getEventType() {
return Mail.class.getName();
public Class<Mail> getEventClass() {
return Mail.class;
}
}

View File

@ -0,0 +1,19 @@
package dev.struchkov.godfather.quarkus.core.util;
import dev.struchkov.godfather.quarkus.domain.unit.MainUnit;
import java.util.Comparator;
/**
* Компоратор для сортировки по приоритету.
*
* @author upagge [07/07/2019]
*/
public class UnitPriorityComparator implements Comparator<MainUnit<?>> {
@Override
public int compare(MainUnit unit1, MainUnit unit2) {
return Integer.compare(unit1.getPriority(), unit2.getPriority());
}
}

View File

@ -0,0 +1,37 @@
package dev.struchkov.godfather.simple.core;
import dev.struchkov.godfather.main.domain.EventContainer;
import dev.struchkov.godfather.simple.context.service.EventDispatching;
import dev.struchkov.godfather.simple.context.service.EventHandler;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import static dev.struchkov.haiti.utils.Checker.checkNotNull;
public class EventDispatchingImpl implements EventDispatching {
private final Map<Class, List<EventHandler>> eventHandlerMap;
public EventDispatchingImpl(List<EventHandler> eventProviders) {
this.eventHandlerMap = eventProviders.stream().collect(Collectors.groupingBy(EventHandler::getEventClass));
}
@Override
public void dispatch(EventContainer event) {
if (checkNotNull(event)) {
final Optional<List<EventHandler>> optHandlers = getHandler(event.getType());
if (optHandlers.isPresent()) {
final List<EventHandler> eventHandlers = optHandlers.get();
eventHandlers.forEach(eventHandler -> eventHandler.handle(event));
}
}
}
private Optional<List<EventHandler>> getHandler(Class type) {
return Optional.ofNullable(eventHandlerMap.get(type));
}
}

View File

@ -1,6 +1,5 @@
package dev.struchkov.godfather.simple.core;
import dev.struchkov.autoresponder.Responder;
import dev.struchkov.godfather.main.domain.content.Message;
import dev.struchkov.godfather.main.domain.unit.UnitActiveType;
import dev.struchkov.godfather.simple.context.service.Accessibility;

View File

@ -0,0 +1,149 @@
package dev.struchkov.godfather.simple.core;
import dev.struchkov.godfather.main.domain.content.Message;
import dev.struchkov.godfather.main.domain.unit.KeyWord;
import dev.struchkov.godfather.simple.core.util.UnitPriorityComparator;
import dev.struchkov.godfather.simple.domain.unit.MainUnit;
import lombok.extern.slf4j.Slf4j;
import java.util.Collection;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import static dev.struchkov.godfather.main.core.utils.Parser.splitWords;
import static dev.struchkov.haiti.utils.Checker.checkEmpty;
import static dev.struchkov.haiti.utils.Checker.checkNotEmpty;
import static dev.struchkov.haiti.utils.Checker.checkNotNull;
import static dev.struchkov.haiti.utils.Checker.checkNull;
import static dev.struchkov.haiti.utils.Exceptions.utilityClass;
import static dev.struchkov.haiti.utils.Inspector.isNotNull;
/**
* Реализуют основную логику автоответчика.
*
* @author upagge [07/07/2019]
*/
@Slf4j
public final class Responder {
/**
* Компаратор для сортировки Unit-ов
*/
private static final UnitPriorityComparator UNIT_PRIORITY_COMPARATOR = new UnitPriorityComparator();
private Responder() {
utilityClass();
}
/**
* Выбирает, какой Unit будет отдан для обработки
*
* @param nextUnits Множество следующих Unit-ов
* @param message Запрос пользователя - текстовое сообщение
* @return Юнит, который нуждается в обработке в соответствии с запросом пользователя
*/
public static <M extends Message> Optional<MainUnit<M>> nextUnit(M message, Collection<MainUnit<M>> nextUnits) {
isNotNull(nextUnits);
final Set<MainUnit<M>> searchUnit = new HashSet<>();
if (checkNotEmpty(nextUnits)) {
for (MainUnit<M> unit : nextUnits) {
final String text = message.getText();
if (checkNotNull(text)) {
final Set<String> unitPhrases = unit.getPhrases();
if (checkNotEmpty(unitPhrases) && unitPhrases.contains(text)) {
searchUnit.add(unit);
}
final Set<Pattern> patterns = unit.getTriggerPatterns();
if (checkNotEmpty(patterns)) {
for (Pattern pattern : patterns) {
if (patternReg(pattern, text)) {
searchUnit.add(unit);
break;
}
}
}
if (percentageMatch(unit, text) >= unit.getMatchThreshold()) {
searchUnit.add(unit);
}
}
final Predicate<M> triggerCheck = unit.getTriggerCheck();
if (checkNotNull(triggerCheck) && triggerCheck.test(message)) {
searchUnit.add(unit);
}
}
}
if (searchUnit.isEmpty()) {
for (MainUnit<M> nextUnit : nextUnits) {
if (isNotTrigger(nextUnit)) {
searchUnit.add(nextUnit);
}
}
}
return searchUnit.stream().max(UNIT_PRIORITY_COMPARATOR);
}
private static <M extends Message> boolean isNotTrigger(MainUnit<M> nextUnit) {
return isNotPattern(nextUnit) && isNotKeyWords(nextUnit) && isNotPhrase(nextUnit) && isNotCheck(nextUnit);
}
private static <M extends Message> boolean isNotCheck(MainUnit<M> unit) {
return checkNull(unit.getTriggerCheck());
}
private static <M extends Message> boolean isNotPhrase(MainUnit<M> unit) {
return checkEmpty(unit.getPhrases());
}
private static <M extends Message> boolean isNotPattern(MainUnit<M> unit) {
return checkEmpty(unit.getTriggerPatterns());
}
private static <M extends Message> boolean isNotKeyWords(MainUnit<M> unit) {
return checkEmpty(unit.getTriggerWords());
}
private static boolean patternReg(Pattern pattern, String message) {
isNotNull(pattern);
return message.matches(pattern.pattern());
}
private static <M extends Message> double percentageMatch(MainUnit<M> unit, String message) {
final Set<KeyWord> unitKeyWords = unit.getTriggerWords();
if (checkNotEmpty(unitKeyWords)) {
final Set<String> messageWords = splitWords(message);
final Set<KeyWord> intersection = getIntersection(unitKeyWords, messageWords);
final double intersectionWeight = getIntersectionWeight(intersection);
log.trace("Ключевые слова юнита: {} ({})", unitKeyWords, unitKeyWords.size());
log.trace("Слова, которые прислал пользователь: {}", messageWords);
log.trace("Пересечение: {} ({})", intersection, intersectionWeight);
log.trace("Процент: {} Необходимо: {}", intersectionWeight / unitKeyWords.size() * 100.0, unit.getMatchThreshold());
return (double) intersection.size() / (double) unitKeyWords.size() * 100.0;
} else {
return 0.0;
}
}
private static double getIntersectionWeight(Set<KeyWord> intersection) {
return intersection.stream().mapToInt(KeyWord::getImportant).sum();
}
private static Set<KeyWord> getIntersection(Set<KeyWord> unitKeyWords, Set<String> messageWords) {
final Set<KeyWord> intersection = new HashSet<>();
for (KeyWord unitKeyWord : unitKeyWords) {
if (messageWords.contains(unitKeyWord.getWord())) {
intersection.add(unitKeyWord);
}
}
return intersection;
}
}

View File

@ -1,5 +1,6 @@
package dev.struchkov.godfather.simple.core.provider;
import dev.struchkov.godfather.main.domain.EventContainer;
import dev.struchkov.godfather.main.domain.content.ChatMail;
import dev.struchkov.godfather.simple.context.service.EventHandler;
import dev.struchkov.godfather.simple.core.GeneralAutoResponder;
@ -13,13 +14,13 @@ public class ChatStoryLineHandler implements EventHandler<ChatMail> {
}
@Override
public void handle(ChatMail message) {
generalAutoResponder.processingNewMessage(message);
public void handle(EventContainer<ChatMail> message) {
generalAutoResponder.processingNewMessage(message.getObject());
}
@Override
public String getEventType() {
return ChatMail.class.getSimpleName();
public Class<ChatMail> getEventClass() {
return ChatMail.class;
}
}

View File

@ -1,5 +1,6 @@
package dev.struchkov.godfather.simple.core.provider;
import dev.struchkov.godfather.main.domain.EventContainer;
import dev.struchkov.godfather.main.domain.content.Mail;
import dev.struchkov.godfather.simple.context.service.EventHandler;
import dev.struchkov.godfather.simple.core.GeneralAutoResponder;
@ -13,13 +14,13 @@ public class PersonStoryLineHandler implements EventHandler<Mail> {
}
@Override
public void handle(Mail message) {
generalAutoResponder.processingNewMessage(message);
public void handle(EventContainer<Mail> event) {
generalAutoResponder.processingNewMessage(event.getObject());
}
@Override
public String getEventType() {
return Mail.class.getSimpleName();
public Class<Mail> getEventClass() {
return Mail.class;
}
}

View File

@ -1,6 +1,5 @@
package dev.struchkov.godfather.simple.core.service;
import dev.struchkov.autoresponder.entity.Unit;
import dev.struchkov.godfather.main.domain.StorylineHistory;
import dev.struchkov.godfather.main.domain.UnitPointer;
import dev.struchkov.godfather.main.domain.content.Message;
@ -53,7 +52,7 @@ public class StorylineMailService<T extends Message> implements StorylineService
@Override
public Set<MainUnit<T>> getNextUnitByPersonId(@NotNull String personId) {
final Optional<Set<MainUnit<T>>> optMainUnits = getUnitNameByPersonId(personId)
.map(Unit::getNextUnits)
.map(MainUnit::getNextUnits)
.filter(mainUnits -> !mainUnits.isEmpty());
if (optMainUnits.isEmpty()) {
storylineRepository.cleanHistoryByPersonId(personId);

View File

@ -0,0 +1,19 @@
package dev.struchkov.godfather.simple.core.util;
import dev.struchkov.godfather.simple.domain.unit.MainUnit;
import java.util.Comparator;
/**
* Компоратор для сортировки по приоритету.
*
* @author upagge [07/07/2019]
*/
public class UnitPriorityComparator implements Comparator<MainUnit<?>> {
@Override
public int compare(MainUnit unit1, MainUnit unit2) {
return Integer.compare(unit1.getPriority(), unit2.getPriority());
}
}

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>bot-domain</artifactId>
@ -22,8 +23,13 @@
</dependency>
<dependency>
<groupId>dev.struchkov</groupId>
<artifactId>autoresponder</artifactId>
<groupId>dev.struchkov.haiti</groupId>
<artifactId>haiti-utils</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>

View File

@ -0,0 +1,20 @@
package dev.struchkov.godfather.main.domain;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import dev.struchkov.godfather.main.domain.jackson.TelegramEventContainerDeserializer;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@JsonDeserialize(using = TelegramEventContainerDeserializer.class)
public class EventContainer<T> {
private Class<T> type;
private T object;
}

View File

@ -1,7 +1,15 @@
package dev.struchkov.godfather.main.domain.content;
public interface Attachment {
public abstract class Attachment {
String getType();
protected final String type;
protected Attachment(String type) {
this.type = type;
}
public String getType() {
return type;
}
}

View File

@ -3,6 +3,7 @@ package dev.struchkov.godfather.main.domain.content;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.ArrayList;
import java.util.Collection;
@ -16,7 +17,8 @@ import java.util.List;
@Getter
@Setter
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@ToString
@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = true)
public class Mail extends Message {
public static final String TYPE = "MAIL";
@ -53,4 +55,5 @@ public class Mail extends Message {
this.attachments.addAll(attachments);
}
}

View File

@ -1,14 +1,17 @@
package dev.struchkov.godfather.main.domain.content;
import dev.struchkov.autoresponder.entity.DeliverableText;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import dev.struchkov.godfather.main.domain.jackson.TelegramPayloadDeserializer;
import dev.struchkov.godfather.main.domain.jackson.TelegramPayloadSerializer;
import dev.struchkov.haiti.utils.container.ContextKey;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import static dev.struchkov.haiti.utils.Checker.checkNotNull;
@ -20,8 +23,10 @@ import static dev.struchkov.haiti.utils.Checker.checkNotNull;
*/
@Getter
@Setter
public abstract class Message implements DeliverableText {
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public abstract class Message {
@EqualsAndHashCode.Include
protected String id;
/**
@ -44,7 +49,9 @@ public abstract class Message implements DeliverableText {
*/
protected String text;
protected Map<String, Object> payload = new HashMap<>();
@JsonSerialize(using = TelegramPayloadSerializer.class)
@JsonDeserialize(using = TelegramPayloadDeserializer.class)
protected Map<ContextKey, Object> payload = new HashMap<>();
protected Message(Message source) {
this.id = source.getId();
@ -60,26 +67,13 @@ public abstract class Message implements DeliverableText {
public <T> void addPayload(ContextKey<T> key, T value) {
if (checkNotNull(value)) {
payload.put(key.getValue(), value);
payload.put(key, value);
}
}
public <T> Optional<T> getPayLoad(ContextKey<T> contextKey) {
return Optional.ofNullable(payload.get(contextKey.getValue()))
return Optional.ofNullable(payload.get(contextKey))
.map(value -> (T) value);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Message message = (Message) o;
return Objects.equals(id, message.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}

View File

@ -0,0 +1,36 @@
package dev.struchkov.godfather.main.domain.jackson;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import dev.struchkov.godfather.main.domain.EventContainer;
import lombok.SneakyThrows;
import java.io.IOException;
public class TelegramEventContainerDeserializer extends StdDeserializer<EventContainer> {
public TelegramEventContainerDeserializer() {
this(null);
}
public TelegramEventContainerDeserializer(Class<?> vc) {
super(vc);
}
@Override
@SneakyThrows
public EventContainer deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
final JsonNode node = jsonParser.getCodec().readTree(jsonParser);
final Class typeEvent = Class.forName(node.get("type").asText());
final JsonNode object = node.get("object");
final EventContainer eventContainer = new EventContainer<>();
eventContainer.setType(typeEvent);
eventContainer.setObject(jsonParser.getCodec().treeToValue(object, typeEvent));
return eventContainer;
}
}

View File

@ -0,0 +1,58 @@
package dev.struchkov.godfather.main.domain.jackson;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import dev.struchkov.haiti.utils.container.ContextKey;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class TelegramPayloadDeserializer extends StdDeserializer<Map<ContextKey, Object>> {
public TelegramPayloadDeserializer() {
super(HashMap.class);
}
@Override
public Map<ContextKey, Object> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
Map<ContextKey, Object> resultMap = new HashMap<>();
JsonNode rootNode = jsonParser.getCodec().readTree(jsonParser);
Iterator<Map.Entry<String, JsonNode>> fieldsIterator = rootNode.fields();
while (fieldsIterator.hasNext()) {
Map.Entry<String, JsonNode> fieldEntry = fieldsIterator.next();
String keyString = fieldEntry.getKey();
JsonNode valueNode = fieldEntry.getValue();
// Разделяем строковое представление ключа на имя класса и значение
String[] keyParts = keyString.split("@");
if (keyParts.length != 2) {
throw new IOException("Некорректный формат ключа: " + keyString);
}
String className = keyParts[0];
String keyValue = keyParts[1];
Class<?> keyClass;
try {
keyClass = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new IOException("Не удалось найти класс " + className, e);
}
ContextKey contextKey = ContextKey.of(keyValue, keyClass);
// Десериализация значения
Object objectValue = jsonParser.getCodec().treeToValue(valueNode, keyClass);
resultMap.put(contextKey, objectValue);
}
return resultMap;
}
}

View File

@ -0,0 +1,38 @@
package dev.struchkov.godfather.main.domain.jackson;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import dev.struchkov.haiti.utils.container.ContextKey;
import java.io.IOException;
import java.util.Map;
public class TelegramPayloadSerializer extends StdSerializer<Map<ContextKey, Object>> {
public TelegramPayloadSerializer() {
this(null);
}
public TelegramPayloadSerializer(Class<Map<ContextKey, Object>> t) {
super(t);
}
@Override
public void serialize(Map<ContextKey, Object> value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartObject();
for (Map.Entry<ContextKey, Object> entry : value.entrySet()) {
ContextKey<?> key = entry.getKey();
String keyString = key.getType().getName() + "@" + key.getValue();
Object objectValue = entry.getValue();
gen.writeObjectField(keyString, objectValue);
}
gen.writeEndObject();
}
}

View File

@ -0,0 +1,35 @@
package dev.struchkov.godfather.main.domain.unit;
/**
* Ключевое слово для юнитов.
*/
public class KeyWord {
private final Integer important;
private final String word;
private KeyWord(Integer important, String word) {
if (important < 0 || important > 10) {
throw new RuntimeException("Вес слова должен быть значением от 0 до 100");
}
this.important = important;
this.word = word;
}
public static KeyWord of(Integer weight, String word) {
return new KeyWord(weight, word);
}
public static KeyWord of(String word) {
return new KeyWord(1, word);
}
public Integer getImportant() {
return important;
}
public String getWord() {
return word;
}
}

View File

@ -1,15 +1,15 @@
package dev.struchkov.godfather.quarkus.domain.unit;
import dev.struchkov.autoresponder.entity.KeyWord;
import dev.struchkov.godfather.main.domain.content.Message;
import dev.struchkov.godfather.main.domain.unit.KeyWord;
import dev.struchkov.godfather.main.domain.unit.UnitActiveType;
import dev.struchkov.godfather.quarkus.domain.BoxAnswer;
import dev.struchkov.godfather.quarkus.domain.unit.func.CheckData;
import dev.struchkov.godfather.quarkus.domain.unit.func.UniPredicate;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@ -50,17 +50,17 @@ public class AnswerCheck<M extends Message> extends MainUnit<M> {
private AnswerCheck(Builder<M> builder) {
super(
builder.name,
builder.description,
builder.triggerWords,
builder.triggerPhrases,
builder.triggerCheck,
builder.triggerPatterns,
builder.triggerCheck,
builder.matchThreshold,
builder.priority,
new HashSet<>(),
builder.description,
TYPE,
builder.activeType,
builder.notSaveHistory,
TYPE
builder.notSaveHistory
);
unitTrue = builder.unitTrue;
unitFalse = builder.unitFalse;
@ -100,7 +100,7 @@ public class AnswerCheck<M extends Message> extends MainUnit<M> {
private Set<KeyWord> triggerWords;
private Set<String> triggerPhrases;
private Predicate<M> triggerCheck;
private UniPredicate<M> triggerCheck;
private Set<Pattern> triggerPatterns;
private Integer matchThreshold;
@ -186,7 +186,7 @@ public class AnswerCheck<M extends Message> extends MainUnit<M> {
return this;
}
public Builder<M> triggerCheck(Predicate<M> trigger) {
public Builder<M> triggerCheck(UniPredicate<M> trigger) {
triggerCheck = trigger;
return this;
}

View File

@ -1,18 +1,18 @@
package dev.struchkov.godfather.quarkus.domain.unit;
import dev.struchkov.autoresponder.entity.KeyWord;
import dev.struchkov.godfather.main.domain.content.Message;
import dev.struchkov.godfather.main.domain.unit.KeyWord;
import dev.struchkov.godfather.main.domain.unit.UnitActiveType;
import dev.struchkov.godfather.quarkus.domain.unit.func.CheckSave;
import dev.struchkov.godfather.quarkus.domain.unit.func.PreservableData;
import dev.struchkov.godfather.quarkus.domain.unit.func.Pusher;
import dev.struchkov.godfather.quarkus.domain.unit.func.UniPredicate;
import dev.struchkov.godfather.quarkus.domain.unit.func.preser.AnswerSavePreservable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@ -57,17 +57,17 @@ public class AnswerSave<M extends Message, D> extends MainUnit<M> {
private AnswerSave(Builder<M, D> builder) {
super(
builder.name,
builder.description,
builder.triggerWords,
builder.triggerPhrases,
builder.triggerCheck,
builder.triggerPatterns,
builder.triggerCheck,
builder.matchThreshold,
builder.priority,
builder.nextUnits,
builder.description,
TYPE,
(builder.hidden) ? UnitActiveType.AFTER : UnitActiveType.DEFAULT,
builder.notSaveHistory,
TYPE
builder.notSaveHistory
);
maintenanceNextUnit(nextUnits);
preservable = builder.preservable;
@ -121,7 +121,7 @@ public class AnswerSave<M extends Message, D> extends MainUnit<M> {
private Set<KeyWord> triggerWords;
private Set<String> triggerPhrases;
private Set<Pattern> triggerPatterns;
private Predicate<M> triggerCheck;
private UniPredicate<M> triggerCheck;
private Integer matchThreshold;
private Integer priority;
@ -206,7 +206,7 @@ public class AnswerSave<M extends Message, D> extends MainUnit<M> {
return this;
}
public Builder<M, D> triggerCheck(Predicate<M> trigger) {
public Builder<M, D> triggerCheck(UniPredicate<M> trigger) {
triggerCheck = trigger;
return this;
}

View File

@ -1,11 +1,12 @@
package dev.struchkov.godfather.quarkus.domain.unit;
import dev.struchkov.autoresponder.entity.KeyWord;
import dev.struchkov.godfather.main.domain.content.Message;
import dev.struchkov.godfather.main.domain.unit.KeyWord;
import dev.struchkov.godfather.main.domain.unit.UnitActiveType;
import dev.struchkov.godfather.quarkus.domain.BoxAnswer;
import dev.struchkov.godfather.quarkus.domain.unit.func.CallBackConsumer;
import dev.struchkov.godfather.quarkus.domain.unit.func.ProcessingData;
import dev.struchkov.godfather.quarkus.domain.unit.func.UniPredicate;
import io.smallrye.mutiny.Uni;
import java.util.HashSet;
@ -14,7 +15,6 @@ import java.util.Set;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@ -43,17 +43,17 @@ public class AnswerText<M extends Message> extends MainUnit<M> {
private AnswerText(Builder<M> builder) {
super(
builder.name,
builder.description,
builder.triggerWords,
builder.triggerPhrases,
builder.triggerCheck,
builder.triggerPatterns,
builder.triggerCheck,
builder.matchThreshold,
builder.priority,
builder.nextUnits,
builder.description,
TYPE,
builder.activeType,
builder.notSaveHistory,
TYPE
builder.notSaveHistory
);
answer = builder.boxAnswer;
callBack = builder.callBack;
@ -83,7 +83,7 @@ public class AnswerText<M extends Message> extends MainUnit<M> {
private Set<KeyWord> triggerWords;
private Set<String> triggerPhrases;
private Predicate<M> triggerCheck;
private UniPredicate<M> triggerCheck;
private Set<Pattern> triggerPatterns;
private Integer matchThreshold;
@ -189,7 +189,7 @@ public class AnswerText<M extends Message> extends MainUnit<M> {
return this;
}
public Builder<M> triggerCheck(Predicate<M> trigger) {
public Builder<M> triggerCheck(UniPredicate<M> trigger) {
triggerCheck = trigger;
return this;
}

View File

@ -1,28 +1,69 @@
package dev.struchkov.godfather.quarkus.domain.unit;
import dev.struchkov.autoresponder.entity.KeyWord;
import dev.struchkov.autoresponder.entity.Unit;
import dev.struchkov.godfather.main.domain.content.Message;
import dev.struchkov.godfather.main.domain.unit.KeyWord;
import dev.struchkov.godfather.main.domain.unit.UnitActiveType;
import dev.struchkov.godfather.quarkus.domain.unit.func.UniPredicate;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import java.util.Objects;
import java.util.Collection;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.regex.Pattern;
/**
* Главный обработчик {@link Unit}, от него наследуются все остальные Unit-ы.
* Главный обработчик, от него наследуются все остальные Unit-ы.
*
* @author upagge [08/07/2019]
*/
public abstract class MainUnit<M extends Message> extends Unit<MainUnit<M>, M> {
@Getter
@Setter
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public abstract class MainUnit<M extends Message> {
/**
* Уникальное имя юнита
*/
@EqualsAndHashCode.Include
private String name;
/**
* Ключевые слова.
*/
protected Set<KeyWord> triggerWords;
/**
* Точная фраза.
*/
protected Set<String> phrases;
/**
* Триггеры на срабатывание юнита по регулярному выражению.
*/
protected Set<Pattern> triggerPatterns;
/**
* Пользовательский триггер
*/
protected UniPredicate<M> triggerCheck;
/**
* Значение минимального отношения количества найденных ключевых слов, к количеству ключевых слов Unit-а.
*/
protected Integer matchThreshold;
/**
* Значение приоритета.
*/
protected Integer priority;
/**
* Множество следующих Unit в сценарии.
*/
protected Set<MainUnit<M>> nextUnits;
/**
* Описание юнита, что он делает. Никак не влияет на работу юнита и не участвует в ней. Возможно отображение этого текста в логах.
*/
@ -42,65 +83,38 @@ public abstract class MainUnit<M extends Message> extends Unit<MainUnit<M>, M> {
protected MainUnit(
String name,
String description,
Set<KeyWord> keyWords,
Set<KeyWord> triggerWords,
Set<String> phrases,
Predicate<M> triggerCheck,
Set<Pattern> patterns,
Set<Pattern> triggerPatterns,
UniPredicate<M> triggerCheck,
Integer matchThreshold,
Integer priority,
Set<MainUnit<M>> nextUnits,
String description,
String type,
UnitActiveType activeType,
boolean notSaveHistory,
String type
boolean notSaveHistory
) {
super(keyWords, phrases, triggerCheck, patterns, matchThreshold, priority, nextUnits);
this.name = name;
this.triggerWords = triggerWords;
this.phrases = phrases;
this.triggerPatterns = triggerPatterns;
this.triggerCheck = triggerCheck;
this.matchThreshold = matchThreshold == null ? 10 : matchThreshold;
this.priority = priority == null ? 10 : priority;
this.nextUnits = nextUnits;
this.description = description;
this.activeType = Optional.ofNullable(activeType).orElse(UnitActiveType.DEFAULT);
this.type = type;
this.activeType = Optional.ofNullable(activeType).orElse(UnitActiveType.DEFAULT);
this.notSaveHistory = notSaveHistory;
}
public String getType() {
return type;
public void addPhrase(String phrase) {
phrases.add(phrase);
}
public UnitActiveType getActiveType() {
return activeType;
}
public void setActiveType(UnitActiveType activeType) {
this.activeType = activeType;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public boolean isNotSaveHistory() {
return notSaveHistory;
}
public String getDescription() {
return description;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MainUnit<?> unit = (MainUnit<?>) o;
return name.equals(unit.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
public void addPhrases(Collection<String> phrases) {
this.phrases.addAll(phrases);
}
}

View File

@ -1,15 +1,15 @@
package dev.struchkov.godfather.quarkus.domain.unit.cmd;
import dev.struchkov.autoresponder.entity.KeyWord;
import dev.struchkov.godfather.main.domain.content.Message;
import dev.struchkov.godfather.main.domain.unit.KeyWord;
import dev.struchkov.godfather.main.domain.unit.UnitActiveType;
import dev.struchkov.godfather.quarkus.domain.unit.MainUnit;
import dev.struchkov.godfather.quarkus.domain.unit.func.UniPredicate;
import dev.struchkov.haiti.utils.Checker;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@ -22,17 +22,17 @@ public class ReplaceCmd<M extends Message> extends MainUnit<M> {
private ReplaceCmd(Builder<M> builder) {
super(
builder.name,
builder.description,
builder.triggerWords,
builder.triggerPhrases,
builder.triggerCheck,
builder.triggerPatterns,
builder.triggerCheck,
builder.matchThreshold,
builder.priority,
new HashSet<>(),
builder.description,
TYPE,
builder.activeType,
true,
TYPE
true
);
this.thisUnit = builder.thisUnit;
}
@ -46,11 +46,12 @@ public class ReplaceCmd<M extends Message> extends MainUnit<M> {
}
public static final class Builder<M extends Message> {
private String name = UUID.randomUUID().toString();
private String description;
private Set<String> triggerPhrases;
private Predicate<M> triggerCheck;
private UniPredicate<M> triggerCheck;
private Set<Pattern> triggerPatterns;
private Set<KeyWord> triggerWords;
private Integer matchThreshold;
@ -130,7 +131,7 @@ public class ReplaceCmd<M extends Message> extends MainUnit<M> {
return this;
}
public Builder<M> triggerCheck(Predicate<M> trigger) {
public Builder<M> triggerCheck(UniPredicate<M> trigger) {
triggerCheck = trigger;
return this;
}

View File

@ -1,15 +1,15 @@
package dev.struchkov.godfather.quarkus.domain.unit.cmd;
import dev.struchkov.autoresponder.entity.KeyWord;
import dev.struchkov.godfather.exception.UnitConfigException;
import dev.struchkov.godfather.main.domain.content.Message;
import dev.struchkov.godfather.main.domain.unit.KeyWord;
import dev.struchkov.godfather.main.domain.unit.UnitActiveType;
import dev.struchkov.godfather.quarkus.domain.unit.MainUnit;
import dev.struchkov.godfather.quarkus.domain.unit.func.UniPredicate;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@ -35,17 +35,17 @@ public class RollBackCmd<M extends Message> extends MainUnit<M> {
private RollBackCmd(Builder<M> builder) {
super(
builder.name,
builder.description,
builder.triggerWords,
builder.triggerPhrases,
builder.triggerCheck,
builder.triggerPatterns,
builder.triggerCheck,
builder.matchThreshold,
builder.priority,
new HashSet<>(),
builder.description,
TYPE,
builder.activeType,
true,
TYPE
true
);
this.countBack = builder.countBack;
this.rollbackUnitName = builder.rollbackUnitName;
@ -89,7 +89,7 @@ public class RollBackCmd<M extends Message> extends MainUnit<M> {
private String description;
private Set<String> triggerPhrases;
private Predicate<M> triggerCheck;
private UniPredicate<M> triggerCheck;
private Set<Pattern> triggerPatterns;
private Set<KeyWord> triggerWords;
private Integer matchThreshold;
@ -158,7 +158,7 @@ public class RollBackCmd<M extends Message> extends MainUnit<M> {
return this;
}
public Builder<M> triggerCheck(Predicate<M> trigger) {
public Builder<M> triggerCheck(UniPredicate<M> trigger) {
triggerCheck = trigger;
return this;
}

View File

@ -0,0 +1,9 @@
package dev.struchkov.godfather.quarkus.domain.unit.func;
import io.smallrye.mutiny.Uni;
public interface UniPredicate<T> {
Uni<Boolean> test(T t);
}

View File

@ -1,7 +1,7 @@
package dev.struchkov.godfather.simple.domain.unit;
import dev.struchkov.autoresponder.entity.KeyWord;
import dev.struchkov.godfather.main.domain.content.Message;
import dev.struchkov.godfather.main.domain.unit.KeyWord;
import dev.struchkov.godfather.main.domain.unit.UnitActiveType;
import dev.struchkov.godfather.simple.domain.BoxAnswer;
import dev.struchkov.godfather.simple.domain.unit.func.CheckData;
@ -50,17 +50,17 @@ public class AnswerCheck<M extends Message> extends MainUnit<M> {
private AnswerCheck(Builder<M> builder) {
super(
builder.name,
builder.description,
builder.triggerWords,
builder.triggerPhrases,
builder.triggerCheck,
builder.triggerPatterns,
builder.triggerCheck,
builder.matchThreshold,
builder.priority,
new HashSet<>(),
builder.description,
TYPE,
builder.activeType,
builder.notSaveHistory,
TYPE
builder.notSaveHistory
);
unitTrue = builder.unitTrue;
unitFalse = builder.unitFalse;

View File

@ -1,7 +1,7 @@
package dev.struchkov.godfather.simple.domain.unit;
import dev.struchkov.autoresponder.entity.KeyWord;
import dev.struchkov.godfather.main.domain.content.Message;
import dev.struchkov.godfather.main.domain.unit.KeyWord;
import dev.struchkov.godfather.main.domain.unit.UnitActiveType;
import dev.struchkov.godfather.simple.domain.unit.func.CheckSave;
import dev.struchkov.godfather.simple.domain.unit.func.PreservableData;
@ -56,17 +56,17 @@ public class AnswerSave<M extends Message, D> extends MainUnit<M> {
private AnswerSave(Builder<M, D> builder) {
super(
builder.name,
builder.description,
builder.triggerWords,
builder.triggerPhrases,
builder.triggerCheck,
builder.triggerPatterns,
builder.triggerCheck,
builder.matchThreshold,
builder.priority,
builder.nextUnits,
builder.description,
TYPE,
(builder.hidden) ? UnitActiveType.AFTER : UnitActiveType.DEFAULT,
builder.notSaveHistory,
TYPE
builder.notSaveHistory
);
maintenanceNextUnit(nextUnits);
preservable = builder.preservable;

View File

@ -1,7 +1,7 @@
package dev.struchkov.godfather.simple.domain.unit;
import dev.struchkov.autoresponder.entity.KeyWord;
import dev.struchkov.godfather.main.domain.content.Message;
import dev.struchkov.godfather.main.domain.unit.KeyWord;
import dev.struchkov.godfather.main.domain.unit.UnitActiveType;
import dev.struchkov.godfather.simple.domain.BoxAnswer;
import dev.struchkov.godfather.simple.domain.unit.func.CallBackConsumer;
@ -38,17 +38,17 @@ public class AnswerText<M extends Message> extends MainUnit<M> {
private AnswerText(Builder<M> builder) {
super(
builder.name,
builder.description,
builder.triggerWords,
builder.triggerPhrases,
builder.triggerCheck,
builder.triggerPatterns,
builder.triggerCheck,
builder.matchThreshold,
builder.priority,
builder.nextUnits,
builder.description,
TYPE,
builder.activeType,
builder.notSaveHistory,
TYPE
builder.notSaveHistory
);
answer = builder.boxAnswer;
callBack = builder.callBack;

View File

@ -1,28 +1,69 @@
package dev.struchkov.godfather.simple.domain.unit;
import dev.struchkov.autoresponder.entity.KeyWord;
import dev.struchkov.autoresponder.entity.Unit;
import dev.struchkov.godfather.main.domain.content.Message;
import dev.struchkov.godfather.main.domain.unit.KeyWord;
import dev.struchkov.godfather.main.domain.unit.UnitActiveType;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import java.util.Objects;
import java.util.Collection;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.regex.Pattern;
/**
* Главный обработчик {@link Unit}, от него наследуются все остальные Unit-ы.
* Главный обработчик, от него наследуются все остальные Unit-ы.
*
* @author upagge [08/07/2019]
*/
public abstract class MainUnit<M extends Message> extends Unit<MainUnit<M>, M> {
@Getter
@Setter
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public abstract class MainUnit<M extends Message> {
/**
* Уникальное имя юнита
*/
@EqualsAndHashCode.Include
private String name;
/**
* Ключевые слова.
*/
protected Set<KeyWord> triggerWords;
/**
* Точная фраза.
*/
protected Set<String> phrases;
/**
* Триггеры на срабатывание юнита по регулярному выражению.
*/
protected Set<Pattern> triggerPatterns;
/**
* Пользовательский триггер
*/
protected Predicate<M> triggerCheck;
/**
* Значение минимального отношения количества найденных ключевых слов, к количеству ключевых слов Unit-а.
*/
protected Integer matchThreshold;
/**
* Значение приоритета.
*/
protected Integer priority;
/**
* Множество следующих Unit в сценарии.
*/
protected Set<MainUnit<M>> nextUnits;
/**
* Описание юнита, что он делает. Никак не влияет на работу юнита и не участвует в ней. Возможно отображение этого текста в логах.
*/
@ -42,65 +83,38 @@ public abstract class MainUnit<M extends Message> extends Unit<MainUnit<M>, M> {
protected MainUnit(
String name,
String description,
Set<KeyWord> keyWords,
Set<KeyWord> triggerWords,
Set<String> phrases,
Set<Pattern> triggerPatterns,
Predicate<M> triggerCheck,
Set<Pattern> patterns,
Integer matchThreshold,
Integer priority,
Set<MainUnit<M>> nextUnits,
String description,
String type,
UnitActiveType activeType,
boolean notSaveHistory,
String type
boolean notSaveHistory
) {
super(keyWords, phrases, triggerCheck, patterns, matchThreshold, priority, nextUnits);
this.name = name;
this.triggerWords = triggerWords;
this.phrases = phrases;
this.triggerPatterns = triggerPatterns;
this.triggerCheck = triggerCheck;
this.matchThreshold = matchThreshold == null ? 10 : matchThreshold;
this.priority = priority == null ? 10 : priority;
this.nextUnits = nextUnits;
this.description = description;
this.activeType = Optional.ofNullable(activeType).orElse(UnitActiveType.DEFAULT);
this.type = type;
this.activeType = Optional.ofNullable(activeType).orElse(UnitActiveType.DEFAULT);
this.notSaveHistory = notSaveHistory;
}
public String getType() {
return type;
public void addPhrase(String phrase) {
phrases.add(phrase);
}
public UnitActiveType getActiveType() {
return activeType;
}
public void setActiveType(UnitActiveType activeType) {
this.activeType = activeType;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public boolean isNotSaveHistory() {
return notSaveHistory;
}
public String getDescription() {
return description;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MainUnit<?> unit = (MainUnit<?>) o;
return name.equals(unit.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
public void addPhrases(Collection<String> phrases) {
this.phrases.addAll(phrases);
}
}

View File

@ -1,7 +1,7 @@
package dev.struchkov.godfather.simple.domain.unit.cmd;
import dev.struchkov.autoresponder.entity.KeyWord;
import dev.struchkov.godfather.main.domain.content.Message;
import dev.struchkov.godfather.main.domain.unit.KeyWord;
import dev.struchkov.godfather.main.domain.unit.UnitActiveType;
import dev.struchkov.godfather.simple.domain.unit.MainUnit;
import dev.struchkov.haiti.utils.Checker;
@ -22,17 +22,17 @@ public class ReplaceCmd<M extends Message> extends MainUnit<M> {
private ReplaceCmd(Builder<M> builder) {
super(
builder.name,
builder.description,
builder.triggerWords,
builder.triggerPhrases,
builder.triggerCheck,
builder.triggerPatterns,
builder.triggerCheck,
builder.matchThreshold,
builder.priority,
new HashSet<>(),
builder.description,
TYPE,
builder.activeType,
true,
TYPE
true
);
this.thisUnit = builder.thisUnit;
}
@ -46,6 +46,7 @@ public class ReplaceCmd<M extends Message> extends MainUnit<M> {
}
public static final class Builder<M extends Message> {
private String name = UUID.randomUUID().toString();
private String description;

View File

@ -1,8 +1,8 @@
package dev.struchkov.godfather.simple.domain.unit.cmd;
import dev.struchkov.autoresponder.entity.KeyWord;
import dev.struchkov.godfather.exception.UnitConfigException;
import dev.struchkov.godfather.main.domain.content.Message;
import dev.struchkov.godfather.main.domain.unit.KeyWord;
import dev.struchkov.godfather.main.domain.unit.UnitActiveType;
import dev.struchkov.godfather.simple.domain.unit.MainUnit;
@ -35,17 +35,17 @@ public class RollBackCmd<M extends Message> extends MainUnit<M> {
private RollBackCmd(Builder<M> builder) {
super(
builder.name,
builder.description,
builder.triggerWords,
builder.triggerPhrases,
builder.triggerCheck,
builder.triggerPatterns,
builder.triggerCheck,
builder.matchThreshold,
builder.priority,
new HashSet<>(),
builder.description,
TYPE,
builder.activeType,
true,
TYPE
true
);
this.countBack = builder.countBack;
this.rollbackUnitName = builder.rollbackUnitName;

16
pom.xml
View File

@ -33,8 +33,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<autoresponder.ver>3.6.1</autoresponder.ver>
<haiti.utils>2.6.0</haiti.utils>
<haiti.utils>2.7.2</haiti.utils>
<!-- https://mvnrepository.com/artifact/io.smallrye.reactive/smallrye-mutiny-vertx-core -->
<smallrye.mutiny.vertx.core.version>2.30.1</smallrye.mutiny.vertx.core.version>
@ -127,12 +126,6 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.struchkov</groupId>
<artifactId>autoresponder</artifactId>
<version>${autoresponder.ver}</version>
</dependency>
<dependency>
<groupId>dev.struchkov.haiti</groupId>
<artifactId>haiti-utils</artifactId>
@ -150,6 +143,13 @@
<artifactId>annotations</artifactId>
<version>${jetbrains.annotations.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.2</version>
</dependency>
</dependencies>
</dependencyManagement>